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