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