Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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/Construction.hh>
58 : #include <EnergyPlus/ConvectionCoefficients.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataEnvironment.hh>
61 : #include <EnergyPlus/DataHVACGlobals.hh>
62 : #include <EnergyPlus/DataHeatBalance.hh>
63 : #include <EnergyPlus/DataIPShortCuts.hh>
64 : #include <EnergyPlus/DataLoopNode.hh>
65 : #include <EnergyPlus/DataPrecisionGlobals.hh>
66 : #include <EnergyPlus/FluidProperties.hh>
67 : #include <EnergyPlus/General.hh>
68 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
69 : #include <EnergyPlus/Material.hh>
70 : #include <EnergyPlus/NodeInputManager.hh>
71 : #include <EnergyPlus/OutputProcessor.hh>
72 : #include <EnergyPlus/Plant/DataPlant.hh>
73 : #include <EnergyPlus/PlantUtilities.hh>
74 : #include <EnergyPlus/SurfaceGroundHeatExchanger.hh>
75 : #include <EnergyPlus/UtilityRoutines.hh>
76 :
77 : namespace EnergyPlus {
78 :
79 : namespace SurfaceGroundHeatExchanger {
80 :
81 : // Module containing the routines dealing with surface/panel ground heat exchangers
82 :
83 : // MODULE INFORMATION:
84 : // AUTHOR Simon Rees
85 : // DATE WRITTEN August 2002
86 : // MODIFIED Brent Griffith, Sept 2010, plant upgrades
87 : // RE-ENGINEERED na
88 :
89 : // PURPOSE OF THIS MODULE:
90 : // The purpose of this module is to simulate hydronic Surface Ground Heat
91 : // Exchangers. This includes pavement surfaces with embedded pipes for snow-
92 : // melting or heat rejection from hybrid ground source heat pump systems.
93 : // The heat exchanger may be gound coupled or not. In the latter case the
94 : // bottom surface is exposed to the wind but not solar gains.
95 :
96 : // METHODOLOGY EMPLOYED:
97 : // This model is based on the QTF formulation of heat transfer through
98 : // building elements with embedded heat sources/sinks. The model uses
99 : // a heat exchanger analogy to relate the inlet fluid temperature to the
100 : // net heat transfer rate and consequently outlet temperature. The model
101 : // is entirely passive i.e. it does not set any flow rates or incorporate
102 : // any controls. In order to deal with the non-linear boundary conditions
103 : // at the top surface due to the presence of ice/snow fluxes have to be
104 : // calculated by the QTF model and temperature calculated from the surface
105 : // heat balance. This requires some iteration.
106 : // Note: top surface variables correspond to 'outside' variables in standard
107 : // CTF/QTF definition. Bottom surface variables correspond to 'inside' variables.
108 :
109 : // REFERENCES:
110 : // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
111 : // Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
112 : // of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
113 : // Engineering.
114 : // Seem, J.E. 1986. "Heat Transfer in Buildings", Ph.D. dissertation, University
115 : // of Wisconsin-Madison.
116 :
117 : // OTHER NOTES: none
118 :
119 : // USE STATEMENTS:
120 : // Use statements for data only modules
121 : // Using/Aliasing
122 : using namespace DataLoopNode;
123 :
124 : // Use statements for access to subroutines in other modules
125 :
126 : // Data
127 : // MODULE PARAMETER DEFINITIONS
128 : Real64 constexpr SmallNum(1.0e-30); // Very small number to avoid div0 errors
129 : Real64 constexpr StefBoltzmann(5.6697e-08); // Stefan-Boltzmann constant
130 : Real64 constexpr SurfaceHXHeight(0.0); // Surface Height above ground -- used in height dependent calcs.
131 :
132 : int constexpr SurfCond_Ground(1);
133 : int constexpr SurfCond_Exposed(2);
134 :
135 1 : PlantComponent *SurfaceGroundHeatExchangerData::factory(EnergyPlusData &state,
136 : [[maybe_unused]] DataPlant::PlantEquipmentType objectType,
137 : std::string const objectName)
138 : {
139 1 : if (state.dataSurfaceGroundHeatExchangers->GetInputFlag) {
140 1 : GetSurfaceGroundHeatExchanger(state);
141 1 : state.dataSurfaceGroundHeatExchangers->GetInputFlag = false;
142 : }
143 : // Now look for this particular pipe in the list
144 1 : for (auto &ghx : state.dataSurfaceGroundHeatExchangers->SurfaceGHE) {
145 1 : if (ghx.Name == objectName) {
146 1 : return &ghx;
147 : }
148 : }
149 : // If we didn't find it, fatal
150 0 : ShowFatalError(state, "Surface Ground Heat Exchanger: Error getting inputs for pipe named: " + objectName);
151 : // Shut up the compiler
152 0 : return nullptr;
153 : }
154 :
155 14621 : void SurfaceGroundHeatExchangerData::simulate(EnergyPlusData &state,
156 : [[maybe_unused]] const PlantLocation &calledFromLocation,
157 : bool const FirstHVACIteration,
158 : [[maybe_unused]] Real64 &CurLoad,
159 : [[maybe_unused]] bool const RunFlag)
160 : {
161 14621 : this->InitSurfaceGroundHeatExchanger(state);
162 14621 : this->CalcSurfaceGroundHeatExchanger(state, FirstHVACIteration);
163 14621 : this->UpdateSurfaceGroundHeatExchngr(state);
164 14621 : this->ReportSurfaceGroundHeatExchngr(state);
165 14621 : }
166 :
167 1 : void GetSurfaceGroundHeatExchanger(EnergyPlusData &state)
168 : {
169 :
170 : // SUBROUTINE INFORMATION:
171 : // AUTHOR Simon Rees
172 : // DATE WRITTEN August 2002
173 : // MODIFIED na
174 : // RE-ENGINEERED na
175 :
176 : // PURPOSE OF THIS SUBROUTINE:
177 : // This subroutine reads the input for hydronic Surface Ground Heat Exchangers
178 : // from the user input file. This will contain all of the information
179 : // needed to define and simulate the surface.
180 :
181 : // METHODOLOGY EMPLOYED:
182 : // Standard EnergyPlus methodology.
183 :
184 : // Using/Aliasing
185 : using BranchNodeConnections::TestCompSet;
186 : using FluidProperties::CheckFluidPropertyName;
187 : using FluidProperties::FindGlycol;
188 :
189 : using NodeInputManager::GetOnlySingleNode;
190 : using namespace DataLoopNode;
191 :
192 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
193 :
194 1 : bool ErrorsFound(false); // Set to true if errors in input,
195 : // fatal at end of routine
196 : int IOStatus; // Used in GetObjectItem
197 : int Item; // Item to be "gotten"
198 : int NumAlphas; // Number of Alphas for each GetObjectItem call
199 : int NumNumbers; // Number of Numbers for each GetObjectItem call
200 1 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
201 : // Initializations and allocations
202 1 : cCurrentModuleObject = "GroundHeatExchanger:Surface";
203 1 : int NumOfSurfaceGHEs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
204 : // allocate data structures
205 1 : if (allocated(state.dataSurfaceGroundHeatExchangers->SurfaceGHE)) state.dataSurfaceGroundHeatExchangers->SurfaceGHE.deallocate();
206 :
207 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE.allocate(NumOfSurfaceGHEs);
208 1 : state.dataSurfaceGroundHeatExchangers->CheckEquipName.dimension(NumOfSurfaceGHEs, true);
209 :
210 : // initialize data structures
211 : // surface data
212 : // Obtain all of the user data related to the surfaces...
213 2 : for (Item = 1; Item <= NumOfSurfaceGHEs; ++Item) {
214 :
215 : // get the input data
216 5 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
217 : cCurrentModuleObject,
218 : Item,
219 1 : state.dataIPShortCut->cAlphaArgs,
220 : NumAlphas,
221 1 : state.dataIPShortCut->rNumericArgs,
222 : NumNumbers,
223 : IOStatus,
224 : _,
225 : _,
226 1 : state.dataIPShortCut->cAlphaFieldNames,
227 1 : state.dataIPShortCut->cNumericFieldNames);
228 :
229 : // General user input data
230 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
231 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionName = state.dataIPShortCut->cAlphaArgs(2);
232 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionNum =
233 1 : UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataConstruction->Construct);
234 :
235 1 : if (state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionNum == 0) {
236 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + '=' + state.dataIPShortCut->cAlphaArgs(2));
237 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
238 0 : ErrorsFound = true;
239 : }
240 :
241 : // Error checking for surfaces, zones, and construction information
242 1 : if (!state.dataConstruction->Construct(state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionNum).SourceSinkPresent) {
243 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + '=' + state.dataIPShortCut->cAlphaArgs(2));
244 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
245 0 : ShowContinueError(
246 : state, "Construction must have internal source/sink and be referenced by a ConstructionProperty:InternalHeatSource object");
247 0 : ErrorsFound = true;
248 : }
249 :
250 : // get inlet node data
251 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletNode = state.dataIPShortCut->cAlphaArgs(3);
252 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletNodeNum =
253 2 : GetOnlySingleNode(state,
254 1 : state.dataIPShortCut->cAlphaArgs(3),
255 : ErrorsFound,
256 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerSurface,
257 1 : state.dataIPShortCut->cAlphaArgs(1),
258 : DataLoopNode::NodeFluidType::Water,
259 : DataLoopNode::ConnectionType::Inlet,
260 : NodeInputManager::CompFluidStream::Primary,
261 1 : ObjectIsNotParent);
262 1 : if (state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletNodeNum == 0) {
263 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + '=' + state.dataIPShortCut->cAlphaArgs(3));
264 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
265 0 : ErrorsFound = true;
266 : }
267 :
268 : // get outlet node data
269 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(4);
270 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletNodeNum =
271 2 : GetOnlySingleNode(state,
272 1 : state.dataIPShortCut->cAlphaArgs(4),
273 : ErrorsFound,
274 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerSurface,
275 1 : state.dataIPShortCut->cAlphaArgs(1),
276 : DataLoopNode::NodeFluidType::Water,
277 : DataLoopNode::ConnectionType::Outlet,
278 : NodeInputManager::CompFluidStream::Primary,
279 1 : ObjectIsNotParent);
280 1 : if (state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletNodeNum == 0) {
281 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(4) + '=' + state.dataIPShortCut->cAlphaArgs(4));
282 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
283 0 : ErrorsFound = true;
284 : }
285 :
286 2 : TestCompSet(state,
287 : cCurrentModuleObject,
288 1 : state.dataIPShortCut->cAlphaArgs(1),
289 1 : state.dataIPShortCut->cAlphaArgs(3),
290 1 : state.dataIPShortCut->cAlphaArgs(4),
291 : "Condenser Water Nodes");
292 :
293 : // tube data
294 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TubeDiameter = state.dataIPShortCut->rNumericArgs(1);
295 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TubeCircuits = state.dataIPShortCut->rNumericArgs(2);
296 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TubeSpacing = state.dataIPShortCut->rNumericArgs(3);
297 :
298 1 : if (state.dataIPShortCut->rNumericArgs(2) == 0) {
299 0 : ShowSevereError(state,
300 0 : format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
301 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
302 0 : ShowContinueError(state, "Value must be greater than 0.0");
303 0 : ErrorsFound = true;
304 : }
305 1 : if (state.dataIPShortCut->rNumericArgs(3) == 0.0) {
306 0 : ShowSevereError(state,
307 0 : format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(3), state.dataIPShortCut->rNumericArgs(3)));
308 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
309 0 : ShowContinueError(state, "Value must be greater than 0.0");
310 0 : ErrorsFound = true;
311 : }
312 :
313 : // surface geometry data
314 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfaceLength = state.dataIPShortCut->rNumericArgs(4);
315 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfaceWidth = state.dataIPShortCut->rNumericArgs(5);
316 1 : if (state.dataIPShortCut->rNumericArgs(4) <= 0.0) {
317 0 : ShowSevereError(state,
318 0 : format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(4), state.dataIPShortCut->rNumericArgs(4)));
319 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
320 0 : ShowContinueError(state, "Value must be greater than 0.0");
321 0 : ErrorsFound = true;
322 : }
323 1 : if (state.dataIPShortCut->rNumericArgs(5) <= 0.0) {
324 0 : ShowSevereError(state,
325 0 : format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(5), state.dataIPShortCut->rNumericArgs(5)));
326 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
327 0 : ShowContinueError(state, "Value must be greater than 0.0");
328 0 : ErrorsFound = true;
329 : }
330 :
331 : // get lower b.c. type
332 1 : if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(5), "GROUND")) {
333 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).LowerSurfCond = SurfCond_Ground;
334 0 : } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(5), "EXPOSED")) {
335 0 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).LowerSurfCond = SurfCond_Exposed;
336 : } else {
337 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(5) + '=' + state.dataIPShortCut->cAlphaArgs(5));
338 0 : ShowContinueError(state, "Entered in " + cCurrentModuleObject + '=' + state.dataIPShortCut->cAlphaArgs(1));
339 0 : ShowContinueError(state, "Only \"Ground\" or \"Exposed\" is allowed.");
340 0 : ErrorsFound = true;
341 : }
342 :
343 : } // end of input loop
344 :
345 : // final error check
346 1 : if (ErrorsFound) {
347 0 : ShowFatalError(state, "Errors found in processing input for " + cCurrentModuleObject);
348 : }
349 :
350 : // Set up the output variables
351 2 : for (Item = 1; Item <= NumOfSurfaceGHEs; ++Item) {
352 4 : SetupOutputVariable(state,
353 : "Ground Heat Exchanger Heat Transfer Rate",
354 : OutputProcessor::Unit::W,
355 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).HeatTransferRate,
356 : OutputProcessor::SOVTimeStepType::Plant,
357 : OutputProcessor::SOVStoreType::Average,
358 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
359 4 : SetupOutputVariable(state,
360 : "Ground Heat Exchanger Surface Heat Transfer Rate",
361 : OutputProcessor::Unit::W,
362 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfHeatTransferRate,
363 : OutputProcessor::SOVTimeStepType::Plant,
364 : OutputProcessor::SOVStoreType::Average,
365 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
366 4 : SetupOutputVariable(state,
367 : "Ground Heat Exchanger Heat Transfer Energy",
368 : OutputProcessor::Unit::J,
369 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Energy,
370 : OutputProcessor::SOVTimeStepType::Plant,
371 : OutputProcessor::SOVStoreType::Summed,
372 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
373 4 : SetupOutputVariable(state,
374 : "Ground Heat Exchanger Mass Flow Rate",
375 : OutputProcessor::Unit::kg_s,
376 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).MassFlowRate,
377 : OutputProcessor::SOVTimeStepType::Plant,
378 : OutputProcessor::SOVStoreType::Average,
379 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
380 4 : SetupOutputVariable(state,
381 : "Ground Heat Exchanger Inlet Temperature",
382 : OutputProcessor::Unit::C,
383 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletTemp,
384 : OutputProcessor::SOVTimeStepType::Plant,
385 : OutputProcessor::SOVStoreType::Average,
386 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
387 4 : SetupOutputVariable(state,
388 : "Ground Heat Exchanger Outlet Temperature",
389 : OutputProcessor::Unit::C,
390 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletTemp,
391 : OutputProcessor::SOVTimeStepType::Plant,
392 : OutputProcessor::SOVStoreType::Average,
393 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
394 4 : SetupOutputVariable(state,
395 : "Ground Heat Exchanger Top Surface Temperature",
396 : OutputProcessor::Unit::C,
397 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TopSurfaceTemp,
398 : OutputProcessor::SOVTimeStepType::Plant,
399 : OutputProcessor::SOVStoreType::Average,
400 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
401 4 : SetupOutputVariable(state,
402 : "Ground Heat Exchanger Bottom Surface Temperature",
403 : OutputProcessor::Unit::C,
404 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).BtmSurfaceTemp,
405 : OutputProcessor::SOVTimeStepType::Plant,
406 : OutputProcessor::SOVStoreType::Average,
407 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
408 4 : SetupOutputVariable(state,
409 : "Ground Heat Exchanger Top Surface Heat Transfer Energy per Area",
410 : OutputProcessor::Unit::J_m2,
411 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TopSurfaceFlux,
412 : OutputProcessor::SOVTimeStepType::Plant,
413 : OutputProcessor::SOVStoreType::Average,
414 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
415 4 : SetupOutputVariable(state,
416 : "Ground Heat Exchanger Bottom Surface Heat Transfer Energy per Area",
417 : OutputProcessor::Unit::J_m2,
418 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).BtmSurfaceFlux,
419 : OutputProcessor::SOVTimeStepType::Plant,
420 : OutputProcessor::SOVStoreType::Average,
421 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
422 4 : SetupOutputVariable(state,
423 : "Ground Heat Exchanger Surface Heat Transfer Energy",
424 : OutputProcessor::Unit::J,
425 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfEnergy,
426 : OutputProcessor::SOVTimeStepType::Plant,
427 : OutputProcessor::SOVStoreType::Summed,
428 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
429 4 : SetupOutputVariable(state,
430 : "Ground Heat Exchanger Source Temperature",
431 : OutputProcessor::Unit::C,
432 1 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SourceTemp,
433 : OutputProcessor::SOVTimeStepType::Plant,
434 : OutputProcessor::SOVStoreType::Average,
435 2 : state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
436 : }
437 :
438 1 : if (state.dataSurfaceGroundHeatExchangers->NoSurfaceGroundTempObjWarning) {
439 1 : if (!state.dataEnvrn->GroundTemp_SurfaceObjInput) {
440 0 : ShowWarningError(state, "GetSurfaceGroundHeatExchanger: No \"Site:GroundTemperature:Shallow\" were input.");
441 0 : ShowContinueError(state,
442 0 : format("Defaults, constant throughout the year of ({:.1R}) will be used.", state.dataEnvrn->GroundTemp_Surface));
443 : }
444 1 : state.dataSurfaceGroundHeatExchangers->NoSurfaceGroundTempObjWarning = false;
445 : }
446 1 : }
447 :
448 14621 : void SurfaceGroundHeatExchangerData::InitSurfaceGroundHeatExchanger(EnergyPlusData &state)
449 : {
450 :
451 : // SUBROUTINE INFORMATION:
452 : // AUTHOR Simon Rees
453 : // DATE WRITTEN August 2002
454 : // MODIFIED na
455 : // RE-ENGINEERED na
456 :
457 : // PURPOSE OF THIS SUBROUTINE:
458 : // This subroutine Resets the elements of the data structure as necessary
459 : // at the first HVAC iteration of each time step. The weather and QTF data
460 : // is initialized once only.
461 :
462 : // METHODOLOGY EMPLOYED:
463 : // Check flags and update data structure
464 :
465 : // Using/Aliasing
466 : using namespace DataEnvironment;
467 : using PlantUtilities::RegulateCondenserCompFlowReqOp;
468 : using PlantUtilities::SetComponentFlowRate;
469 :
470 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
471 :
472 : Real64 DesignFlow; // Hypothetical design flow rate
473 : int Cons; // construction counter
474 : int LayerNum; // material layer number for bottom
475 : Real64 OutDryBulb; // Height Dependent dry bulb.
476 :
477 : // get QTF data - only once
478 14621 : if (this->InitQTF) {
479 9 : for (Cons = 1; Cons <= state.dataHeatBal->TotConstructs; ++Cons) {
480 8 : if (UtilityRoutines::SameString(state.dataConstruction->Construct(Cons).Name, this->ConstructionName)) {
481 : // some error checking ??
482 : // CTF stuff
483 1 : LayerNum = state.dataConstruction->Construct(Cons).TotLayers;
484 1 : this->NumCTFTerms = state.dataConstruction->Construct(Cons).NumCTFTerms;
485 1 : this->CTFin = state.dataConstruction->Construct(Cons).CTFInside; // Z coefficents
486 1 : this->CTFout = state.dataConstruction->Construct(Cons).CTFOutside; // X coefficents
487 1 : this->CTFcross = state.dataConstruction->Construct(Cons).CTFCross; // Y coefficents
488 1 : this->CTFflux({1, _}) = state.dataConstruction->Construct(Cons).CTFFlux; // F & f coefficents
489 : // QTF stuff
490 1 : this->CTFSourceIn = state.dataConstruction->Construct(Cons).CTFSourceIn; // Wi coefficents
491 1 : this->CTFSourceOut = state.dataConstruction->Construct(Cons).CTFSourceOut; // Wo coefficents
492 1 : this->CTFTSourceOut = state.dataConstruction->Construct(Cons).CTFTSourceOut; // y coefficents
493 1 : this->CTFTSourceIn = state.dataConstruction->Construct(Cons).CTFTSourceIn; // x coefficents
494 1 : this->CTFTSourceQ = state.dataConstruction->Construct(Cons).CTFTSourceQ; // w coefficents
495 1 : this->ConstructionNum = Cons;
496 : // surface properties
497 1 : this->BtmRoughness = state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(LayerNum)).Roughness;
498 1 : this->TopThermAbs = state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(LayerNum)).AbsorpThermal;
499 1 : this->TopRoughness = state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(1)).Roughness;
500 1 : this->TopThermAbs = state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(1)).AbsorpThermal;
501 1 : this->TopSolarAbs = state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(1)).AbsorpSolar;
502 : }
503 : }
504 : // set one-time flag
505 1 : this->InitQTF = false;
506 : }
507 :
508 14621 : if (this->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
509 6 : OutDryBulb = OutDryBulbTempAt(state, SurfaceHXHeight);
510 6 : this->CTFflux(0) = 0.0;
511 6 : this->TbtmHistory = OutDryBulb;
512 6 : this->TtopHistory = OutDryBulb;
513 6 : this->TsrcHistory = OutDryBulb;
514 6 : this->QbtmHistory = 0.0;
515 6 : this->QtopHistory = 0.0;
516 6 : this->QsrcHistory = 0.0;
517 6 : this->TsrcConstCoef = 0.0;
518 6 : this->TsrcVarCoef = 0.0;
519 6 : this->QbtmConstCoef = 0.0;
520 6 : this->QbtmVarCoef = 0.0;
521 6 : this->QtopConstCoef = 0.0;
522 6 : this->QtopVarCoef = 0.0;
523 6 : this->QSrc = 0.0;
524 6 : this->QSrcAvg = 0.0;
525 6 : this->LastQSrc = 0.0;
526 6 : this->LastSysTimeElapsed = 0.0;
527 6 : this->LastTimeStepSys = 0.0;
528 : // initialize past weather variables
529 6 : state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad = state.dataEnvrn->BeamSolarRad;
530 6 : state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert = state.dataEnvrn->SOLCOS(3);
531 6 : state.dataSurfaceGroundHeatExchangers->PastDifSolarRad = state.dataEnvrn->DifSolarRad;
532 6 : state.dataSurfaceGroundHeatExchangers->PastGroundTemp = state.dataEnvrn->GroundTemp_Surface;
533 6 : state.dataSurfaceGroundHeatExchangers->PastIsRain = state.dataEnvrn->IsRain;
534 6 : state.dataSurfaceGroundHeatExchangers->PastIsSnow = state.dataEnvrn->IsSnow;
535 6 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp = OutDryBulbTempAt(state, SurfaceHXHeight);
536 6 : state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp = OutWetBulbTempAt(state, SurfaceHXHeight);
537 6 : state.dataSurfaceGroundHeatExchangers->PastSkyTemp = state.dataEnvrn->SkyTemp;
538 6 : state.dataSurfaceGroundHeatExchangers->PastWindSpeed = DataEnvironment::WindSpeedAt(state, SurfaceHXHeight);
539 6 : this->MyEnvrnFlag = false;
540 : }
541 :
542 14621 : if (!state.dataGlobal->BeginEnvrnFlag) this->MyEnvrnFlag = true;
543 :
544 : // always initialize - module variables
545 14621 : this->SurfaceArea = this->SurfaceLength * this->SurfaceWidth;
546 :
547 : // If loop operation is controlled by an environmental variable (DBtemp, WBtemp, etc)
548 : // then shut branch down when equipment is not scheduled to run.
549 14621 : DesignFlow = RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesignMassFlowRate);
550 :
551 14621 : SetComponentFlowRate(state, DesignFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
552 :
553 : // get the current flow rate - module variable
554 14621 : state.dataSurfaceGroundHeatExchangers->FlowRate = state.dataLoopNodes->Node(this->InletNodeNum).MassFlowRate;
555 14621 : }
556 :
557 14621 : void SurfaceGroundHeatExchangerData::CalcSurfaceGroundHeatExchanger(
558 : EnergyPlusData &state, bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
559 : )
560 : {
561 :
562 : // AUTHOR Simon Rees
563 : // DATE WRITTEN August 2002
564 : // MODIFIED na
565 : // RE-ENGINEERED na
566 :
567 : // PURPOSE OF THIS SUBROUTINE:
568 : // This subroutine does all of the stuff that is necessary to simulate
569 : // a surface ground heat exchanger. Calls are made to appropriate subroutines
570 : // either in this module or outside of it.
571 :
572 : // METHODOLOGY EMPLOYED:
573 : // To update temperature and flux histories it is necessary to make a surface
574 : // flux/temperature calculation at the begining of each zone time step using the
575 : // weather data from the previous step, and using the average source flux.
576 : // Once this has been done a new source flux, and current surface temperatures,
577 : // are calculated using the current weather data. These surface temperatures and
578 : // fluxes are used for the rest of the system time steps. During subsequent system
579 : // time steps only the source flux is updated.
580 :
581 : // Surface fluxes are calculated from the QTF equations using assumed surface
582 : // temperatures. Surface fluxes are then dependant only on source flux. Constant
583 : // and terms and terms that multiply the source flux from the QTF equations, are
584 : // grouped together for convenience. These are calculated in "CalcBottomFluxCoefficents"
585 : // etc. It is necessary to iterate on these equations, updating the current surface
586 : // temperatures at each step.
587 :
588 : // REFERENCES:
589 : // See 'LowTempRadiantSystem' module
590 : // IBLAST-QTF research program, completed in January 1995 (unreleased)
591 : // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
592 : // Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
593 : // of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
594 : // Engineering.
595 : // Seem, J.E. 1986. "Heat Transfer in Buildings", Ph.D. dissertation, University
596 : // of Wisconsin-Madison.
597 :
598 : // Using/Aliasing
599 : using namespace DataEnvironment;
600 :
601 14621 : Real64 constexpr SurfFluxTol(0.001); // tolerance on the surface fluxes
602 14621 : Real64 constexpr SrcFluxTol(0.001); // tolerance on the source flux
603 14621 : Real64 constexpr RelaxT(0.1); // temperature relaxation factor
604 14621 : int constexpr Maxiter(100);
605 14621 : int constexpr Maxiter1(100);
606 :
607 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
608 : Real64 PastFluxTop; // top surface flux - past value
609 : Real64 PastFluxBtm; // bottom surface flux - past value
610 : Real64 PastTempBtm; // bottom surface temp - past value
611 : Real64 PastTempTop; // top surface temp - past value
612 : Real64 OldPastFluxTop; // top surface flux - past value used during iteration
613 : Real64 OldPastFluxBtm; // bottom surface flux - past value used during iteration
614 : // variables used with current environmental conditions
615 14621 : auto &FluxTop = state.dataSurfaceGroundHeatExchangers->FluxTop; // top surface flux
616 14621 : auto &FluxBtm = state.dataSurfaceGroundHeatExchangers->FluxBtm; // bottom surface flux
617 14621 : auto &TempBtm = state.dataSurfaceGroundHeatExchangers->TempBtm; // bottom surface temp
618 14621 : auto &TempTop = state.dataSurfaceGroundHeatExchangers->TempTop; // top surface temp
619 : Real64 TempT; // top surface temp - used in underrelaxation
620 : Real64 TempB; // bottom surface temp - used in underrelaxation
621 : Real64 OldFluxTop; // top surface flux - value used during iteration
622 : Real64 OldFluxBtm; // bottom surface flux - value used during iteration
623 : Real64 OldSourceFlux; // previous value of source flux - used during iteration
624 : int iter;
625 : int iter1;
626 :
627 : // check if we are in very first call for this zone time step
628 14621 : if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && this->firstTimeThrough) {
629 1612 : this->firstTimeThrough = false;
630 : // calc temps and fluxes with past env. conditions and average source flux
631 1612 : state.dataSurfaceGroundHeatExchangers->SourceFlux = this->QSrcAvg;
632 : // starting values for the surface temps
633 1612 : PastTempBtm = this->TbtmHistory(1);
634 1612 : PastTempTop = this->TtopHistory(1);
635 1612 : OldPastFluxTop = 1.0e+30;
636 1612 : OldPastFluxBtm = 1.0e+30;
637 1612 : TempB = 0.0;
638 1612 : TempT = 0.0;
639 1612 : iter = 0;
640 : while (true) { // iterate to find surface heat balances
641 : // update coefficients
642 :
643 21054 : ++iter;
644 11333 : CalcTopFluxCoefficents(PastTempBtm, PastTempTop);
645 : // calc top surface flux
646 11333 : PastFluxTop = this->QtopConstCoef + this->QtopVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
647 :
648 : // calc new top surface temp
649 101997 : CalcTopSurfTemp(-PastFluxTop,
650 : TempT,
651 11333 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
652 11333 : state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp,
653 11333 : state.dataSurfaceGroundHeatExchangers->PastSkyTemp,
654 11333 : state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad,
655 11333 : state.dataSurfaceGroundHeatExchangers->PastDifSolarRad,
656 11333 : state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert,
657 11333 : state.dataSurfaceGroundHeatExchangers->PastWindSpeed,
658 11333 : state.dataSurfaceGroundHeatExchangers->PastIsRain,
659 11333 : state.dataSurfaceGroundHeatExchangers->PastIsSnow);
660 : // under relax
661 11333 : PastTempTop = PastTempTop * (1.0 - RelaxT) + RelaxT * TempT;
662 :
663 : // update coefficients
664 11333 : CalcBottomFluxCoefficents(PastTempBtm, PastTempTop);
665 11333 : PastFluxBtm = this->QbtmConstCoef + this->QbtmVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
666 :
667 13152 : if (std::abs((OldPastFluxTop - PastFluxTop) / OldPastFluxTop) <= SurfFluxTol &&
668 1819 : std::abs((OldPastFluxBtm - PastFluxBtm) / OldPastFluxBtm) <= SurfFluxTol)
669 1612 : break;
670 :
671 : // calc new surface temps
672 29163 : CalcBottomSurfTemp(PastFluxBtm,
673 : TempB,
674 9721 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
675 9721 : state.dataSurfaceGroundHeatExchangers->PastWindSpeed,
676 9721 : state.dataSurfaceGroundHeatExchangers->PastGroundTemp);
677 : // underrelax
678 9721 : PastTempBtm = PastTempBtm * (1.0 - RelaxT) + RelaxT * TempB;
679 : // update flux record
680 9721 : OldPastFluxTop = PastFluxTop;
681 9721 : OldPastFluxBtm = PastFluxBtm;
682 :
683 : // Check for non-convergence
684 9721 : if (iter > Maxiter) {
685 0 : if (this->ConvErrIndex1 == 0) {
686 0 : ShowWarningMessage(
687 0 : state, format("CalcSurfaceGroundHeatExchanger=\"{}\", Did not converge (part 1), Iterations={}", this->Name, Maxiter));
688 0 : ShowContinueErrorTimeStamp(state, "");
689 : }
690 0 : ShowRecurringWarningErrorAtEnd(
691 0 : state, "CalcSurfaceGroundHeatExchanger=\"" + this->Name + "\", Did not converge (part 1)", this->ConvErrIndex1);
692 0 : break;
693 : }
694 : }
695 :
696 1612 : if (!state.dataSurfaceGroundHeatExchangers->InitializeTempTop) {
697 1 : TempTop = TempT;
698 1 : TempBtm = TempB;
699 1 : FluxTop = PastFluxTop;
700 1 : FluxBtm = PastFluxBtm;
701 1 : state.dataSurfaceGroundHeatExchangers->InitializeTempTop = true;
702 : }
703 :
704 : // update module variables
705 1612 : state.dataSurfaceGroundHeatExchangers->TopSurfTemp = TempTop;
706 1612 : state.dataSurfaceGroundHeatExchangers->BtmSurfTemp = TempBtm;
707 1612 : state.dataSurfaceGroundHeatExchangers->TopSurfFlux = -FluxTop;
708 1612 : state.dataSurfaceGroundHeatExchangers->BtmSurfFlux = FluxBtm;
709 :
710 : // get source temp for output
711 1612 : CalcSourceTempCoefficents(PastTempBtm, PastTempTop);
712 1612 : this->SourceTemp = this->TsrcConstCoef + this->TsrcVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
713 : // update histories
714 1612 : UpdateHistories(PastFluxTop, PastFluxBtm, state.dataSurfaceGroundHeatExchangers->SourceFlux, this->SourceTemp);
715 :
716 : // At the beginning of a time step, reset to zero so average calculation can start again
717 1612 : this->QSrcAvg = 0.0;
718 1612 : this->LastSysTimeElapsed = 0.0;
719 1612 : this->LastTimeStepSys = 0.0;
720 :
721 : // get current env. conditions
722 1612 : state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad = state.dataEnvrn->BeamSolarRad;
723 1612 : state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert = state.dataEnvrn->SOLCOS(3);
724 1612 : state.dataSurfaceGroundHeatExchangers->PastDifSolarRad = state.dataEnvrn->DifSolarRad;
725 1612 : state.dataSurfaceGroundHeatExchangers->PastGroundTemp = state.dataEnvrn->GroundTemp_Surface;
726 1612 : state.dataSurfaceGroundHeatExchangers->PastIsRain = state.dataEnvrn->IsRain;
727 1612 : state.dataSurfaceGroundHeatExchangers->PastIsSnow = state.dataEnvrn->IsSnow;
728 1612 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp = OutDryBulbTempAt(state, SurfaceHXHeight);
729 1612 : state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp = OutWetBulbTempAt(state, SurfaceHXHeight);
730 1612 : state.dataSurfaceGroundHeatExchangers->PastSkyTemp = state.dataEnvrn->SkyTemp;
731 1612 : state.dataSurfaceGroundHeatExchangers->PastWindSpeed = DataEnvironment::WindSpeedAt(state, SurfaceHXHeight);
732 :
733 1612 : TempBtm = this->TbtmHistory(1);
734 1612 : TempTop = this->TtopHistory(1);
735 1612 : OldFluxTop = 1.0e+30;
736 1612 : OldFluxBtm = 1.0e+30;
737 1612 : OldSourceFlux = 1.0e+30;
738 1612 : state.dataSurfaceGroundHeatExchangers->SourceFlux = CalcSourceFlux(state);
739 1612 : iter = 0;
740 : while (true) { // iterate to find source flux
741 4836 : ++iter;
742 3224 : iter1 = 0;
743 : while (true) { // iterate to find surface heat balances
744 23610 : ++iter1;
745 : // update top coefficients
746 13417 : CalcTopFluxCoefficents(TempBtm, TempTop);
747 : // calc top surface flux
748 13417 : FluxTop = this->QtopConstCoef + this->QtopVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
749 : // calc new surface temps
750 120753 : CalcTopSurfTemp(-FluxTop,
751 : TempT,
752 13417 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
753 13417 : state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp,
754 13417 : state.dataSurfaceGroundHeatExchangers->PastSkyTemp,
755 13417 : state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad,
756 13417 : state.dataSurfaceGroundHeatExchangers->PastDifSolarRad,
757 13417 : state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert,
758 13417 : state.dataSurfaceGroundHeatExchangers->PastWindSpeed,
759 13417 : state.dataSurfaceGroundHeatExchangers->PastIsRain,
760 13417 : state.dataSurfaceGroundHeatExchangers->PastIsSnow);
761 : // under-relax
762 13417 : TempTop = TempTop * (1.0 - RelaxT) + RelaxT * TempT;
763 : // update bottom coefficients
764 13417 : CalcBottomFluxCoefficents(TempBtm, TempTop);
765 13417 : FluxBtm = this->QbtmConstCoef + this->QbtmVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
766 : // convergence test on surface fluxes
767 13417 : if (std::abs((OldFluxTop - FluxTop) / OldFluxTop) <= SurfFluxTol && std::abs((OldFluxBtm - FluxBtm) / OldFluxBtm) <= SurfFluxTol)
768 3224 : break;
769 :
770 : // calc new surface temps
771 30579 : CalcBottomSurfTemp(FluxBtm,
772 : TempB,
773 10193 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
774 10193 : state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
775 10193 : state.dataEnvrn->GroundTemp_Surface);
776 : // under-relax
777 10193 : TempBtm = TempBtm * (1.0 - RelaxT) + RelaxT * TempB;
778 : // update flux record
779 10193 : OldFluxBtm = FluxBtm;
780 10193 : OldFluxTop = FluxTop;
781 :
782 : // Check for non-convergence
783 10193 : if (iter1 > Maxiter1) {
784 0 : if (this->ConvErrIndex2 == 0) {
785 0 : ShowWarningMessage(
786 : state,
787 0 : format("CalcSurfaceGroundHeatExchanger=\"{}\", Did not converge (part 2), Iterations={}", this->Name, Maxiter));
788 0 : ShowContinueErrorTimeStamp(state, "");
789 : }
790 0 : ShowRecurringWarningErrorAtEnd(
791 0 : state, "CalcSurfaceGroundHeatExchanger=\"" + this->Name + "\", Did not converge (part 2)", this->ConvErrIndex2);
792 0 : break;
793 : }
794 : }
795 : // update the source temp coefficients and update the source flux
796 3224 : CalcSourceTempCoefficents(TempBtm, TempTop);
797 3224 : state.dataSurfaceGroundHeatExchangers->SourceFlux = CalcSourceFlux(state);
798 : // check source flux convergence
799 3224 : if (std::abs((OldSourceFlux - state.dataSurfaceGroundHeatExchangers->SourceFlux) / (1.0e-20 + OldSourceFlux)) <= SrcFluxTol) break;
800 1612 : OldSourceFlux = state.dataSurfaceGroundHeatExchangers->SourceFlux;
801 :
802 : // Check for non-convergence
803 1612 : if (iter > Maxiter) {
804 0 : if (this->ConvErrIndex3 == 0) {
805 0 : ShowWarningMessage(
806 0 : state, format("CalcSurfaceGroundHeatExchanger=\"{}\", Did not converge (part 3), Iterations={}", this->Name, Maxiter));
807 0 : ShowContinueErrorTimeStamp(state, "");
808 : }
809 0 : ShowRecurringWarningErrorAtEnd(
810 0 : state, "CalcSurfaceGroundHeatExchanger=\"" + this->Name + "\", Did not converge (part 3)", this->ConvErrIndex3);
811 0 : break;
812 : }
813 : } // end surface heat balance iteration
814 :
815 13009 : } else if (!FirstHVACIteration) { // end source flux iteration
816 : // For the rest of the system time steps ...
817 : // update source flux from Twi
818 7308 : this->firstTimeThrough = true;
819 7308 : state.dataSurfaceGroundHeatExchangers->SourceFlux = this->CalcSourceFlux(state);
820 : }
821 14621 : }
822 :
823 24750 : void SurfaceGroundHeatExchangerData::CalcBottomFluxCoefficents(Real64 const Tbottom, // current bottom (lower) surface temperature
824 : Real64 const Ttop // current top (upper) surface temperature
825 : )
826 : {
827 :
828 : // AUTHOR Simon Rees
829 : // DATE WRITTEN August 2002
830 : // MODIFIED na
831 : // RE-ENGINEERED na
832 :
833 : // PURPOSE OF THIS SUBROUTINE:
834 : // Calculates current version of constant variable parts of QTF equations.
835 :
836 : // METHODOLOGY EMPLOYED:
837 : // For given current surface temperatures the terms of the QTF equations can be
838 : // grouped into constant terms, and those depending on the current source flux.
839 : // This routine calculates the current coefficient values for the bottom flux
840 : // equation.
841 :
842 : // REFERENCES:
843 : // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
844 : // Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
845 : // of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
846 : // Engineering.
847 :
848 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
849 : int Term;
850 :
851 : // add current surface temperatures to history data
852 24750 : this->TbtmHistory(0) = Tbottom;
853 24750 : this->TtopHistory(0) = Ttop;
854 :
855 : // Bottom Surface Coefficients
856 24750 : this->QbtmConstCoef = 0.0;
857 247500 : for (Term = 0; Term <= this->NumCTFTerms - 1; ++Term) {
858 :
859 668250 : this->QbtmConstCoef += (-this->CTFin(Term) * this->TbtmHistory(Term)) + (this->CTFcross(Term) * this->TtopHistory(Term)) +
860 445500 : (this->CTFflux(Term) * this->QbtmHistory(Term)) + (this->CTFSourceIn(Term) * this->QsrcHistory(Term));
861 : }
862 :
863 : // correct for extra bottom surface flux term
864 24750 : this->QbtmConstCoef -= this->CTFSourceIn(0) * this->QsrcHistory(0);
865 : // source flux current coefficient
866 24750 : this->QbtmVarCoef = this->CTFSourceIn(0);
867 24750 : }
868 :
869 24750 : void SurfaceGroundHeatExchangerData::CalcTopFluxCoefficents(Real64 const Tbottom, // current bottom (lower) surface temperature
870 : Real64 const Ttop // current top (upper) surface temperature
871 : )
872 : {
873 :
874 : // AUTHOR Simon Rees
875 : // DATE WRITTEN August 2002
876 : // MODIFIED na
877 : // RE-ENGINEERED na
878 :
879 : // PURPOSE OF THIS SUBROUTINE:
880 : // Calculates current version of constant variable parts of QTF equations.
881 :
882 : // METHODOLOGY EMPLOYED:
883 : // For given current surface temperatures the terms of the QTF equations can be
884 : // grouped into constant terms, and those depending on the current source flux.
885 : // This routine calculates the current coefficient values for the top flux
886 : // equation.
887 :
888 : // REFERENCES:
889 : // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
890 : // Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
891 : // of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
892 : // Engineering.
893 :
894 : // add current surface temperatures to history data
895 24750 : this->TbtmHistory(0) = Tbottom;
896 24750 : this->TtopHistory(0) = Ttop;
897 :
898 : // Top Surface Coefficients
899 24750 : this->QtopConstCoef = 0.0;
900 247500 : for (int Term = 0; Term <= this->NumCTFTerms - 1; ++Term) {
901 :
902 668250 : this->QtopConstCoef += (this->CTFout(Term) * this->TtopHistory(Term)) - (this->CTFcross(Term) * this->TbtmHistory(Term)) +
903 445500 : (this->CTFflux(Term) * this->QtopHistory(Term)) + (this->CTFSourceOut(Term) * this->QsrcHistory(Term));
904 : }
905 :
906 : // correct for extra top surface flux term
907 24750 : this->QtopConstCoef -= (this->CTFSourceOut(0) * this->QsrcHistory(0));
908 : // surface flux current coefficient
909 24750 : this->QtopVarCoef = this->CTFSourceOut(0);
910 24750 : }
911 :
912 4836 : void SurfaceGroundHeatExchangerData::CalcSourceTempCoefficents(Real64 const Tbottom, // current bottom (lower) surface temperature
913 : Real64 const Ttop // current top (upper) surface temperature
914 : )
915 : {
916 :
917 : // AUTHOR Simon Rees
918 : // DATE WRITTEN August 2002
919 : // MODIFIED na
920 : // RE-ENGINEERED na
921 :
922 : // PURPOSE OF THIS SUBROUTINE:
923 : // Calculates current version of constant variable parts of QTF equations.
924 :
925 : // METHODOLOGY EMPLOYED:
926 : // For given current surface temperatures the terms of the QTF equations can be
927 : // grouped into constant terms, and those depending on the current source flux.
928 : // This routine calculates the current coefficient values for the source temperature
929 : // equation.
930 :
931 : // REFERENCES:
932 : // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
933 : // Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
934 : // of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
935 : // Engineering.
936 :
937 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
938 : int Term;
939 :
940 : // add current surface temperatures to history data
941 4836 : this->TbtmHistory(0) = Tbottom;
942 4836 : this->TtopHistory(0) = Ttop;
943 :
944 4836 : this->TsrcConstCoef = 0.0;
945 48360 : for (Term = 0; Term <= this->NumCTFTerms - 1; ++Term) {
946 :
947 130572 : this->TsrcConstCoef += (this->CTFTSourceIn(Term) * this->TbtmHistory(Term)) + (this->CTFTSourceOut(Term) * this->TtopHistory(Term)) +
948 87048 : (this->CTFflux(Term) * this->TsrcHistory(Term)) + (this->CTFTSourceQ(Term) * this->QsrcHistory(Term));
949 : }
950 :
951 : // correct for extra source flux term
952 4836 : this->TsrcConstCoef -= this->CTFTSourceQ(0) * this->QsrcHistory(0);
953 : // source flux current coefficient
954 4836 : this->TsrcVarCoef = this->CTFTSourceQ(0);
955 4836 : }
956 :
957 12144 : Real64 SurfaceGroundHeatExchangerData::CalcSourceFlux(EnergyPlusData &state) // component number
958 : {
959 :
960 : // AUTHOR Simon Rees
961 : // DATE WRITTEN August 2002
962 : // MODIFIED na
963 : // RE-ENGINEERED na
964 :
965 : // PURPOSE OF THIS SUBROUTINE:
966 : // This calculates the source flux given the inlet fluid temperature. A
967 : // heat exchanger analogy is used, with the surface as a 'Fixed' fluid.
968 :
969 : // METHODOLOGY EMPLOYED:
970 :
971 : // REFERENCES:
972 : // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
973 : // Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
974 : // of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
975 : // Engineering.
976 :
977 : // Return value
978 : Real64 CalcSourceFlux;
979 :
980 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
981 : Real64 EpsMdotCp; // Epsilon (heat exchanger terminology) times water mass flow rate times water specific heat
982 :
983 : // Effectiveness * Modot * specific heat
984 12144 : if (state.dataSurfaceGroundHeatExchangers->FlowRate > 0.0) {
985 7233 : EpsMdotCp = CalcHXEffectTerm(state, this->InletTemp, state.dataSurfaceGroundHeatExchangers->FlowRate);
986 : // calc flux
987 7233 : CalcSourceFlux = (this->InletTemp - this->TsrcConstCoef) / (this->SurfaceArea / EpsMdotCp + this->TsrcVarCoef);
988 : } else {
989 4911 : CalcSourceFlux = 0.0;
990 : }
991 :
992 12144 : return CalcSourceFlux;
993 : }
994 :
995 1612 : void SurfaceGroundHeatExchangerData::UpdateHistories(Real64 const TopFlux, // current top (top) surface flux
996 : Real64 const BottomFlux, // current bottom (bottom) surface flux
997 : Real64 const sourceFlux, // current source surface flux
998 : Real64 const sourceTemp // current source temperature
999 : )
1000 : {
1001 :
1002 : // AUTHOR Simon Rees
1003 : // DATE WRITTEN August 2002
1004 : // MODIFIED na
1005 : // RE-ENGINEERED na
1006 :
1007 : // PURPOSE OF THIS SUBROUTINE:
1008 : // This is used to update the temperature and flux records for the QTF
1009 : // calculations. This is called at the start of each zone timestep.
1010 :
1011 : // METHODOLOGY EMPLOYED:
1012 : // Just shift along and replace zero index element with current value.
1013 :
1014 : // update top surface temps
1015 1612 : this->TtopHistory = eoshift(this->TtopHistory, -1);
1016 :
1017 : // update bottom surface temps
1018 1612 : this->TbtmHistory = eoshift(this->TbtmHistory, -1);
1019 :
1020 : // update bottom surface temps
1021 1612 : this->TsrcHistory = eoshift(this->TsrcHistory, -1);
1022 1612 : this->TsrcHistory(1) = sourceTemp;
1023 :
1024 : // update bottom surface fluxes
1025 1612 : this->QbtmHistory = eoshift(this->QbtmHistory, -1);
1026 1612 : this->QbtmHistory(1) = BottomFlux;
1027 :
1028 : // update bottom surface fluxes
1029 1612 : this->QtopHistory = eoshift(this->QtopHistory, -1);
1030 1612 : this->QtopHistory(1) = TopFlux;
1031 :
1032 : // update bottom surface fluxes
1033 1612 : this->QsrcHistory = eoshift(this->QsrcHistory, -1);
1034 1612 : this->QsrcHistory(1) = sourceFlux;
1035 1612 : }
1036 :
1037 7233 : Real64 SurfaceGroundHeatExchangerData::CalcHXEffectTerm(EnergyPlusData &state,
1038 : Real64 const Temperature, // Temperature of water entering the surface, in C
1039 : Real64 const WaterMassFlow // Mass flow rate, in kg/s
1040 : )
1041 : {
1042 :
1043 : // SUBROUTINE INFORMATION:
1044 : // AUTHOR Rick Strand
1045 : // DATE WRITTEN December 2000
1046 : // MODIFIED Simon Rees, August 2002
1047 : // RE-ENGINEERED na
1048 :
1049 : // PURPOSE OF THIS SUBROUTINE:
1050 : // This subroutine calculates the "heat exchanger"
1051 : // effectiveness term. This is equal to the mass flow rate of water
1052 : // times the specific heat of water times the effectiveness of
1053 : // the surface heat exchanger. This routine is adapted from that in
1054 : // the low temp radiant surface model.
1055 :
1056 : // METHODOLOGY EMPLOYED:
1057 : // Assumes that the only REAL(r64) heat transfer term that we have to
1058 : // deal with is the convection from the water to the tube. The
1059 : // other assumptions are that the tube bottom surface temperature
1060 : // is equal to the "source location temperature" and that it is
1061 : // a CONSTANT throughout the surface.
1062 :
1063 : // REFERENCES:
1064 : // See RadiantSystemLowTemp module.
1065 : // Property data for water shown below as parameters taken from
1066 : // Incropera and DeWitt, Introduction to Heat Transfer, Table A.6.
1067 : // Heat exchanger information also from Incropera and DeWitt.
1068 : // Code based loosely on code from IBLAST program (research version)
1069 :
1070 : // Using/Aliasing
1071 : using FluidProperties::GetSpecificHeatGlycol;
1072 :
1073 : // Return value
1074 : Real64 CalcHXEffectTerm;
1075 :
1076 7233 : Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
1077 7233 : int constexpr NumOfPropDivisions(13); // intervals in property correlation
1078 : static constexpr std::array<Real64, NumOfPropDivisions> Temps = {
1079 : 1.85, 6.85, 11.85, 16.85, 21.85, 26.85, 31.85, 36.85, 41.85, 46.85, 51.85, 56.85, 61.85}; // Temperature, in C
1080 : static constexpr std::array<Real64, NumOfPropDivisions> Mu = {0.001652,
1081 : 0.001422,
1082 : 0.001225,
1083 : 0.00108,
1084 : 0.000959,
1085 : 0.000855,
1086 : 0.000769,
1087 : 0.000695,
1088 : 0.000631,
1089 : 0.000577,
1090 : 0.000528,
1091 : 0.000489,
1092 : 0.000453}; // Viscosity, in Ns/m2
1093 : static constexpr std::array<Real64, NumOfPropDivisions> Conductivity = {
1094 : 0.574, 0.582, 0.590, 0.598, 0.606, 0.613, 0.620, 0.628, 0.634, 0.640, 0.645, 0.650, 0.656}; // Conductivity, in W/mK
1095 : static constexpr std::array<Real64, NumOfPropDivisions> Pr = {
1096 : 12.22, 10.26, 8.81, 7.56, 6.62, 5.83, 5.20, 4.62, 4.16, 3.77, 3.42, 3.15, 2.88}; // Prandtl number (dimensionless)
1097 7233 : int constexpr WaterIndex(1);
1098 : static constexpr std::string_view RoutineName("SurfaceGroundHeatExchanger:CalcHXEffectTerm");
1099 :
1100 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1101 : int Index;
1102 : Real64 InterpFrac;
1103 : Real64 NuD;
1104 : Real64 ReD;
1105 : Real64 NTU;
1106 : Real64 CpWater;
1107 : Real64 Kactual;
1108 : Real64 MUactual;
1109 : Real64 PRactual;
1110 : Real64 PipeLength;
1111 :
1112 : // First find out where we are in the range of temperatures
1113 7233 : Index = 0;
1114 94743 : while (Index < NumOfPropDivisions) {
1115 50988 : if (Temperature < Temps[Index]) break; // DO loop
1116 43755 : ++Index;
1117 : }
1118 :
1119 : // Initialize thermal properties of water
1120 7233 : if (Index == 0) {
1121 0 : MUactual = Mu[Index];
1122 0 : Kactual = Conductivity[Index];
1123 0 : PRactual = Pr[Index];
1124 7233 : } else if (Index > NumOfPropDivisions - 1) {
1125 0 : Index = NumOfPropDivisions - 1;
1126 0 : MUactual = Mu[Index];
1127 0 : Kactual = Conductivity[Index];
1128 0 : PRactual = Pr[Index];
1129 : } else {
1130 7233 : InterpFrac = (Temperature - Temps[Index - 1]) / (Temps[Index] - Temps[Index - 1]);
1131 7233 : MUactual = Mu[Index - 1] + InterpFrac * (Mu[Index] - Mu[Index - 1]);
1132 7233 : Kactual = Conductivity[Index - 1] + InterpFrac * (Conductivity[Index] - Conductivity[Index - 1]);
1133 7233 : PRactual = Pr[Index - 1] + InterpFrac * (Pr[Index] - Pr[Index - 1]);
1134 : }
1135 : // arguments are glycol name, temperature, and concentration
1136 7233 : if (Temperature < 0.0) { // check if fluid is water and would be freezing
1137 0 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex == WaterIndex) {
1138 0 : if (this->FrozenErrIndex1 == 0) {
1139 0 : ShowWarningMessage(
1140 : state,
1141 0 : format("GroundHeatExchanger:Surface=\"{}\", water is frozen; Model not valid. Calculated Water Temperature=[{:.2R}] C",
1142 : this->Name,
1143 0 : this->InletTemp));
1144 0 : ShowContinueErrorTimeStamp(state, "");
1145 : }
1146 0 : ShowRecurringWarningErrorAtEnd(state,
1147 0 : "GroundHeatExchanger:Surface=\"" + this->Name + "\", water is frozen",
1148 : this->FrozenErrIndex1,
1149 : this->InletTemp,
1150 : this->InletTemp,
1151 : _,
1152 : "[C]",
1153 : "[C]");
1154 0 : this->InletTemp = max(this->InletTemp, 0.0);
1155 : }
1156 : }
1157 14466 : CpWater = GetSpecificHeatGlycol(state,
1158 7233 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
1159 : Temperature,
1160 7233 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
1161 : RoutineName);
1162 :
1163 : // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter)
1164 7233 : ReD = 4.0 * WaterMassFlow / (DataGlobalConstants::Pi * MUactual * this->TubeDiameter * this->TubeCircuits);
1165 :
1166 : // Calculate the Nusselt number based on what flow regime one is in
1167 7233 : if (ReD >= MaxLaminarRe) { // Turbulent flow --> use Colburn equation
1168 7233 : NuD = 0.023 * std::pow(ReD, 0.8) * std::pow(PRactual, 1.0 / 3.0);
1169 : } else { // Laminar flow --> use constant surface temperature relation
1170 0 : NuD = 3.66;
1171 : }
1172 : // Calculate the NTU parameter
1173 : // NTU = UA/[(Mdot*Cp)min]
1174 : // where: U = h (convection coefficient) and h = (k)(Nu)/D
1175 : // A = Pi*D*TubeLength
1176 : // NTU = PI * Kactual * NuD * SurfaceGHE(SurfaceGHENum)%TubeLength / (WaterMassFlow * CpWater)
1177 :
1178 7233 : PipeLength = this->SurfaceLength * this->SurfaceWidth / this->TubeSpacing;
1179 :
1180 7233 : NTU = DataGlobalConstants::Pi * Kactual * NuD * PipeLength / (WaterMassFlow * CpWater);
1181 : // Calculate Epsilon*MassFlowRate*Cp
1182 7233 : if (-NTU >= DataPrecisionGlobals::EXP_LowerLimit) {
1183 0 : CalcHXEffectTerm = (1.0 - std::exp(-NTU)) * WaterMassFlow * CpWater;
1184 : } else {
1185 7233 : CalcHXEffectTerm = 1.0 * WaterMassFlow * CpWater;
1186 : }
1187 :
1188 7233 : return CalcHXEffectTerm;
1189 : }
1190 :
1191 24750 : void SurfaceGroundHeatExchangerData::CalcTopSurfTemp(Real64 const FluxTop, // top surface flux
1192 : Real64 &TempTop, // top surface temperature
1193 : Real64 const ThisDryBulb, // dry bulb temperature
1194 : Real64 const ThisWetBulb, // wet bulb temperature
1195 : Real64 const ThisSkyTemp, // sky temperature
1196 : Real64 const ThisBeamSolarRad, // beam solar radiation
1197 : Real64 const ThisDifSolarRad, // diffuse solar radiation
1198 : Real64 const ThisSolarDirCosVert, // vertical component of solar normal
1199 : Real64 const ThisWindSpeed, // wind speed
1200 : bool const ThisIsRain, // rain flag
1201 : bool const ThisIsSnow // snow flag
1202 : )
1203 : {
1204 :
1205 : // AUTHOR Simon Rees
1206 : // DATE WRITTEN August 2002
1207 : // MODIFIED na
1208 : // RE-ENGINEERED na
1209 :
1210 : // PURPOSE OF THIS SUBROUTINE:
1211 : // This subroutine is used to calculate the top surface
1212 : // temperature for the given surface flux.
1213 :
1214 : // METHODOLOGY EMPLOYED:
1215 : // calc surface heat balance
1216 :
1217 : // Using/Aliasing
1218 : using ConvectionCoefficients::CalcASHRAESimpExtConvectCoeff;
1219 :
1220 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1221 : Real64 ConvCoef; // convection coefficient
1222 : Real64 RadCoef; // radiation coefficient
1223 : Real64 ExternalTemp; // external environmental temp - drybulb or wetbulb
1224 : Real64 OldSurfTemp; // previous surface temperature
1225 : Real64 QSolAbsorbed; // absorbed solar flux
1226 : Real64 SurfTempAbs; // absolute value of surface temp
1227 : Real64 SkyTempAbs; // absolute value of sky temp
1228 :
1229 : // make a surface heat balance and solve for temperature
1230 :
1231 : // set appropriate external temp
1232 24750 : if (ThisIsSnow || ThisIsRain) {
1233 0 : ExternalTemp = ThisWetBulb;
1234 : } else { // normal dry conditions
1235 24750 : ExternalTemp = ThisDryBulb;
1236 : }
1237 :
1238 : // set previous surface temp
1239 24750 : OldSurfTemp = this->TtopHistory(1);
1240 : // absolute temperatures
1241 24750 : SurfTempAbs = OldSurfTemp + DataGlobalConstants::KelvinConv;
1242 24750 : SkyTempAbs = ThisSkyTemp + DataGlobalConstants::KelvinConv;
1243 :
1244 : // ASHRAE simple convection coefficient model for external surfaces.
1245 24750 : ConvCoef = CalcASHRAESimpExtConvectCoeff(this->TopRoughness, ThisWindSpeed);
1246 : // radiation coefficient using surf temp from past time step
1247 24750 : if (std::abs(SurfTempAbs - SkyTempAbs) > SmallNum) {
1248 24750 : RadCoef = StefBoltzmann * this->TopThermAbs * (pow_4(SurfTempAbs) - pow_4(SkyTempAbs)) / (SurfTempAbs - SkyTempAbs);
1249 : } else {
1250 0 : RadCoef = 0.0;
1251 : }
1252 :
1253 : // total absorbed solar - no ground solar
1254 24750 : QSolAbsorbed = this->TopSolarAbs * (max(ThisSolarDirCosVert, 0.0) * ThisBeamSolarRad + ThisDifSolarRad);
1255 :
1256 : // solve for temperature
1257 24750 : TempTop = (FluxTop + ConvCoef * ExternalTemp + RadCoef * ThisSkyTemp + QSolAbsorbed) / (ConvCoef + RadCoef);
1258 24750 : }
1259 :
1260 19914 : void SurfaceGroundHeatExchangerData::CalcBottomSurfTemp(Real64 const FluxBtm, // bottom surface flux
1261 : Real64 &TempBtm, // bottom surface temperature
1262 : Real64 const ThisDryBulb, // dry bulb temperature
1263 : Real64 const ThisWindSpeed, // wind speed
1264 : Real64 const ThisGroundTemp // ground temperature
1265 : )
1266 : {
1267 :
1268 : // AUTHOR Simon Rees
1269 : // DATE WRITTEN August 2002
1270 : // MODIFIED na
1271 : // RE-ENGINEERED na
1272 :
1273 : // PURPOSE OF THIS SUBROUTINE:
1274 : // This subroutine is used to calculate the bottom surface
1275 : // temperature for the given surface flux.
1276 :
1277 : // METHODOLOGY EMPLOYED:
1278 : // calc surface heat balances
1279 :
1280 : // Using/Aliasing
1281 : using ConvectionCoefficients::CalcASHRAESimpExtConvectCoeff;
1282 :
1283 : Real64 ConvCoef; // convection coefficient
1284 : Real64 RadCoef; // radiation coefficient
1285 : Real64 OldSurfTemp; // previous surface temperature
1286 : Real64 SurfTempAbs; // absolute value of surface temp
1287 : Real64 ExtTempAbs; // absolute value of sky temp
1288 :
1289 19914 : if (this->LowerSurfCond == SurfCond_Exposed) {
1290 :
1291 : // make a surface heat balance and solve for temperature
1292 0 : OldSurfTemp = this->TbtmHistory(1);
1293 : // absolute temperatures
1294 0 : SurfTempAbs = OldSurfTemp + DataGlobalConstants::KelvinConv;
1295 0 : ExtTempAbs = ThisDryBulb + DataGlobalConstants::KelvinConv;
1296 :
1297 : // ASHRAE simple convection coefficient model for external surfaces.
1298 0 : ConvCoef = CalcASHRAESimpExtConvectCoeff(this->TopRoughness, ThisWindSpeed);
1299 :
1300 : // radiation coefficient using surf temp from past time step
1301 0 : if (std::abs(SurfTempAbs - ExtTempAbs) > SmallNum) {
1302 0 : RadCoef = StefBoltzmann * this->TopThermAbs * (pow_4(SurfTempAbs) - pow_4(ExtTempAbs)) / (SurfTempAbs - ExtTempAbs);
1303 : } else {
1304 0 : RadCoef = 0.0;
1305 : }
1306 :
1307 : // total absorbed solar - no ground solar
1308 0 : TempBtm = (FluxBtm + ConvCoef * ThisDryBulb + RadCoef * ThisDryBulb) / (ConvCoef + RadCoef);
1309 :
1310 : } else { // ground coupled
1311 : // just use the supplied ground temperature
1312 19914 : TempBtm = ThisGroundTemp;
1313 : }
1314 19914 : }
1315 :
1316 14621 : void SurfaceGroundHeatExchangerData::UpdateSurfaceGroundHeatExchngr(EnergyPlusData &state) // Index for the surface
1317 : {
1318 :
1319 : // SUBROUTINE INFORMATION:
1320 : // AUTHOR Simon Rees
1321 : // DATE WRITTEN August 2002
1322 : // MODIFIED na
1323 : // RE-ENGINEERED na
1324 :
1325 : // PURPOSE OF THIS SUBROUTINE:
1326 : // This subroutine does any updating that needs to be done for surface
1327 : // ground heat exchangers. One of the most important functions of
1328 : // this routine is to update the average heat source/sink for a
1329 : // particular system over the various system time steps that make up
1330 : // the zone time step. This routine must also set the outlet water conditions.
1331 :
1332 : // METHODOLOGY EMPLOYED:
1333 : // For the source/sink average update, if the system time step elapsed
1334 : // is still what it used to be, then either we are still iterating or
1335 : // we had to go back and shorten the time step. As a result, we have
1336 : // to subtract out the previous value that we added. If the system
1337 : // time step elapsed is different, then we just need to add the new
1338 : // values to the running average.
1339 :
1340 : // Using/Aliasing
1341 14621 : auto &SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
1342 14621 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
1343 : using FluidProperties::GetSpecificHeatGlycol;
1344 : using PlantUtilities::SafeCopyPlantNode;
1345 :
1346 : // SUBROUTINE PARAMETER DEFINITIONS:
1347 : static constexpr std::string_view RoutineName("SurfaceGroundHeatExchanger:Update");
1348 :
1349 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1350 : Real64 CpFluid; // Specific heat of working fluid
1351 :
1352 : // update flux
1353 14621 : this->QSrc = state.dataSurfaceGroundHeatExchangers->SourceFlux;
1354 :
1355 14621 : if (this->LastSysTimeElapsed == SysTimeElapsed) { // only update in normal mode
1356 12573 : if (this->LastSysTimeElapsed == SysTimeElapsed) {
1357 : // Still iterating or reducing system time step, so subtract old values which were
1358 : // not valid
1359 12573 : this->QSrcAvg -= this->LastQSrc * this->LastTimeStepSys / state.dataGlobal->TimeStepZone;
1360 : }
1361 :
1362 : // Update the running average and the "last" values with the current values of the appropriate variables
1363 12573 : this->QSrcAvg += this->QSrc * TimeStepSys / state.dataGlobal->TimeStepZone;
1364 :
1365 12573 : this->LastQSrc = state.dataSurfaceGroundHeatExchangers->SourceFlux;
1366 12573 : this->LastSysTimeElapsed = SysTimeElapsed;
1367 12573 : this->LastTimeStepSys = TimeStepSys;
1368 : }
1369 :
1370 : // Calculate the water side outlet conditions and set the
1371 : // appropriate conditions on the correct HVAC node.
1372 14621 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName == "WATER") {
1373 14621 : if (InletTemp < 0.0) {
1374 0 : ShowRecurringWarningErrorAtEnd(state,
1375 0 : "UpdateSurfaceGroundHeatExchngr: Water is frozen in Surf HX=" + this->Name,
1376 : this->FrozenErrIndex2,
1377 : this->InletTemp,
1378 : this->InletTemp);
1379 : }
1380 14621 : this->InletTemp = max(this->InletTemp, 0.0);
1381 : }
1382 :
1383 29242 : CpFluid = GetSpecificHeatGlycol(state,
1384 14621 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
1385 : this->InletTemp,
1386 14621 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
1387 : RoutineName);
1388 :
1389 14621 : SafeCopyPlantNode(state, this->InletNodeNum, this->OutletNodeNum);
1390 : // check for flow
1391 14621 : if ((CpFluid > 0.0) && (state.dataSurfaceGroundHeatExchangers->FlowRate > 0.0)) {
1392 26928 : state.dataLoopNodes->Node(this->OutletNodeNum).Temp = this->InletTemp - this->SurfaceArea *
1393 17952 : state.dataSurfaceGroundHeatExchangers->SourceFlux /
1394 8976 : (state.dataSurfaceGroundHeatExchangers->FlowRate * CpFluid);
1395 8976 : state.dataLoopNodes->Node(this->OutletNodeNum).Enthalpy = state.dataLoopNodes->Node(this->OutletNodeNum).Temp * CpFluid;
1396 : }
1397 14621 : }
1398 :
1399 14621 : void SurfaceGroundHeatExchangerData::ReportSurfaceGroundHeatExchngr(EnergyPlusData &state) // Index for the surface under consideration
1400 : {
1401 :
1402 : // SUBROUTINE INFORMATION:
1403 : // AUTHOR Simon Rees
1404 : // DATE WRITTEN August 2002
1405 : // MODIFIED na
1406 : // RE-ENGINEERED na
1407 :
1408 : // PURPOSE OF THIS SUBROUTINE:
1409 : // This subroutine simply produces output for Surface ground heat exchangers
1410 :
1411 : // Using/Aliasing
1412 14621 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
1413 :
1414 : // update flows and temps from node data
1415 14621 : this->InletTemp = state.dataLoopNodes->Node(this->InletNodeNum).Temp;
1416 14621 : this->OutletTemp = state.dataLoopNodes->Node(this->OutletNodeNum).Temp;
1417 14621 : this->MassFlowRate = state.dataLoopNodes->Node(this->InletNodeNum).MassFlowRate;
1418 :
1419 : // update other variables from module variables
1420 14621 : this->HeatTransferRate = state.dataSurfaceGroundHeatExchangers->SourceFlux * this->SurfaceArea;
1421 14621 : this->SurfHeatTransferRate =
1422 14621 : this->SurfaceArea * (state.dataSurfaceGroundHeatExchangers->TopSurfFlux + state.dataSurfaceGroundHeatExchangers->BtmSurfFlux);
1423 14621 : this->Energy = state.dataSurfaceGroundHeatExchangers->SourceFlux * this->SurfaceArea * TimeStepSys * DataGlobalConstants::SecInHour;
1424 14621 : this->TopSurfaceTemp = state.dataSurfaceGroundHeatExchangers->TopSurfTemp;
1425 14621 : this->BtmSurfaceTemp = state.dataSurfaceGroundHeatExchangers->BtmSurfTemp;
1426 14621 : this->TopSurfaceFlux = state.dataSurfaceGroundHeatExchangers->TopSurfFlux;
1427 14621 : this->BtmSurfaceFlux = state.dataSurfaceGroundHeatExchangers->BtmSurfFlux;
1428 43863 : this->SurfEnergy = SurfaceArea * (state.dataSurfaceGroundHeatExchangers->TopSurfFlux + state.dataSurfaceGroundHeatExchangers->BtmSurfFlux) *
1429 29242 : TimeStepSys * DataGlobalConstants::SecInHour;
1430 14621 : }
1431 1 : void SurfaceGroundHeatExchangerData::oneTimeInit_new(EnergyPlusData &state)
1432 : {
1433 :
1434 : using FluidProperties::GetDensityGlycol;
1435 : using PlantUtilities::InitComponentNodes;
1436 : using PlantUtilities::RegisterPlantCompDesignFlow;
1437 : using PlantUtilities::ScanPlantLoopsForObject;
1438 :
1439 : // SUBROUTINE PARAMETER DEFINITIONS:
1440 1 : Real64 constexpr DesignVelocity(0.5); // Hypothetical design max pipe velocity [m/s]
1441 : Real64 rho; // local fluid density
1442 : bool errFlag;
1443 1 : static std::string const RoutineName("InitSurfaceGroundHeatExchanger");
1444 :
1445 : // Locate the hx on the plant loops for later usage
1446 1 : errFlag = false;
1447 1 : ScanPlantLoopsForObject(state, this->Name, DataPlant::PlantEquipmentType::GrndHtExchgSurface, this->plantLoc, errFlag, _, _, _, _, _);
1448 :
1449 1 : if (errFlag) {
1450 0 : ShowFatalError(state, "InitSurfaceGroundHeatExchanger: Program terminated due to previous condition(s).");
1451 : }
1452 3 : rho = GetDensityGlycol(state,
1453 1 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
1454 : DataPrecisionGlobals::constant_zero,
1455 1 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
1456 : RoutineName);
1457 1 : this->DesignMassFlowRate = DataGlobalConstants::Pi / 4.0 * pow_2(this->TubeDiameter) * DesignVelocity * rho * this->TubeCircuits;
1458 1 : InitComponentNodes(state, 0.0, this->DesignMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
1459 1 : RegisterPlantCompDesignFlow(state, this->InletNodeNum, this->DesignMassFlowRate / rho);
1460 1 : }
1461 0 : void SurfaceGroundHeatExchangerData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
1462 : {
1463 0 : }
1464 :
1465 : } // namespace SurfaceGroundHeatExchanger
1466 :
1467 2313 : } // namespace EnergyPlus
|