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 : #include <memory>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Array3D.hh>
55 : #include <ObjexxFCL/Fmath.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/Construction.hh>
60 : #include <EnergyPlus/ConvectionCoefficients.hh>
61 : #include <EnergyPlus/Data/EnergyPlusData.hh>
62 : #include <EnergyPlus/DataEnvironment.hh>
63 : #include <EnergyPlus/DataHVACGlobals.hh>
64 : #include <EnergyPlus/DataHeatBalance.hh>
65 : #include <EnergyPlus/DataIPShortCuts.hh>
66 : #include <EnergyPlus/DataLoopNode.hh>
67 : #include <EnergyPlus/FluidProperties.hh>
68 : #include <EnergyPlus/General.hh>
69 : #include <EnergyPlus/GlobalNames.hh>
70 : #include <EnergyPlus/GroundTemperatureModeling/GroundTemperatureModelManager.hh>
71 : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
72 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
73 : #include <EnergyPlus/Material.hh>
74 : #include <EnergyPlus/NodeInputManager.hh>
75 : #include <EnergyPlus/OutAirNodeManager.hh>
76 : #include <EnergyPlus/OutputProcessor.hh>
77 : #include <EnergyPlus/PipeHeatTransfer.hh>
78 : #include <EnergyPlus/Plant/DataPlant.hh>
79 : #include <EnergyPlus/PlantUtilities.hh>
80 : #include <EnergyPlus/ScheduleManager.hh>
81 : #include <EnergyPlus/UtilityRoutines.hh>
82 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
83 :
84 : namespace EnergyPlus::PipeHeatTransfer {
85 :
86 : // Module containing the routines dealing with pipes with transport delay
87 : // and heat transfer.
88 :
89 : // MODULE INFORMATION:
90 : // AUTHOR Simon Rees
91 : // DATE WRITTEN July 2007
92 : // MODIFIED May 2008
93 : // RE-ENGINEERED na
94 :
95 : // PURPOSE OF THIS MODULE:
96 : // The purpose of this module is to simulate a pipe with heat transfer
97 :
98 : // METHODOLOGY EMPLOYED:
99 : // An implicit finite difference method is used to solve the temperature distribution of the
100 : // fluid in the pipe as a result of the transport delay and heat transfer to the environment.
101 : // For buried pipes, the simulation involves an implicit finite difference model of the soil,
102 : // which was originally based on Piechowski's thesis (below). Equation numbers for
103 : // pipe:underground calculations are from Piechowski's thesis. In Piechowski, the near-pipe
104 : // region is solved with a detailed finite difference grid, this current model makes use of
105 : // the Hanby model to simulate the actual pipe.
106 :
107 : // Kusuda, T. & Achenbach, P. (1965), 'Earth temperature and thermal diffusivity at
108 : // selected stations in the united states', ASHRAE Transactions 71(1), 61-75.
109 : // Piechowski, M. (1996), A Ground Coupled Heat Pump System with Energy Storage,
110 : // PhD thesis, University of Melbourne.
111 :
112 : // OTHER NOTES: Equation Numbers listed in buried pipe routines are from Piechowski's thesis
113 :
114 : enum class PipeIndoorBoundaryType
115 : {
116 : Invalid = -1,
117 : Zone,
118 : Schedule,
119 : Num
120 : };
121 : constexpr std::array<std::string_view, static_cast<int>(PipeIndoorBoundaryType::Num)> pipeIndoorBoundaryTypeNamesUC = {"ZONE", "SCHEDULE"};
122 :
123 : // Using/Aliasing
124 : using namespace GroundTemperatureManager;
125 :
126 : // Functions
127 :
128 4 : PlantComponent *PipeHTData::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType objectType, std::string const &objectName)
129 : {
130 : // Process the input data for pipes if it hasn't been done already
131 4 : if (state.dataPipeHT->GetPipeInputFlag) {
132 4 : GetPipesHeatTransfer(state);
133 4 : state.dataPipeHT->GetPipeInputFlag = false;
134 : }
135 : // Now look for this particular pipe in the list
136 4 : for (auto &pipe : state.dataPipeHT->PipeHT) {
137 4 : if (pipe.Type == objectType && pipe.Name == objectName) {
138 4 : return &pipe;
139 : }
140 : }
141 : // If we didn't find it, fatal
142 0 : ShowFatalError(state, format("PipeHTFactory: Error getting inputs for pipe named: {}", objectName));
143 : // Shut up the compiler
144 0 : return nullptr;
145 : }
146 :
147 58100 : void PipeHTData::simulate(EnergyPlusData &state,
148 : [[maybe_unused]] const PlantLocation &calledFromLocation,
149 : bool const FirstHVACIteration,
150 : [[maybe_unused]] Real64 &CurLoad,
151 : [[maybe_unused]] bool const RunFlag)
152 : {
153 58100 : this->InitPipesHeatTransfer(state, FirstHVACIteration);
154 : // make the calculations
155 802816 : for (int InnerTimeStepCtr = 1; InnerTimeStepCtr <= state.dataPipeHT->nsvNumInnerTimeSteps; ++InnerTimeStepCtr) {
156 744716 : switch (this->EnvironmentPtr) {
157 186179 : case EnvrnPtr::GroundEnv: {
158 186179 : this->CalcBuriedPipeSoil(state);
159 186179 : } break;
160 558537 : default: {
161 558537 : this->CalcPipesHeatTransfer(state);
162 558537 : } break;
163 : }
164 744716 : this->PushInnerTimeStepArrays();
165 : }
166 : // update variables
167 58100 : this->UpdatePipesHeatTransfer(state);
168 : // update report variables
169 58100 : this->ReportPipesHeatTransfer(state);
170 58100 : }
171 :
172 744716 : void PipeHTData::PushInnerTimeStepArrays()
173 : {
174 744716 : if (this->EnvironmentPtr == EnvrnPtr::GroundEnv) {
175 3723580 : for (int LengthIndex = 2; LengthIndex <= this->NumSections; ++LengthIndex) {
176 31836609 : for (int DepthIndex = 1; DepthIndex <= this->NumDepthNodes; ++DepthIndex) {
177 113196832 : for (int WidthIndex = 2; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
178 : // This will store the old 'current' values as the new 'previous values' This allows
179 : // us to use the previous time array as history terms in the equations
180 84897624 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Previous) =
181 84897624 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Current);
182 : }
183 : }
184 : }
185 : }
186 : // Then update the Hanby near pipe model temperatures
187 744716 : this->PreviousFluidTemp = this->FluidTemp;
188 744716 : this->PreviousPipeTemp = this->PipeTemp;
189 744716 : }
190 :
191 4 : void GetPipesHeatTransfer(EnergyPlusData &state)
192 : {
193 :
194 : // SUBROUTINE INFORMATION:
195 : // AUTHOR Simon Rees
196 : // DATE WRITTEN July 2007
197 : // MODIFIED na
198 : // RE-ENGINEERED na
199 : // PURPOSE OF THIS SUBROUTINE:
200 : // This subroutine reads the input for hydronic Pipe Heat Transfers
201 : // from the user input file. This will contain all of the information
202 : // needed to define and simulate the surface.
203 :
204 : // Using/Aliasing
205 : using BranchNodeConnections::TestCompSet;
206 :
207 : using NodeInputManager::GetOnlySingleNode;
208 : using namespace DataLoopNode;
209 : using OutAirNodeManager::CheckOutAirNodeNumber;
210 : using ScheduleManager::GetScheduleIndex;
211 :
212 : // SUBROUTINE PARAMETER DEFINITIONS:
213 4 : int constexpr NumPipeSections(20);
214 4 : int constexpr NumberOfDepthNodes(8); // Number of nodes in the cartesian grid-Should be an even # for now
215 4 : Real64 const SecondsInHour(Constant::SecInHour);
216 :
217 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
218 4 : bool ErrorsFound(false); // Set to true if errors in input,
219 :
220 : // fatal at end of routine
221 : int IOStatus; // Used in GetObjectItem
222 : int NumAlphas; // Number of Alphas for each GetObjectItem call
223 : int NumNumbers; // Number of Numbers for each GetObjectItem call
224 : int NumOfPipeHTInt; // Number of Pipe Heat Transfer objects
225 : int NumOfPipeHTExt; // Number of Pipe Heat Transfer objects
226 : int NumOfPipeHTUG; // Number of Pipe Heat Transfer objects
227 4 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
228 : // Initializations and allocations
229 4 : cCurrentModuleObject = "Pipe:Indoor";
230 4 : NumOfPipeHTInt = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
231 4 : cCurrentModuleObject = "Pipe:Outdoor";
232 4 : NumOfPipeHTExt = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
233 4 : cCurrentModuleObject = "Pipe:Underground";
234 4 : NumOfPipeHTUG = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
235 :
236 4 : state.dataPipeHT->nsvNumOfPipeHT = NumOfPipeHTInt + NumOfPipeHTExt + NumOfPipeHTUG;
237 : // allocate data structures
238 4 : if (allocated(state.dataPipeHT->PipeHT)) state.dataPipeHT->PipeHT.deallocate();
239 :
240 4 : state.dataPipeHT->PipeHT.allocate(state.dataPipeHT->nsvNumOfPipeHT);
241 4 : state.dataPipeHT->PipeHTUniqueNames.reserve(static_cast<unsigned>(state.dataPipeHT->nsvNumOfPipeHT));
242 4 : int Item = 0;
243 :
244 4 : cCurrentModuleObject = "Pipe:Indoor";
245 6 : for (int PipeItem = 1; PipeItem <= NumOfPipeHTInt; ++PipeItem) {
246 2 : ++Item;
247 : // get the object name
248 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
249 : cCurrentModuleObject,
250 : PipeItem,
251 2 : state.dataIPShortCut->cAlphaArgs,
252 : NumAlphas,
253 2 : state.dataIPShortCut->rNumericArgs,
254 : NumNumbers,
255 : IOStatus,
256 2 : state.dataIPShortCut->lNumericFieldBlanks,
257 2 : state.dataIPShortCut->lAlphaFieldBlanks,
258 2 : state.dataIPShortCut->cAlphaFieldNames,
259 2 : state.dataIPShortCut->cNumericFieldNames);
260 :
261 2 : GlobalNames::VerifyUniqueInterObjectName(state,
262 2 : state.dataPipeHT->PipeHTUniqueNames,
263 2 : state.dataIPShortCut->cAlphaArgs(1),
264 : cCurrentModuleObject,
265 2 : state.dataIPShortCut->cAlphaFieldNames(1),
266 : ErrorsFound);
267 2 : state.dataPipeHT->PipeHT(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
268 2 : state.dataPipeHT->PipeHT(Item).Type = DataPlant::PlantEquipmentType::PipeInterior;
269 :
270 : // General user input data
271 2 : state.dataPipeHT->PipeHT(Item).Construction = state.dataIPShortCut->cAlphaArgs(2);
272 2 : state.dataPipeHT->PipeHT(Item).ConstructionNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataConstruction->Construct);
273 :
274 2 : if (state.dataPipeHT->PipeHT(Item).ConstructionNum == 0) {
275 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
276 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
277 0 : ErrorsFound = true;
278 : }
279 :
280 : // get inlet node data
281 2 : state.dataPipeHT->PipeHT(Item).InletNode = state.dataIPShortCut->cAlphaArgs(3);
282 4 : state.dataPipeHT->PipeHT(Item).InletNodeNum = GetOnlySingleNode(state,
283 2 : state.dataIPShortCut->cAlphaArgs(3),
284 : ErrorsFound,
285 : DataLoopNode::ConnectionObjectType::PipeIndoor,
286 2 : state.dataIPShortCut->cAlphaArgs(1),
287 : DataLoopNode::NodeFluidType::Water,
288 : DataLoopNode::ConnectionType::Inlet,
289 : NodeInputManager::CompFluidStream::Primary,
290 : ObjectIsNotParent);
291 2 : if (state.dataPipeHT->PipeHT(Item).InletNodeNum == 0) {
292 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
293 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
294 0 : ErrorsFound = true;
295 : }
296 :
297 : // get outlet node data
298 2 : state.dataPipeHT->PipeHT(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(4);
299 4 : state.dataPipeHT->PipeHT(Item).OutletNodeNum = GetOnlySingleNode(state,
300 2 : state.dataIPShortCut->cAlphaArgs(4),
301 : ErrorsFound,
302 : DataLoopNode::ConnectionObjectType::PipeIndoor,
303 2 : state.dataIPShortCut->cAlphaArgs(1),
304 : DataLoopNode::NodeFluidType::Water,
305 : DataLoopNode::ConnectionType::Outlet,
306 : NodeInputManager::CompFluidStream::Primary,
307 : ObjectIsNotParent);
308 2 : if (state.dataPipeHT->PipeHT(Item).OutletNodeNum == 0) {
309 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
310 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
311 0 : ErrorsFound = true;
312 : }
313 :
314 4 : TestCompSet(state,
315 : cCurrentModuleObject,
316 2 : state.dataIPShortCut->cAlphaArgs(1),
317 2 : state.dataIPShortCut->cAlphaArgs(3),
318 2 : state.dataIPShortCut->cAlphaArgs(4),
319 : "Pipe Nodes");
320 :
321 : // get environmental boundary condition type
322 :
323 2 : if (state.dataIPShortCut->lAlphaFieldBlanks(5)) state.dataIPShortCut->cAlphaArgs(5) = "ZONE";
324 :
325 : PipeIndoorBoundaryType indoorType =
326 2 : static_cast<PipeIndoorBoundaryType>(getEnumValue(pipeIndoorBoundaryTypeNamesUC, state.dataIPShortCut->cAlphaArgs(5)));
327 2 : switch (indoorType) {
328 1 : case PipeIndoorBoundaryType::Zone:
329 1 : state.dataPipeHT->PipeHT(Item).EnvironmentPtr = EnvrnPtr::ZoneEnv;
330 1 : state.dataPipeHT->PipeHT(Item).EnvrZonePtr = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(6), state.dataHeatBal->Zone);
331 1 : if (state.dataPipeHT->PipeHT(Item).EnvrZonePtr == 0) {
332 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataIPShortCut->cAlphaArgs(6)));
333 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
334 0 : ErrorsFound = true;
335 : }
336 1 : break;
337 :
338 1 : case PipeIndoorBoundaryType::Schedule:
339 1 : state.dataPipeHT->PipeHT(Item).EnvironmentPtr = EnvrnPtr::ScheduleEnv;
340 1 : state.dataPipeHT->PipeHT(Item).EnvrSchedule = state.dataIPShortCut->cAlphaArgs(7);
341 1 : state.dataPipeHT->PipeHT(Item).EnvrSchedPtr = GetScheduleIndex(state, state.dataPipeHT->PipeHT(Item).EnvrSchedule);
342 1 : state.dataPipeHT->PipeHT(Item).EnvrVelSchedule = state.dataIPShortCut->cAlphaArgs(8);
343 1 : state.dataPipeHT->PipeHT(Item).EnvrVelSchedPtr = GetScheduleIndex(state, state.dataPipeHT->PipeHT(Item).EnvrVelSchedule);
344 1 : if (state.dataPipeHT->PipeHT(Item).EnvrSchedPtr == 0) {
345 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(7), state.dataIPShortCut->cAlphaArgs(7)));
346 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
347 0 : ErrorsFound = true;
348 : }
349 1 : if (state.dataPipeHT->PipeHT(Item).EnvrVelSchedPtr == 0) {
350 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(8), state.dataIPShortCut->cAlphaArgs(8)));
351 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
352 0 : ErrorsFound = true;
353 : }
354 1 : break;
355 :
356 0 : default:
357 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
358 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
359 0 : ShowContinueError(state, R"(Should be "ZONE" or "SCHEDULE")"); // TODO rename point
360 0 : ErrorsFound = true;
361 : }
362 :
363 : // dimensions
364 2 : state.dataPipeHT->PipeHT(Item).PipeID = state.dataIPShortCut->rNumericArgs(1);
365 2 : if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) { // not really necessary because idd field has "minimum> 0"
366 0 : ShowSevereError(state,
367 0 : format("GetPipesHeatTransfer: invalid {} of {:.4R}",
368 0 : state.dataIPShortCut->cNumericFieldNames(1),
369 0 : state.dataIPShortCut->rNumericArgs(1)));
370 0 : ShowContinueError(state, format("{} must be > 0.0", state.dataIPShortCut->cNumericFieldNames(1)));
371 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
372 :
373 0 : ErrorsFound = true;
374 : }
375 :
376 2 : state.dataPipeHT->PipeHT(Item).Length = state.dataIPShortCut->rNumericArgs(2);
377 2 : if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) { // not really necessary because idd field has "minimum> 0"
378 0 : ShowSevereError(state,
379 0 : format("GetPipesHeatTransfer: invalid {} of {:.4R}",
380 0 : state.dataIPShortCut->cNumericFieldNames(2),
381 0 : state.dataIPShortCut->rNumericArgs(2)));
382 0 : ShowContinueError(state, format("{} must be > 0.0", state.dataIPShortCut->cNumericFieldNames(2)));
383 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
384 0 : ErrorsFound = true;
385 : }
386 :
387 2 : if (state.dataPipeHT->PipeHT(Item).ConstructionNum != 0) {
388 2 : state.dataPipeHT->PipeHT(Item).ValidatePipeConstruction(state,
389 : cCurrentModuleObject,
390 2 : state.dataIPShortCut->cAlphaArgs(2),
391 2 : state.dataIPShortCut->cAlphaFieldNames(2),
392 2 : state.dataPipeHT->PipeHT(Item).ConstructionNum,
393 : ErrorsFound);
394 : }
395 :
396 : } // end of input loop
397 :
398 4 : cCurrentModuleObject = "Pipe:Outdoor";
399 5 : for (int PipeItem = 1; PipeItem <= NumOfPipeHTExt; ++PipeItem) {
400 1 : ++Item;
401 : // get the object name
402 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
403 : cCurrentModuleObject,
404 : PipeItem,
405 1 : state.dataIPShortCut->cAlphaArgs,
406 : NumAlphas,
407 1 : state.dataIPShortCut->rNumericArgs,
408 : NumNumbers,
409 : IOStatus,
410 1 : state.dataIPShortCut->lNumericFieldBlanks,
411 1 : state.dataIPShortCut->lAlphaFieldBlanks,
412 1 : state.dataIPShortCut->cAlphaFieldNames,
413 1 : state.dataIPShortCut->cNumericFieldNames);
414 :
415 1 : GlobalNames::VerifyUniqueInterObjectName(state,
416 1 : state.dataPipeHT->PipeHTUniqueNames,
417 1 : state.dataIPShortCut->cAlphaArgs(1),
418 : cCurrentModuleObject,
419 1 : state.dataIPShortCut->cAlphaFieldNames(1),
420 : ErrorsFound);
421 1 : state.dataPipeHT->PipeHT(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
422 1 : state.dataPipeHT->PipeHT(Item).Type = DataPlant::PlantEquipmentType::PipeExterior;
423 :
424 : // General user input data
425 1 : state.dataPipeHT->PipeHT(Item).Construction = state.dataIPShortCut->cAlphaArgs(2);
426 1 : state.dataPipeHT->PipeHT(Item).ConstructionNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataConstruction->Construct);
427 :
428 1 : if (state.dataPipeHT->PipeHT(Item).ConstructionNum == 0) {
429 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
430 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
431 0 : ErrorsFound = true;
432 : }
433 :
434 : // get inlet node data
435 1 : state.dataPipeHT->PipeHT(Item).InletNode = state.dataIPShortCut->cAlphaArgs(3);
436 2 : state.dataPipeHT->PipeHT(Item).InletNodeNum = GetOnlySingleNode(state,
437 1 : state.dataIPShortCut->cAlphaArgs(3),
438 : ErrorsFound,
439 : DataLoopNode::ConnectionObjectType::PipeOutdoor,
440 1 : state.dataIPShortCut->cAlphaArgs(1),
441 : DataLoopNode::NodeFluidType::Water,
442 : DataLoopNode::ConnectionType::Inlet,
443 : NodeInputManager::CompFluidStream::Primary,
444 : ObjectIsNotParent);
445 1 : if (state.dataPipeHT->PipeHT(Item).InletNodeNum == 0) {
446 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
447 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
448 0 : ErrorsFound = true;
449 : }
450 :
451 : // get outlet node data
452 1 : state.dataPipeHT->PipeHT(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(4);
453 2 : state.dataPipeHT->PipeHT(Item).OutletNodeNum = GetOnlySingleNode(state,
454 1 : state.dataIPShortCut->cAlphaArgs(4),
455 : ErrorsFound,
456 : DataLoopNode::ConnectionObjectType::PipeOutdoor,
457 1 : state.dataIPShortCut->cAlphaArgs(1),
458 : DataLoopNode::NodeFluidType::Water,
459 : DataLoopNode::ConnectionType::Outlet,
460 : NodeInputManager::CompFluidStream::Primary,
461 : ObjectIsNotParent);
462 1 : if (state.dataPipeHT->PipeHT(Item).OutletNodeNum == 0) {
463 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
464 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
465 0 : ErrorsFound = true;
466 : }
467 :
468 2 : TestCompSet(state,
469 : cCurrentModuleObject,
470 1 : state.dataIPShortCut->cAlphaArgs(1),
471 1 : state.dataIPShortCut->cAlphaArgs(3),
472 1 : state.dataIPShortCut->cAlphaArgs(4),
473 : "Pipe Nodes");
474 :
475 : // get environmental boundary condition type
476 : // PipeHT(Item)%Environment = 'OutdoorAir'
477 1 : state.dataPipeHT->PipeHT(Item).EnvironmentPtr = EnvrnPtr::OutsideAirEnv;
478 :
479 1 : state.dataPipeHT->PipeHT(Item).EnvrAirNode = state.dataIPShortCut->cAlphaArgs(5);
480 2 : state.dataPipeHT->PipeHT(Item).EnvrAirNodeNum = GetOnlySingleNode(state,
481 1 : state.dataIPShortCut->cAlphaArgs(5),
482 : ErrorsFound,
483 : DataLoopNode::ConnectionObjectType::PipeOutdoor,
484 1 : state.dataIPShortCut->cAlphaArgs(1),
485 : DataLoopNode::NodeFluidType::Air,
486 : DataLoopNode::ConnectionType::OutsideAirReference,
487 : NodeInputManager::CompFluidStream::Primary,
488 : ObjectIsNotParent);
489 1 : if (!state.dataIPShortCut->lAlphaFieldBlanks(5)) {
490 1 : if (!CheckOutAirNodeNumber(state, state.dataPipeHT->PipeHT(Item).EnvrAirNodeNum)) {
491 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
492 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
493 0 : ShowContinueError(state, "Outdoor Air Node not on OutdoorAir:NodeList or OutdoorAir:Node");
494 0 : ErrorsFound = true;
495 : }
496 : } else {
497 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
498 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
499 0 : ShowContinueError(state, format("An {} must be used ", state.dataIPShortCut->cAlphaFieldNames(5)));
500 0 : ErrorsFound = true;
501 : }
502 :
503 : // dimensions
504 1 : state.dataPipeHT->PipeHT(Item).PipeID = state.dataIPShortCut->rNumericArgs(1);
505 1 : if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) { // not really necessary because idd field has "minimum> 0"
506 0 : ShowSevereError(state,
507 0 : format("Invalid {} of {:.4R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
508 0 : ShowContinueError(state, format("{} must be > 0.0", state.dataIPShortCut->cNumericFieldNames(1)));
509 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
510 0 : ErrorsFound = true;
511 : }
512 :
513 1 : state.dataPipeHT->PipeHT(Item).Length = state.dataIPShortCut->rNumericArgs(2);
514 1 : if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) { // not really necessary because idd field has "minimum> 0"
515 0 : ShowSevereError(state,
516 0 : format("Invalid {} of {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
517 0 : ShowContinueError(state, format("{} must be > 0.0", state.dataIPShortCut->cNumericFieldNames(2)));
518 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
519 0 : ErrorsFound = true;
520 : }
521 :
522 1 : if (state.dataPipeHT->PipeHT(Item).ConstructionNum != 0) {
523 1 : state.dataPipeHT->PipeHT(Item).ValidatePipeConstruction(state,
524 : cCurrentModuleObject,
525 1 : state.dataIPShortCut->cAlphaArgs(2),
526 1 : state.dataIPShortCut->cAlphaFieldNames(2),
527 1 : state.dataPipeHT->PipeHT(Item).ConstructionNum,
528 : ErrorsFound);
529 : }
530 :
531 : } // end of input loop
532 :
533 4 : cCurrentModuleObject = "Pipe:Underground";
534 5 : for (int PipeItem = 1; PipeItem <= NumOfPipeHTUG; ++PipeItem) {
535 :
536 1 : ++Item;
537 : // get the object name
538 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
539 : cCurrentModuleObject,
540 : PipeItem,
541 1 : state.dataIPShortCut->cAlphaArgs,
542 : NumAlphas,
543 1 : state.dataIPShortCut->rNumericArgs,
544 : NumNumbers,
545 : IOStatus,
546 1 : state.dataIPShortCut->lNumericFieldBlanks,
547 1 : state.dataIPShortCut->lAlphaFieldBlanks,
548 1 : state.dataIPShortCut->cAlphaFieldNames,
549 1 : state.dataIPShortCut->cNumericFieldNames);
550 :
551 1 : GlobalNames::VerifyUniqueInterObjectName(state,
552 1 : state.dataPipeHT->PipeHTUniqueNames,
553 1 : state.dataIPShortCut->cAlphaArgs(1),
554 : cCurrentModuleObject,
555 1 : state.dataIPShortCut->cAlphaFieldNames(1),
556 : ErrorsFound);
557 1 : state.dataPipeHT->PipeHT(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
558 1 : state.dataPipeHT->PipeHT(Item).Type = DataPlant::PlantEquipmentType::PipeUnderground;
559 :
560 : // General user input data
561 1 : state.dataPipeHT->PipeHT(Item).Construction = state.dataIPShortCut->cAlphaArgs(2);
562 1 : state.dataPipeHT->PipeHT(Item).ConstructionNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataConstruction->Construct);
563 :
564 1 : if (state.dataPipeHT->PipeHT(Item).ConstructionNum == 0) {
565 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
566 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
567 0 : ErrorsFound = true;
568 : }
569 :
570 : // get inlet node data
571 1 : state.dataPipeHT->PipeHT(Item).InletNode = state.dataIPShortCut->cAlphaArgs(3);
572 2 : state.dataPipeHT->PipeHT(Item).InletNodeNum = GetOnlySingleNode(state,
573 1 : state.dataIPShortCut->cAlphaArgs(3),
574 : ErrorsFound,
575 : DataLoopNode::ConnectionObjectType::PipeUnderground,
576 1 : state.dataIPShortCut->cAlphaArgs(1),
577 : DataLoopNode::NodeFluidType::Water,
578 : DataLoopNode::ConnectionType::Inlet,
579 : NodeInputManager::CompFluidStream::Primary,
580 : ObjectIsNotParent);
581 1 : if (state.dataPipeHT->PipeHT(Item).InletNodeNum == 0) {
582 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
583 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
584 0 : ErrorsFound = true;
585 : }
586 :
587 : // get outlet node data
588 1 : state.dataPipeHT->PipeHT(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(4);
589 2 : state.dataPipeHT->PipeHT(Item).OutletNodeNum = GetOnlySingleNode(state,
590 1 : state.dataIPShortCut->cAlphaArgs(4),
591 : ErrorsFound,
592 : DataLoopNode::ConnectionObjectType::PipeUnderground,
593 1 : state.dataIPShortCut->cAlphaArgs(1),
594 : DataLoopNode::NodeFluidType::Water,
595 : DataLoopNode::ConnectionType::Outlet,
596 : NodeInputManager::CompFluidStream::Primary,
597 : ObjectIsNotParent);
598 1 : if (state.dataPipeHT->PipeHT(Item).OutletNodeNum == 0) {
599 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
600 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
601 0 : ErrorsFound = true;
602 : }
603 :
604 2 : TestCompSet(state,
605 : cCurrentModuleObject,
606 1 : state.dataIPShortCut->cAlphaArgs(1),
607 1 : state.dataIPShortCut->cAlphaArgs(3),
608 1 : state.dataIPShortCut->cAlphaArgs(4),
609 : "Pipe Nodes");
610 :
611 1 : state.dataPipeHT->PipeHT(Item).EnvironmentPtr = EnvrnPtr::GroundEnv;
612 :
613 : // Solar inclusion flag
614 : // A6, \field Sun Exposure
615 1 : if (Util::SameString(state.dataIPShortCut->cAlphaArgs(5), "SUNEXPOSED")) {
616 1 : state.dataPipeHT->PipeHT(Item).SolarExposed = true;
617 0 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(5), "NOSUN")) {
618 0 : state.dataPipeHT->PipeHT(Item).SolarExposed = false;
619 : } else {
620 0 : ShowSevereError(state, format("GetPipesHeatTransfer: invalid key for sun exposure flag for {}", state.dataIPShortCut->cAlphaArgs(1)));
621 0 : ShowContinueError(state, format("Key should be either SunExposed or NoSun. Entered Key: {}", state.dataIPShortCut->cAlphaArgs(5)));
622 0 : ErrorsFound = true;
623 : }
624 :
625 : // dimensions
626 1 : state.dataPipeHT->PipeHT(Item).PipeID = state.dataIPShortCut->rNumericArgs(1);
627 1 : if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) { // not really necessary because idd field has "minimum> 0"
628 0 : ShowSevereError(state,
629 0 : format("Invalid {} of {:.4R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
630 0 : ShowContinueError(state, format("{} must be > 0.0", state.dataIPShortCut->cNumericFieldNames(1)));
631 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
632 0 : ErrorsFound = true;
633 : }
634 :
635 1 : state.dataPipeHT->PipeHT(Item).Length = state.dataIPShortCut->rNumericArgs(2);
636 1 : if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) { // not really necessary because idd field has "minimum> 0"
637 0 : ShowSevereError(state,
638 0 : format("Invalid {} of {:.4R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
639 0 : ShowContinueError(state, format("{} must be > 0.0", state.dataIPShortCut->cNumericFieldNames(2)));
640 0 : ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
641 0 : ErrorsFound = true;
642 : }
643 :
644 : // Also get the soil material name
645 : // A7, \field Soil Material
646 1 : state.dataPipeHT->PipeHT(Item).SoilMaterial = state.dataIPShortCut->cAlphaArgs(6);
647 1 : state.dataPipeHT->PipeHT(Item).SoilMaterialNum = Util::FindItemInPtrList(state.dataIPShortCut->cAlphaArgs(6), state.dataMaterial->Material);
648 1 : if (state.dataPipeHT->PipeHT(Item).SoilMaterialNum == 0) {
649 0 : ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(6), state.dataPipeHT->PipeHT(Item).SoilMaterial));
650 0 : ShowContinueError(state, format("Found in {}={}", cCurrentModuleObject, state.dataPipeHT->PipeHT(Item).Name));
651 0 : ErrorsFound = true;
652 : } else {
653 : auto const *thisMaterialSoil =
654 1 : dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(state.dataPipeHT->PipeHT(Item).SoilMaterialNum));
655 1 : assert(thisMaterialSoil != nullptr);
656 1 : state.dataPipeHT->PipeHT(Item).SoilDensity = thisMaterialSoil->Density;
657 1 : state.dataPipeHT->PipeHT(Item).SoilDepth = thisMaterialSoil->Thickness;
658 1 : state.dataPipeHT->PipeHT(Item).SoilCp = thisMaterialSoil->SpecHeat;
659 1 : state.dataPipeHT->PipeHT(Item).SoilConductivity = thisMaterialSoil->Conductivity;
660 1 : state.dataPipeHT->PipeHT(Item).SoilThermAbs = thisMaterialSoil->AbsorpThermal;
661 1 : state.dataPipeHT->PipeHT(Item).SoilSolarAbs = thisMaterialSoil->AbsorpSolar;
662 1 : state.dataPipeHT->PipeHT(Item).SoilRoughness = thisMaterialSoil->Roughness;
663 1 : state.dataPipeHT->PipeHT(Item).PipeDepth = state.dataPipeHT->PipeHT(Item).SoilDepth + state.dataPipeHT->PipeHT(Item).PipeID / 2.0;
664 1 : state.dataPipeHT->PipeHT(Item).DomainDepth = state.dataPipeHT->PipeHT(Item).PipeDepth * 2.0;
665 2 : state.dataPipeHT->PipeHT(Item).SoilDiffusivity = state.dataPipeHT->PipeHT(Item).SoilConductivity /
666 1 : (state.dataPipeHT->PipeHT(Item).SoilDensity * state.dataPipeHT->PipeHT(Item).SoilCp);
667 1 : state.dataPipeHT->PipeHT(Item).SoilDiffusivityPerDay =
668 1 : state.dataPipeHT->PipeHT(Item).SoilDiffusivity * SecondsInHour * Constant::HoursInDay;
669 :
670 : // Mesh the cartesian domain
671 1 : state.dataPipeHT->PipeHT(Item).NumDepthNodes = NumberOfDepthNodes;
672 1 : state.dataPipeHT->PipeHT(Item).PipeNodeDepth = state.dataPipeHT->PipeHT(Item).NumDepthNodes / 2;
673 1 : state.dataPipeHT->PipeHT(Item).PipeNodeWidth = state.dataPipeHT->PipeHT(Item).NumDepthNodes / 2;
674 1 : state.dataPipeHT->PipeHT(Item).DomainDepth = state.dataPipeHT->PipeHT(Item).PipeDepth * 2.0;
675 1 : state.dataPipeHT->PipeHT(Item).dSregular =
676 1 : state.dataPipeHT->PipeHT(Item).DomainDepth / (state.dataPipeHT->PipeHT(Item).NumDepthNodes - 1);
677 : }
678 :
679 1 : if (state.dataPipeHT->PipeHT(Item).ConstructionNum != 0) {
680 1 : state.dataPipeHT->PipeHT(Item).ValidatePipeConstruction(state,
681 : cCurrentModuleObject,
682 1 : state.dataIPShortCut->cAlphaArgs(2),
683 1 : state.dataIPShortCut->cAlphaFieldNames(2),
684 1 : state.dataPipeHT->PipeHT(Item).ConstructionNum,
685 : ErrorsFound);
686 : }
687 :
688 : // Get ground temperature model
689 1 : state.dataPipeHT->PipeHT(Item).groundTempModel =
690 2 : GetGroundTempModelAndInit(state, state.dataIPShortCut->cAlphaArgs(7), state.dataIPShortCut->cAlphaArgs(8));
691 :
692 : // Select number of pipe sections. Hanby's optimal number of 20 section is selected.
693 1 : state.dataPipeHT->PipeHT(Item).NumSections = NumPipeSections;
694 :
695 : // For buried pipes, we need to allocate the cartesian finite difference array
696 2 : state.dataPipeHT->PipeHT(Item).T.allocate(state.dataPipeHT->PipeHT(Item).PipeNodeWidth,
697 1 : state.dataPipeHT->PipeHT(Item).NumDepthNodes,
698 1 : state.dataPipeHT->PipeHT(Item).NumSections,
699 : TimeIndex::Tentative);
700 1 : state.dataPipeHT->PipeHT(Item).T = 0.0;
701 :
702 : } // PipeUG input loop
703 :
704 8 : for (Item = 1; Item <= state.dataPipeHT->nsvNumOfPipeHT; ++Item) {
705 : // Select number of pipe sections. Hanby's optimal number of 20 section is selected.
706 4 : int NumSections = NumPipeSections;
707 4 : state.dataPipeHT->PipeHT(Item).NumSections = NumPipeSections;
708 :
709 : // We need to allocate the Hanby model arrays for all pipes, including buried
710 4 : state.dataPipeHT->PipeHT(Item).TentativeFluidTemp.allocate({0, NumSections});
711 4 : state.dataPipeHT->PipeHT(Item).TentativePipeTemp.allocate({0, NumSections});
712 4 : state.dataPipeHT->PipeHT(Item).FluidTemp.allocate({0, NumSections});
713 4 : state.dataPipeHT->PipeHT(Item).PreviousFluidTemp.allocate({0, NumSections});
714 4 : state.dataPipeHT->PipeHT(Item).PipeTemp.allocate({0, NumSections});
715 4 : state.dataPipeHT->PipeHT(Item).PreviousPipeTemp.allocate({0, NumSections});
716 :
717 4 : state.dataPipeHT->PipeHT(Item).TentativeFluidTemp = 0.0;
718 4 : state.dataPipeHT->PipeHT(Item).FluidTemp = 0.0;
719 4 : state.dataPipeHT->PipeHT(Item).PreviousFluidTemp = 0.0;
720 4 : state.dataPipeHT->PipeHT(Item).TentativePipeTemp = 0.0;
721 4 : state.dataPipeHT->PipeHT(Item).PipeTemp = 0.0;
722 4 : state.dataPipeHT->PipeHT(Item).PreviousPipeTemp = 0.0;
723 :
724 : // work out heat transfer areas (area per section)
725 4 : state.dataPipeHT->PipeHT(Item).InsideArea =
726 4 : Constant::Pi * state.dataPipeHT->PipeHT(Item).PipeID * state.dataPipeHT->PipeHT(Item).Length / NumSections;
727 4 : state.dataPipeHT->PipeHT(Item).OutsideArea =
728 4 : Constant::Pi * (state.dataPipeHT->PipeHT(Item).PipeOD + 2 * state.dataPipeHT->PipeHT(Item).InsulationThickness) *
729 4 : state.dataPipeHT->PipeHT(Item).Length / NumSections;
730 :
731 : // cross sectional area
732 4 : state.dataPipeHT->PipeHT(Item).SectionArea = Constant::Pi * 0.25 * pow_2(state.dataPipeHT->PipeHT(Item).PipeID);
733 :
734 : // pipe & insulation mass
735 4 : state.dataPipeHT->PipeHT(Item).PipeHeatCapacity =
736 4 : state.dataPipeHT->PipeHT(Item).PipeCp * state.dataPipeHT->PipeHT(Item).PipeDensity *
737 4 : (Constant::Pi * 0.25 * pow_2(state.dataPipeHT->PipeHT(Item).PipeOD) - state.dataPipeHT->PipeHT(Item).SectionArea); // the metal component
738 : }
739 :
740 : // final error check
741 4 : if (ErrorsFound) {
742 0 : ShowFatalError(state, "GetPipesHeatTransfer: Errors found in input. Preceding conditions cause termination.");
743 : }
744 :
745 : // Set up the output variables CurrentModuleObject='Pipe:Indoor/Outdoor/Underground'
746 8 : for (Item = 1; Item <= state.dataPipeHT->nsvNumOfPipeHT; ++Item) {
747 :
748 8 : SetupOutputVariable(state,
749 : "Pipe Fluid Heat Transfer Rate",
750 : Constant::Units::W,
751 4 : state.dataPipeHT->PipeHT(Item).FluidHeatLossRate,
752 : OutputProcessor::TimeStepType::System,
753 : OutputProcessor::StoreType::Average,
754 4 : state.dataPipeHT->PipeHT(Item).Name);
755 8 : SetupOutputVariable(state,
756 : "Pipe Fluid Heat Transfer Energy",
757 : Constant::Units::J,
758 4 : state.dataPipeHT->PipeHT(Item).FluidHeatLossEnergy,
759 : OutputProcessor::TimeStepType::System,
760 : OutputProcessor::StoreType::Sum,
761 4 : state.dataPipeHT->PipeHT(Item).Name);
762 :
763 4 : if (state.dataPipeHT->PipeHT(Item).EnvironmentPtr == EnvrnPtr::ZoneEnv) {
764 2 : SetupOutputVariable(state,
765 : "Pipe Ambient Heat Transfer Rate",
766 : Constant::Units::W,
767 1 : state.dataPipeHT->PipeHT(Item).EnvironmentHeatLossRate,
768 : OutputProcessor::TimeStepType::System,
769 : OutputProcessor::StoreType::Average,
770 1 : state.dataPipeHT->PipeHT(Item).Name);
771 2 : SetupOutputVariable(state,
772 : "Pipe Ambient Heat Transfer Energy",
773 : Constant::Units::J,
774 1 : state.dataPipeHT->PipeHT(Item).EnvHeatLossEnergy,
775 : OutputProcessor::TimeStepType::System,
776 : OutputProcessor::StoreType::Sum,
777 1 : state.dataPipeHT->PipeHT(Item).Name);
778 :
779 3 : SetupZoneInternalGain(state,
780 1 : state.dataPipeHT->PipeHT(Item).EnvrZonePtr,
781 1 : state.dataPipeHT->PipeHT(Item).Name,
782 : DataHeatBalance::IntGainType::PipeIndoor,
783 1 : &state.dataPipeHT->PipeHT(Item).ZoneHeatGainRate);
784 : }
785 :
786 8 : SetupOutputVariable(state,
787 : "Pipe Mass Flow Rate",
788 : Constant::Units::kg_s,
789 4 : state.dataPipeHT->PipeHT(Item).MassFlowRate,
790 : OutputProcessor::TimeStepType::System,
791 : OutputProcessor::StoreType::Average,
792 4 : state.dataPipeHT->PipeHT(Item).Name);
793 8 : SetupOutputVariable(state,
794 : "Pipe Volume Flow Rate",
795 : Constant::Units::m3_s,
796 4 : state.dataPipeHT->PipeHT(Item).VolumeFlowRate,
797 : OutputProcessor::TimeStepType::System,
798 : OutputProcessor::StoreType::Average,
799 4 : state.dataPipeHT->PipeHT(Item).Name);
800 8 : SetupOutputVariable(state,
801 : "Pipe Inlet Temperature",
802 : Constant::Units::C,
803 4 : state.dataPipeHT->PipeHT(Item).FluidInletTemp,
804 : OutputProcessor::TimeStepType::System,
805 : OutputProcessor::StoreType::Average,
806 4 : state.dataPipeHT->PipeHT(Item).Name);
807 8 : SetupOutputVariable(state,
808 : "Pipe Outlet Temperature",
809 : Constant::Units::C,
810 4 : state.dataPipeHT->PipeHT(Item).FluidOutletTemp,
811 : OutputProcessor::TimeStepType::System,
812 : OutputProcessor::StoreType::Average,
813 4 : state.dataPipeHT->PipeHT(Item).Name);
814 : }
815 4 : }
816 :
817 4 : void PipeHTData::ValidatePipeConstruction(EnergyPlusData &state,
818 : std::string const &PipeType, // module object of pipe (error messages)
819 : std::string const &ConstructionName, // construction name of pipe (error messages)
820 : std::string_view FieldName, // fieldname of pipe (error messages)
821 : int const ConstructionNum, // pointer into construction data
822 : bool &ErrorsFound // set to true if errors found here
823 : )
824 : {
825 :
826 : // SUBROUTINE INFORMATION:
827 : // AUTHOR Linda Lawrie
828 : // DATE WRITTEN August 2008
829 : // MODIFIED na
830 : // RE-ENGINEERED na
831 :
832 : // PURPOSE OF THIS SUBROUTINE:
833 : // This routine, called from GetInput, validates the pipe construction usage.
834 :
835 : // METHODOLOGY EMPLOYED:
836 : // na
837 :
838 : // REFERENCES:
839 : // na
840 :
841 : // Using/Aliasing
842 :
843 : // Locals
844 : // SUBROUTINE ARGUMENT DEFINITIONS:
845 :
846 : // SUBROUTINE PARAMETER DEFINITIONS:
847 : // na
848 :
849 : // INTERFACE BLOCK SPECIFICATIONS:
850 : // na
851 :
852 : // DERIVED TYPE DEFINITIONS:
853 : // na
854 :
855 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
856 : Real64 Density; // average density [kg/m^3]
857 : Real64 SpHeat; // average specific heat [J/kg.K]
858 4 : Real64 Resistance = 0.0;
859 4 : Real64 TotThickness = 0.0;
860 :
861 : // CTF stuff
862 4 : int TotalLayers = state.dataConstruction->Construct(ConstructionNum).TotLayers;
863 : // get pipe properties
864 4 : if (TotalLayers == 1) { // no insulation layer
865 :
866 0 : this->PipeConductivity = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(1))->Conductivity;
867 0 : this->PipeDensity = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(1))->Density;
868 0 : this->PipeCp = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(1))->SpecHeat;
869 0 : this->PipeOD = this->PipeID + 2.0 * state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(1))->Thickness;
870 0 : this->InsulationOD = this->PipeOD;
871 0 : this->SumTK = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(1))->Thickness /
872 0 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(1))->Conductivity;
873 :
874 4 : } else if (TotalLayers >= 2) { // first layers are insulation, last layer is pipe
875 :
876 8 : for (int LayerNum = 1; LayerNum <= TotalLayers - 1; ++LayerNum) {
877 4 : Resistance += state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Thickness /
878 4 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Conductivity;
879 4 : Density = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Density *
880 4 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Thickness;
881 4 : TotThickness += state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Thickness;
882 4 : SpHeat = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->SpecHeat *
883 4 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Thickness;
884 4 : this->InsulationThickness =
885 4 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Thickness;
886 4 : this->SumTK += state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Thickness /
887 4 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(LayerNum))->Conductivity;
888 : }
889 :
890 4 : this->InsulationResistance = Resistance;
891 4 : this->InsulationConductivity = TotThickness / Resistance;
892 4 : this->InsulationDensity = Density / TotThickness;
893 4 : this->InsulationCp = SpHeat / TotThickness;
894 4 : this->InsulationThickness = TotThickness;
895 :
896 4 : this->PipeConductivity =
897 4 : state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(TotalLayers))->Conductivity;
898 4 : this->PipeDensity = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(TotalLayers))->Density;
899 4 : this->PipeCp = state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(TotalLayers))->SpecHeat;
900 :
901 4 : this->PipeOD =
902 4 : this->PipeID + 2.0 * state.dataMaterial->Material(state.dataConstruction->Construct(ConstructionNum).LayerPoint(TotalLayers))->Thickness;
903 4 : this->InsulationOD = this->PipeOD + 2.0 * this->InsulationThickness;
904 :
905 : } else {
906 0 : ShowSevereError(
907 0 : state, format("{}: invalid {}=\"{}\", too many layers=[{}], only 1 or 2 allowed.", PipeType, FieldName, ConstructionName, TotalLayers));
908 0 : ErrorsFound = true;
909 : }
910 4 : }
911 :
912 4 : void PipeHTData::oneTimeInit_new(EnergyPlusData &state)
913 : {
914 4 : bool errFlag = false;
915 4 : PlantUtilities::ScanPlantLoopsForObject(state, this->Name, this->Type, this->plantLoc, errFlag, _, _, _, _, _);
916 4 : if (errFlag) {
917 0 : ShowFatalError(state, "InitPipesHeatTransfer: Program terminated due to previous condition(s).");
918 : }
919 4 : }
920 :
921 58100 : void PipeHTData::InitPipesHeatTransfer(EnergyPlusData &state, bool const FirstHVACIteration // component number
922 : )
923 : {
924 :
925 : // SUBROUTINE INFORMATION:
926 : // AUTHOR Simon Rees
927 : // DATE WRITTEN July 2007
928 : // MODIFIED L. Gu, 6/19/08, pipe wall heat capacity has metal layer only
929 : // RE-ENGINEERED na
930 :
931 : // PURPOSE OF THIS SUBROUTINE:
932 : // This subroutine Resets the elements of the data structure as necessary
933 : // at the first step, and start of each call to simulated
934 :
935 : // METHODOLOGY EMPLOYED:
936 : // Check flags and update data structure
937 :
938 : // Using/Aliasing
939 58100 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
940 58100 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
941 : using FluidProperties::GetDensityGlycol;
942 : using FluidProperties::GetSpecificHeatGlycol;
943 : using ScheduleManager::GetCurrentScheduleValue;
944 :
945 : // SUBROUTINE PARAMETER DEFINITIONS:
946 : static constexpr std::string_view RoutineName("InitPipesHeatTransfer");
947 :
948 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
949 :
950 : Real64 FirstTemperatures; // initial temperature of every node in pipe (set to inlet temp) [C]
951 : int TimeIndex;
952 : int LengthIndex;
953 : int DepthIndex;
954 : int WidthIndex;
955 : Real64 CurrentDepth;
956 : Real64 CurTemp;
957 : Real64 CurSimDay;
958 : bool PushArrays;
959 :
960 : // Assign variable
961 58100 : CurSimDay = double(state.dataGlobal->DayOfSim);
962 :
963 : // some useful module variables
964 58100 : state.dataPipeHT->nsvInletNodeNum = this->InletNodeNum;
965 58100 : state.dataPipeHT->nsvOutletNodeNum = this->OutletNodeNum;
966 58100 : state.dataPipeHT->nsvMassFlowRate = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).MassFlowRate;
967 58100 : state.dataPipeHT->nsvInletTemp = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).Temp;
968 :
969 : // initialize temperatures by inlet node temp
970 58100 : if ((state.dataGlobal->BeginSimFlag && this->BeginSimInit) || (state.dataGlobal->BeginEnvrnFlag && this->BeginSimEnvrn)) {
971 :
972 20 : if (this->EnvironmentPtr == EnvrnPtr::GroundEnv) {
973 20 : for (TimeIndex = TimeIndex::Previous; TimeIndex <= TimeIndex::Tentative; ++TimeIndex) {
974 : // Loop through all length, depth, and width of pipe to init soil temperature
975 315 : for (LengthIndex = 1; LengthIndex <= this->NumSections; ++LengthIndex) {
976 2700 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes; ++DepthIndex) {
977 12000 : for (WidthIndex = 1; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
978 9600 : CurrentDepth = (DepthIndex - 1) * this->dSregular;
979 9600 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex) = this->TBND(state, CurrentDepth);
980 : }
981 : }
982 : }
983 : }
984 : }
985 :
986 : // We also need to re-init the Hanby arrays for all pipes, including buried
987 20 : FirstTemperatures = 21.0; // Node(InletNodeNum)%Temp
988 20 : this->TentativeFluidTemp = FirstTemperatures;
989 20 : this->FluidTemp = FirstTemperatures;
990 20 : this->PreviousFluidTemp = FirstTemperatures;
991 20 : this->TentativePipeTemp = FirstTemperatures;
992 20 : this->PipeTemp = FirstTemperatures;
993 20 : this->PreviousPipeTemp = FirstTemperatures;
994 20 : this->PreviousSimTime = 0.0;
995 20 : state.dataPipeHT->nsvDeltaTime = 0.0;
996 20 : state.dataPipeHT->nsvOutletTemp = 0.0;
997 20 : state.dataPipeHT->nsvEnvironmentTemp = 0.0;
998 20 : state.dataPipeHT->nsvEnvHeatLossRate = 0.0;
999 20 : state.dataPipeHT->nsvFluidHeatLossRate = 0.0;
1000 :
1001 20 : this->BeginSimInit = false;
1002 20 : this->BeginSimEnvrn = false;
1003 : }
1004 :
1005 58100 : if (!state.dataGlobal->BeginSimFlag) this->BeginSimInit = true;
1006 58100 : if (!state.dataGlobal->BeginEnvrnFlag) this->BeginSimEnvrn = true;
1007 :
1008 : // time step in seconds
1009 58100 : state.dataPipeHT->nsvDeltaTime = TimeStepSysSec;
1010 58100 : state.dataPipeHT->nsvNumInnerTimeSteps = int(state.dataPipeHT->nsvDeltaTime / InnerDeltaTime);
1011 :
1012 : // previous temps are updated if necessary at start of timestep rather than end
1013 58100 : if ((FirstHVACIteration && this->FirstHVACupdateFlag) || (state.dataGlobal->BeginEnvrnFlag && this->BeginEnvrnupdateFlag)) {
1014 :
1015 : // We need to update boundary conditions here, as well as updating the arrays
1016 7260 : if (this->EnvironmentPtr == EnvrnPtr::GroundEnv) {
1017 :
1018 : // And then update Ground Boundary Conditions
1019 7260 : for (TimeIndex = 1; TimeIndex <= TimeIndex::Tentative; ++TimeIndex) {
1020 114345 : for (LengthIndex = 1; LengthIndex <= this->NumSections; ++LengthIndex) {
1021 980100 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes; ++DepthIndex) {
1022 : // Farfield boundary
1023 871200 : CurrentDepth = (DepthIndex - 1) * this->dSregular;
1024 871200 : CurTemp = this->TBND(state, CurrentDepth);
1025 871200 : this->T(1, DepthIndex, LengthIndex, TimeIndex) = CurTemp;
1026 : }
1027 544500 : for (WidthIndex = 1; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
1028 : // Bottom side of boundary
1029 435600 : CurrentDepth = this->DomainDepth;
1030 435600 : CurTemp = this->TBND(state, CurrentDepth);
1031 435600 : this->T(WidthIndex, this->NumDepthNodes, LengthIndex, TimeIndex) = CurTemp;
1032 : }
1033 : }
1034 : }
1035 : }
1036 :
1037 : // should next choose environment temperature according to coupled with air or ground
1038 7260 : switch (this->EnvironmentPtr) {
1039 1815 : case EnvrnPtr::GroundEnv: {
1040 : // EnvironmentTemp = GroundTemp
1041 1815 : } break;
1042 1815 : case EnvrnPtr::OutsideAirEnv: {
1043 1815 : state.dataPipeHT->nsvEnvironmentTemp = state.dataEnvrn->OutDryBulbTemp;
1044 1815 : } break;
1045 1815 : case EnvrnPtr::ZoneEnv: {
1046 1815 : state.dataPipeHT->nsvEnvironmentTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->EnvrZonePtr).MAT;
1047 1815 : } break;
1048 1815 : case EnvrnPtr::ScheduleEnv: {
1049 1815 : state.dataPipeHT->nsvEnvironmentTemp = GetCurrentScheduleValue(state, this->EnvrSchedPtr);
1050 1815 : } break;
1051 0 : case EnvrnPtr::None: { // default to outside temp
1052 0 : state.dataPipeHT->nsvEnvironmentTemp = state.dataEnvrn->OutDryBulbTemp;
1053 0 : } break;
1054 0 : default:
1055 0 : break;
1056 : }
1057 :
1058 7260 : this->BeginEnvrnupdateFlag = false;
1059 7260 : this->FirstHVACupdateFlag = false;
1060 : }
1061 :
1062 58100 : if (!state.dataGlobal->BeginEnvrnFlag) this->BeginEnvrnupdateFlag = true;
1063 58100 : if (!FirstHVACIteration) this->FirstHVACupdateFlag = true;
1064 :
1065 : // Calculate the current sim time for this pipe (not necessarily structure variable, but it is ok for consistency)
1066 58100 : this->CurrentSimTime = (state.dataGlobal->DayOfSim - 1) * 24 + state.dataGlobal->HourOfDay - 1 +
1067 58100 : (state.dataGlobal->TimeStep - 1) * state.dataGlobal->TimeStepZone + SysTimeElapsed;
1068 58100 : if (std::abs(this->CurrentSimTime - this->PreviousSimTime) > 1.0e-6) {
1069 6372 : PushArrays = true;
1070 6372 : this->PreviousSimTime = this->CurrentSimTime;
1071 : } else {
1072 51728 : PushArrays = false; // Time hasn't passed, don't accept the tentative values yet!
1073 : }
1074 :
1075 58100 : if (PushArrays) {
1076 :
1077 : // If sim time has changed all values from previous runs should have been acceptable.
1078 : // Thus we will now shift the arrays from 2>1 and 3>2 so we can then begin
1079 : // to update 2 and 3 again.
1080 6372 : if (this->EnvironmentPtr == EnvrnPtr::GroundEnv) {
1081 31860 : for (LengthIndex = 2; LengthIndex <= this->NumSections; ++LengthIndex) {
1082 272403 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes; ++DepthIndex) {
1083 968544 : for (WidthIndex = 2; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
1084 : // This will essentially 'accept' the tentative values that were calculated last iteration
1085 : // as the new officially 'current' values
1086 726408 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Current) =
1087 726408 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative);
1088 : }
1089 : }
1090 : }
1091 : }
1092 :
1093 : // Then update the Hanby near pipe model temperatures
1094 6372 : this->FluidTemp = this->TentativeFluidTemp;
1095 6372 : this->PipeTemp = this->TentativePipeTemp;
1096 :
1097 : } else { // IF(.NOT. FirstHVACIteration)THEN
1098 :
1099 : // If we don't have FirstHVAC, the last iteration values were not accepted, and we should
1100 : // not step through time. Thus we will revert our T(3,:,:,:) array back to T(2,:,:,:) to
1101 : // start over with the same values as last time.
1102 1034560 : for (LengthIndex = 2; LengthIndex <= this->NumSections; ++LengthIndex) {
1103 2948496 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes; ++DepthIndex) {
1104 7862656 : for (WidthIndex = 2; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
1105 : // This will essentially erase the past iterations and revert back to the correct values
1106 5896992 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative) =
1107 5896992 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Current);
1108 : }
1109 : }
1110 : }
1111 :
1112 : // Similarly for Hanby model arrays
1113 51728 : this->TentativeFluidTemp = this->FluidTemp;
1114 51728 : this->TentativePipeTemp = this->PipeTemp;
1115 : }
1116 :
1117 : // This still catches even in winter design day
1118 : // Even though the loop eventually has no flow rate, it appears it initializes to a value, then converges to OFF
1119 : // Thus, this is called at the beginning of every time step once.
1120 :
1121 58100 : this->FluidSpecHeat = GetSpecificHeatGlycol(state,
1122 58100 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
1123 58100 : state.dataPipeHT->nsvInletTemp,
1124 58100 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
1125 : RoutineName);
1126 58100 : this->FluidDensity = GetDensityGlycol(state,
1127 58100 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
1128 58100 : state.dataPipeHT->nsvInletTemp,
1129 58100 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
1130 : RoutineName);
1131 :
1132 : // At this point, for all Pipe:Interior objects we should zero out the energy and rate arrays
1133 58100 : this->FluidHeatLossRate = 0.0;
1134 58100 : this->FluidHeatLossEnergy = 0.0;
1135 58100 : this->EnvironmentHeatLossRate = 0.0;
1136 58100 : this->EnvHeatLossEnergy = 0.0;
1137 58100 : this->ZoneHeatGainRate = 0.0;
1138 58100 : state.dataPipeHT->nsvFluidHeatLossRate = 0.0;
1139 58100 : state.dataPipeHT->nsvEnvHeatLossRate = 0.0;
1140 58100 : state.dataPipeHT->nsvOutletTemp = 0.0;
1141 :
1142 58100 : if (this->FluidDensity > 0.0) {
1143 : // The density will only be zero the first time through, which will be a warmup day, and not reported
1144 58100 : state.dataPipeHT->nsvVolumeFlowRate = state.dataPipeHT->nsvMassFlowRate / this->FluidDensity;
1145 : }
1146 58100 : }
1147 :
1148 : //==============================================================================
1149 :
1150 4467317 : void PipeHTData::CalcPipesHeatTransfer(EnergyPlusData &state, ObjexxFCL::Optional_int_const LengthIndex)
1151 : {
1152 :
1153 : // AUTHOR Simon Rees
1154 : // DATE WRITTEN July 2007
1155 : // MODIFIED na
1156 : // RE-ENGINEERED na
1157 :
1158 : // PURPOSE OF THIS SUBROUTINE:
1159 : // This subroutine does all of the stuff that is necessary to simulate
1160 : // a Pipe Heat Transfer. Calls are made to appropriate routines
1161 : // for heat transfer coefficients
1162 :
1163 : // METHODOLOGY EMPLOYED:
1164 : // Differential equations for pipe and fluid nodes along the pipe are solved
1165 : // taking backward differences in time.
1166 : // The heat loss/gain calculations are run continuously, even when the loop is off.
1167 : // Fluid temps will drift according to environmental conditions when there is zero flow.
1168 :
1169 : // REFERENCES:
1170 :
1171 : // Using/Aliasing
1172 : using namespace DataEnvironment;
1173 :
1174 : // fluid node heat balance (see engineering doc).
1175 4467317 : Real64 A1(0.0); // sum of the heat balance terms
1176 4467317 : Real64 A2(0.0); // mass flow term
1177 4467317 : Real64 A3(0.0); // inside pipe wall convection term
1178 4467317 : Real64 A4(0.0); // fluid node heat capacity term
1179 : // pipe wall node heat balance (see engineering doc).
1180 4467317 : Real64 B1(0.0); // sum of the heat balance terms
1181 4467317 : Real64 B2(0.0); // inside pipe wall convection term
1182 4467317 : Real64 B3(0.0); // outside pipe wall convection term
1183 4467317 : Real64 B4(0.0); // fluid node heat capacity term
1184 :
1185 4467317 : Real64 AirConvCoef(0.0); // air-pipe convection coefficient
1186 4467317 : Real64 FluidConvCoef(0.0); // fluid-pipe convection coefficient
1187 4467317 : Real64 EnvHeatTransCoef(0.0); // external convection coefficient (outside pipe)
1188 4467317 : Real64 FluidNodeHeatCapacity(0.0); // local var for MCp for single node of pipe
1189 :
1190 : Real64 TempBelow;
1191 : Real64 TempBeside;
1192 : Real64 TempAbove;
1193 : Real64 Numerator;
1194 : Real64 Denominator;
1195 : Real64 SurfaceTemp;
1196 :
1197 : // traps fluid properties problems such as freezing conditions
1198 4467317 : if (this->FluidSpecHeat <= 0.0 || this->FluidDensity <= 0.0) {
1199 : // leave the state of the pipe as it was
1200 0 : state.dataPipeHT->nsvOutletTemp = this->TentativeFluidTemp(this->NumSections);
1201 : // set heat transfer rates to zero for consistency
1202 0 : state.dataPipeHT->nsvEnvHeatLossRate = 0.0;
1203 0 : state.dataPipeHT->nsvFluidHeatLossRate = 0.0;
1204 0 : return;
1205 : }
1206 :
1207 : // AirConvCoef = OutsidePipeHeatTransCoef(PipeHTNum)
1208 : // Revised by L. Gu by including insulation conductance 6/19/08
1209 :
1210 4467317 : if (this->EnvironmentPtr != EnvrnPtr::GroundEnv) {
1211 558537 : AirConvCoef = 1.0 / (1.0 / this->OutsidePipeHeatTransCoef(state) + this->InsulationResistance);
1212 : }
1213 :
1214 4467317 : FluidConvCoef = this->CalcPipeHeatTransCoef(state, state.dataPipeHT->nsvInletTemp, state.dataPipeHT->nsvMassFlowRate, this->PipeID);
1215 :
1216 : // heat transfer to air or ground
1217 4467317 : switch (this->EnvironmentPtr) {
1218 3908780 : case EnvrnPtr::GroundEnv: {
1219 : // Approximate conductance using ground conductivity, (h=k/L), where L is grid spacing
1220 : // between pipe wall and next closest node.
1221 3908780 : EnvHeatTransCoef = this->SoilConductivity / (this->dSregular - (this->PipeID / 2.0));
1222 3908780 : } break;
1223 186179 : case EnvrnPtr::OutsideAirEnv: {
1224 186179 : EnvHeatTransCoef = AirConvCoef;
1225 186179 : } break;
1226 186179 : case EnvrnPtr::ZoneEnv: {
1227 186179 : EnvHeatTransCoef = AirConvCoef;
1228 186179 : } break;
1229 186179 : case EnvrnPtr::ScheduleEnv: {
1230 186179 : EnvHeatTransCoef = AirConvCoef;
1231 186179 : } break;
1232 0 : case EnvrnPtr::None: {
1233 0 : EnvHeatTransCoef = 0.0;
1234 0 : } break;
1235 0 : default: {
1236 0 : EnvHeatTransCoef = 0.0;
1237 0 : } break;
1238 : }
1239 :
1240 : // work out the coefficients
1241 4467317 : FluidNodeHeatCapacity =
1242 4467317 : this->SectionArea * this->Length / this->NumSections * this->FluidSpecHeat * this->FluidDensity; // Mass of Node x Specific heat
1243 :
1244 : // coef of fluid heat balance
1245 4467317 : A1 = FluidNodeHeatCapacity + state.dataPipeHT->nsvMassFlowRate * this->FluidSpecHeat * state.dataPipeHT->nsvDeltaTime +
1246 4467317 : FluidConvCoef * this->InsideArea * state.dataPipeHT->nsvDeltaTime;
1247 :
1248 4467317 : A2 = state.dataPipeHT->nsvMassFlowRate * this->FluidSpecHeat * state.dataPipeHT->nsvDeltaTime;
1249 :
1250 4467317 : A3 = FluidConvCoef * this->InsideArea * state.dataPipeHT->nsvDeltaTime;
1251 :
1252 4467317 : A4 = FluidNodeHeatCapacity;
1253 :
1254 : // coef of pipe heat balance
1255 4467317 : B1 = this->PipeHeatCapacity + FluidConvCoef * this->InsideArea * state.dataPipeHT->nsvDeltaTime +
1256 4467317 : EnvHeatTransCoef * this->OutsideArea * state.dataPipeHT->nsvDeltaTime;
1257 :
1258 4467317 : B2 = A3;
1259 :
1260 4467317 : B3 = EnvHeatTransCoef * this->OutsideArea * state.dataPipeHT->nsvDeltaTime;
1261 :
1262 4467317 : B4 = this->PipeHeatCapacity;
1263 :
1264 4467317 : this->TentativeFluidTemp(0) = state.dataPipeHT->nsvInletTemp;
1265 :
1266 4467317 : this->TentativePipeTemp(0) = this->PipeTemp(1); // for convenience
1267 :
1268 4467317 : if (present(LengthIndex)) { // Just simulate the single section if being called from Pipe:Underground
1269 :
1270 3908780 : int PipeDepth = this->PipeNodeDepth;
1271 3908780 : int PipeWidth = this->PipeNodeWidth;
1272 3908780 : TempBelow = this->T(PipeWidth, PipeDepth + 1, LengthIndex, TimeIndex::Current);
1273 3908780 : TempBeside = this->T(PipeWidth - 1, PipeDepth, LengthIndex, TimeIndex::Current);
1274 3908780 : TempAbove = this->T(PipeWidth, PipeDepth - 1, LengthIndex, TimeIndex::Current);
1275 3908780 : state.dataPipeHT->nsvEnvironmentTemp = (TempBelow + TempBeside + TempAbove) / 3.0;
1276 :
1277 7817560 : this->TentativeFluidTemp(LengthIndex) = (A2 * this->TentativeFluidTemp(LengthIndex - 1) +
1278 3908780 : A3 / B1 * (B3 * state.dataPipeHT->nsvEnvironmentTemp + B4 * this->PreviousPipeTemp(LengthIndex)) +
1279 3908780 : A4 * this->PreviousFluidTemp(LengthIndex)) /
1280 3908780 : (A1 - A3 * B2 / B1);
1281 :
1282 3908780 : this->TentativePipeTemp(LengthIndex) =
1283 3908780 : (B2 * this->TentativeFluidTemp(LengthIndex) + B3 * state.dataPipeHT->nsvEnvironmentTemp + B4 * this->PreviousPipeTemp(LengthIndex)) / B1;
1284 :
1285 : // Get exterior surface temperature from energy balance at the surface
1286 3908780 : Numerator = state.dataPipeHT->nsvEnvironmentTemp - this->TentativeFluidTemp(LengthIndex);
1287 3908780 : Denominator = EnvHeatTransCoef * ((1 / EnvHeatTransCoef) + this->SumTK);
1288 3908780 : SurfaceTemp = state.dataPipeHT->nsvEnvironmentTemp - Numerator / Denominator;
1289 :
1290 : // keep track of environmental heat loss rate - not same as fluid loss at same time
1291 3908780 : state.dataPipeHT->nsvEnvHeatLossRate += EnvHeatTransCoef * this->OutsideArea * (SurfaceTemp - state.dataPipeHT->nsvEnvironmentTemp);
1292 :
1293 : } else { // Simulate all sections at once if not pipe:underground
1294 :
1295 : // start loop along pipe
1296 : // b1 must not be zero but this should have been checked on input
1297 11729277 : for (int curnode = 1; curnode <= this->NumSections; ++curnode) {
1298 11170740 : this->TentativeFluidTemp(curnode) = (A2 * this->TentativeFluidTemp(curnode - 1) +
1299 11170740 : A3 / B1 * (B3 * state.dataPipeHT->nsvEnvironmentTemp + B4 * this->PreviousPipeTemp(curnode)) +
1300 11170740 : A4 * this->PreviousFluidTemp(curnode)) /
1301 11170740 : (A1 - A3 * B2 / B1);
1302 :
1303 22341480 : this->TentativePipeTemp(curnode) =
1304 11170740 : (B2 * this->TentativeFluidTemp(curnode) + B3 * state.dataPipeHT->nsvEnvironmentTemp + B4 * this->PreviousPipeTemp(curnode)) / B1;
1305 :
1306 : // Get exterior surface temperature from energy balance at the surface
1307 11170740 : Numerator = state.dataPipeHT->nsvEnvironmentTemp - this->TentativeFluidTemp(curnode);
1308 11170740 : Denominator = EnvHeatTransCoef * ((1 / EnvHeatTransCoef) + this->SumTK);
1309 11170740 : SurfaceTemp = state.dataPipeHT->nsvEnvironmentTemp - Numerator / Denominator;
1310 :
1311 : // Keep track of environmental heat loss
1312 11170740 : state.dataPipeHT->nsvEnvHeatLossRate += EnvHeatTransCoef * this->OutsideArea * (SurfaceTemp - state.dataPipeHT->nsvEnvironmentTemp);
1313 : }
1314 : }
1315 :
1316 8934634 : state.dataPipeHT->nsvFluidHeatLossRate =
1317 4467317 : state.dataPipeHT->nsvMassFlowRate * this->FluidSpecHeat * (this->TentativeFluidTemp(0) - this->TentativeFluidTemp(this->NumSections));
1318 :
1319 4467317 : state.dataPipeHT->nsvOutletTemp = this->TentativeFluidTemp(this->NumSections);
1320 : }
1321 :
1322 : //==============================================================================
1323 :
1324 186179 : void PipeHTData::CalcBuriedPipeSoil(EnergyPlusData &state) // Current Simulation Pipe Number
1325 : {
1326 :
1327 : // AUTHOR Edwin Lee
1328 : // DATE WRITTEN May 2008
1329 : // MODIFIED na
1330 : // RE-ENGINEERED na
1331 :
1332 : // PURPOSE OF THIS SUBROUTINE:
1333 : // This subroutine does all of the stuff that is necessary to simulate
1334 : // soil heat transfer with a Buried Pipe.
1335 :
1336 : // METHODOLOGY EMPLOYED:
1337 : // An implicit pseudo 3D finite difference grid
1338 : // is set up, which simulates transient behavior in the soil.
1339 : // This then interfaces with the Hanby model for near-pipe region
1340 :
1341 : // Using/Aliasing
1342 : using Convect::CalcASHRAESimpExtConvCoeff;
1343 :
1344 : // SUBROUTINE PARAMETER DEFINITIONS:
1345 186179 : int constexpr NumSections(20);
1346 186179 : Real64 constexpr ConvCrit(0.05);
1347 186179 : int constexpr MaxIterations(200);
1348 186179 : Real64 constexpr StefBoltzmann(5.6697e-08); // Stefan-Boltzmann constant
1349 :
1350 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1351 186179 : int IterationIndex(0); // Index when stepping through equations
1352 186179 : int DepthIndex(0); // Index for nodes in the depth direction
1353 186179 : int WidthIndex(0); // Index for nodes in the width direction
1354 186179 : Real64 ConvCoef(0.0); // Current convection coefficient = f(Wind Speed,Roughness)
1355 186179 : Real64 RadCoef(0.0); // Current radiation coefficient
1356 186179 : Real64 QSolAbsorbed(0.0); // Current total solar energy absorbed
1357 372358 : Array3D<Real64> T_O(this->PipeNodeWidth, this->NumDepthNodes, NumSections);
1358 :
1359 : // Local variable placeholders for code readability
1360 186179 : Real64 A1(0.0); // Placeholder for CoefA1
1361 186179 : Real64 A2(0.0); // Placeholder for CoefA2
1362 186179 : Real64 NodeBelow(0.0); // Placeholder for Node temp below current node
1363 186179 : Real64 NodeAbove(0.0); // Placeholder for Node temp above current node
1364 186179 : Real64 NodeRight(0.0); // Placeholder for Node temp to the right of current node
1365 186179 : Real64 NodeLeft(0.0); // Placeholder for Node temp to the left of current node
1366 186179 : Real64 NodePast(0.0); // Placeholder for Node temp at current node but previous time step
1367 186179 : Real64 PastNodeTempAbs(0.0); // Placeholder for absolute temperature (K) version of NodePast
1368 186179 : Real64 Ttemp(0.0); // Placeholder for a current temperature node in convergence check
1369 186179 : Real64 SkyTempAbs(0.0); // Placeholder for current sky temperature in Kelvin
1370 186179 : Material::SurfaceRoughness TopRoughness(Material::SurfaceRoughness::Invalid); // Placeholder for soil surface roughness
1371 186179 : Real64 TopThermAbs(0.0); // Placeholder for soil thermal radiation absorptivity
1372 186179 : Real64 TopSolarAbs(0.0); // Placeholder for soil solar radiation absorptivity
1373 186179 : Real64 kSoil(0.0); // Placeholder for soil conductivity
1374 186179 : Real64 dS(0.0); // Placeholder for soil grid spacing
1375 186179 : Real64 rho(0.0); // Placeholder for soil density
1376 186179 : Real64 Cp(0.0); // Placeholder for soil specific heat
1377 :
1378 : // There are a number of coefficients which change through the simulation, and they are updated here
1379 186179 : this->FourierDS = this->SoilDiffusivity * state.dataPipeHT->nsvDeltaTime / pow_2(this->dSregular); // Eq. D4
1380 186179 : this->CoefA1 = this->FourierDS / (1 + 4 * this->FourierDS); // Eq. D2
1381 186179 : this->CoefA2 = 1 / (1 + 4 * this->FourierDS); // Eq. D3
1382 :
1383 195439 : for (IterationIndex = 1; IterationIndex <= MaxIterations; ++IterationIndex) {
1384 195439 : if (IterationIndex == MaxIterations) {
1385 0 : ShowWarningError(state, format("BuriedPipeHeatTransfer: Large number of iterations detected in object: {}", this->Name));
1386 : }
1387 :
1388 : // Store computed values in T_O array
1389 3908780 : for (int LengthIndex = 2; LengthIndex <= this->NumSections; ++LengthIndex) {
1390 29706728 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes - 1; ++DepthIndex) {
1391 103973548 : for (WidthIndex = 2; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
1392 77980161 : T_O(WidthIndex, DepthIndex, LengthIndex) = this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative);
1393 : }
1394 : }
1395 : }
1396 :
1397 : // Loop along entire length of pipe, analyzing cross sects
1398 4104219 : for (int LengthIndex = 1; LengthIndex <= this->NumSections; ++LengthIndex) {
1399 31270240 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes - 1; ++DepthIndex) {
1400 109445840 : for (WidthIndex = 2; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
1401 :
1402 82084380 : if (DepthIndex == 1) { // Soil Surface Boundary
1403 :
1404 : // If on soil boundary, load up local variables and perform calculations
1405 11726340 : NodePast = this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Previous);
1406 11726340 : PastNodeTempAbs = NodePast + Constant::Kelvin;
1407 11726340 : SkyTempAbs = state.dataEnvrn->SkyTemp + Constant::Kelvin;
1408 11726340 : TopRoughness = this->SoilRoughness;
1409 11726340 : TopThermAbs = this->SoilThermAbs;
1410 11726340 : TopSolarAbs = this->SoilSolarAbs;
1411 11726340 : kSoil = this->SoilConductivity;
1412 11726340 : dS = this->dSregular;
1413 11726340 : rho = this->SoilDensity;
1414 11726340 : Cp = this->SoilCp;
1415 :
1416 : // ASHRAE simple convection coefficient model for external surfaces.
1417 11726340 : this->OutdoorConvCoef = CalcASHRAESimpExtConvCoeff(TopRoughness, state.dataEnvrn->WindSpeed);
1418 11726340 : ConvCoef = this->OutdoorConvCoef;
1419 :
1420 : // thermal radiation coefficient using surf temp from past time step
1421 11726340 : if (std::abs(PastNodeTempAbs - SkyTempAbs) > Constant::rTinyValue) {
1422 11726340 : RadCoef = StefBoltzmann * TopThermAbs * (pow_4(PastNodeTempAbs) - pow_4(SkyTempAbs)) / (PastNodeTempAbs - SkyTempAbs);
1423 : } else {
1424 0 : RadCoef = 0.0;
1425 : }
1426 :
1427 : // total absorbed solar - no ground solar
1428 11726340 : QSolAbsorbed =
1429 11726340 : TopSolarAbs * (max(state.dataEnvrn->SOLCOS(3), 0.0) * state.dataEnvrn->BeamSolarRad + state.dataEnvrn->DifSolarRad);
1430 :
1431 : // If sun is not exposed, then turn off both solar and thermal radiation
1432 11726340 : if (!this->SolarExposed) {
1433 0 : RadCoef = 0.0;
1434 0 : QSolAbsorbed = 0.0;
1435 : }
1436 :
1437 11726340 : if (WidthIndex == this->PipeNodeWidth) { // Symmetric centerline boundary
1438 :
1439 : //-Coefficients and Temperatures
1440 3908780 : NodeBelow = this->T(WidthIndex, DepthIndex + 1, LengthIndex, TimeIndex::Current);
1441 3908780 : NodeLeft = this->T(WidthIndex - 1, DepthIndex, LengthIndex, TimeIndex::Current);
1442 :
1443 : //-Update Equation, basically a detailed energy balance at the surface
1444 3908780 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative) =
1445 3908780 : (QSolAbsorbed + RadCoef * state.dataEnvrn->SkyTemp + ConvCoef * state.dataEnvrn->OutDryBulbTemp +
1446 3908780 : (kSoil / dS) * (NodeBelow + 2 * NodeLeft) + (rho * Cp / state.dataPipeHT->nsvDeltaTime) * NodePast) /
1447 3908780 : (RadCoef + ConvCoef + 3 * (kSoil / dS) + (rho * Cp / state.dataPipeHT->nsvDeltaTime));
1448 :
1449 : } else { // Soil surface, but not on centerline
1450 :
1451 : //-Coefficients and Temperatures
1452 7817560 : NodeBelow = this->T(WidthIndex, DepthIndex + 1, LengthIndex, TimeIndex::Current);
1453 7817560 : NodeLeft = this->T(WidthIndex - 1, DepthIndex, LengthIndex, TimeIndex::Current);
1454 7817560 : NodeRight = this->T(WidthIndex + 1, DepthIndex, LengthIndex, TimeIndex::Current);
1455 :
1456 : //-Update Equation
1457 7817560 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative) =
1458 7817560 : (QSolAbsorbed + RadCoef * state.dataEnvrn->SkyTemp + ConvCoef * state.dataEnvrn->OutDryBulbTemp +
1459 7817560 : (kSoil / dS) * (NodeBelow + NodeLeft + NodeRight) + (rho * Cp / state.dataPipeHT->nsvDeltaTime) * NodePast) /
1460 7817560 : (RadCoef + ConvCoef + 3 * (kSoil / dS) + (rho * Cp / state.dataPipeHT->nsvDeltaTime));
1461 :
1462 : } // Soil-to-air surface node structure
1463 :
1464 70358040 : } else if (WidthIndex == this->PipeNodeWidth) { // On Symmetric centerline boundary
1465 :
1466 23452680 : if (DepthIndex == this->PipeNodeDepth) { // On the node containing the pipe
1467 :
1468 : //-Call to simulate a single pipe segment (by passing OPTIONAL LengthIndex argument)
1469 3908780 : this->CalcPipesHeatTransfer(state, LengthIndex);
1470 :
1471 : //-Update node for cartesian system
1472 3908780 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative) = this->PipeTemp(LengthIndex);
1473 :
1474 : } else { // Not surface node
1475 :
1476 : //-Coefficients and Temperatures
1477 19543900 : NodeLeft = this->T(WidthIndex - 1, DepthIndex, LengthIndex, TimeIndex::Current);
1478 19543900 : NodeAbove = this->T(WidthIndex, DepthIndex - 1, LengthIndex, TimeIndex::Current);
1479 19543900 : NodeBelow = this->T(WidthIndex, DepthIndex + 1, LengthIndex, TimeIndex::Current);
1480 19543900 : NodePast = this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Current - 1);
1481 19543900 : A1 = this->CoefA1;
1482 19543900 : A2 = this->CoefA2;
1483 :
1484 : //-Update Equation
1485 19543900 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative) =
1486 19543900 : A1 * (NodeBelow + NodeAbove + 2 * NodeLeft) + A2 * NodePast;
1487 :
1488 : } // Symmetric centerline node structure
1489 :
1490 : } else { // All Normal Interior Nodes
1491 :
1492 : //-Coefficients and Temperatures
1493 46905360 : A1 = this->CoefA1;
1494 46905360 : A2 = this->CoefA2;
1495 46905360 : NodeBelow = this->T(WidthIndex, DepthIndex + 1, LengthIndex, TimeIndex::Current);
1496 46905360 : NodeAbove = this->T(WidthIndex, DepthIndex - 1, LengthIndex, TimeIndex::Current);
1497 46905360 : NodeRight = this->T(WidthIndex + 1, DepthIndex, LengthIndex, TimeIndex::Current);
1498 46905360 : NodeLeft = this->T(WidthIndex - 1, DepthIndex, LengthIndex, TimeIndex::Current);
1499 46905360 : NodePast = this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Current - 1);
1500 :
1501 : //-Update Equation
1502 46905360 : this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative) =
1503 46905360 : A1 * (NodeBelow + NodeAbove + NodeRight + NodeLeft) + A2 * NodePast; // Eq. D1
1504 : }
1505 : }
1506 : }
1507 : }
1508 :
1509 : // Check for convergence
1510 3739744 : for (int LengthIndex = 2; LengthIndex <= this->NumSections; ++LengthIndex) {
1511 28372256 : for (DepthIndex = 1; DepthIndex <= this->NumDepthNodes - 1; ++DepthIndex) {
1512 99289775 : for (WidthIndex = 2; WidthIndex <= this->PipeNodeWidth; ++WidthIndex) {
1513 74471084 : Ttemp = this->T(WidthIndex, DepthIndex, LengthIndex, TimeIndex::Tentative);
1514 74471084 : if (std::abs(T_O(WidthIndex, DepthIndex, LengthIndex) - Ttemp) > ConvCrit) goto IterationLoop_loop;
1515 : }
1516 : }
1517 : }
1518 :
1519 : // If we didn't cycle back, then the system is converged
1520 : // PipeHT(PipeHTNum)%PipeUGIters=IterationIndex
1521 186179 : goto IterationLoop_exit;
1522 :
1523 9260 : IterationLoop_loop:;
1524 : }
1525 0 : IterationLoop_exit:;
1526 186179 : }
1527 :
1528 : //==============================================================================
1529 :
1530 58100 : void PipeHTData::UpdatePipesHeatTransfer(EnergyPlusData &state)
1531 : {
1532 :
1533 : // SUBROUTINE INFORMATION:
1534 : // AUTHOR Simon Rees
1535 : // DATE WRITTEN July 2007
1536 : // MODIFIED na
1537 : // RE-ENGINEERED na
1538 :
1539 : // PURPOSE OF THIS SUBROUTINE:
1540 : // This subroutine does any updating that needs to be done for
1541 : // Pipe Heat Transfers. This routine must also set the outlet water conditions.
1542 :
1543 : // METHODOLOGY EMPLOYED:
1544 :
1545 : // REFERENCES:
1546 : // na
1547 :
1548 : // SUBROUTINE ARGUMENT DEFINITIONS:
1549 : // INTEGER, INTENT(IN) :: PipeHTNum ! Index for the surface
1550 :
1551 : // SUBROUTINE PARAMETER DEFINITIONS:
1552 :
1553 : // INTERFACE BLOCK SPECIFICATIONS
1554 : // na
1555 :
1556 : // DERIVED TYPE DEFINITIONS
1557 : // na
1558 :
1559 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1560 :
1561 : // only outlet node temp should need updating
1562 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).Temp = state.dataPipeHT->nsvOutletTemp;
1563 :
1564 : // pass everything else through
1565 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).TempMin = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).TempMin;
1566 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).TempMax = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).TempMax;
1567 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).MassFlowRate =
1568 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).MassFlowRate;
1569 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).MassFlowRateMin =
1570 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).MassFlowRateMin;
1571 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).MassFlowRateMax =
1572 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).MassFlowRateMax;
1573 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).MassFlowRateMinAvail =
1574 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).MassFlowRateMinAvail;
1575 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).MassFlowRateMaxAvail =
1576 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).MassFlowRateMaxAvail;
1577 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).Quality = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).Quality;
1578 : // Only pass pressure if we aren't doing a pressure simulation
1579 58100 : switch (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).PressureSimType) {
1580 58100 : case DataPlant::PressSimType::NoPressure:
1581 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).Press = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).Press;
1582 58100 : break;
1583 0 : default:
1584 : // Don't do anything
1585 0 : break;
1586 : }
1587 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).Enthalpy = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).Enthalpy;
1588 58100 : state.dataLoopNodes->Node(state.dataPipeHT->nsvOutletNodeNum).HumRat = state.dataLoopNodes->Node(state.dataPipeHT->nsvInletNodeNum).HumRat;
1589 58100 : }
1590 :
1591 : //==============================================================================
1592 :
1593 58100 : void PipeHTData::ReportPipesHeatTransfer(EnergyPlusData &state)
1594 : {
1595 :
1596 : // SUBROUTINE INFORMATION:
1597 : // AUTHOR Simon Rees
1598 : // DATE WRITTEN July 2007
1599 : // MODIFIED na
1600 : // RE-ENGINEERED na
1601 :
1602 : // PURPOSE OF THIS SUBROUTINE:
1603 : // This subroutine simply updates the report data
1604 :
1605 : // METHODOLOGY EMPLOYED:
1606 : // Standard EnergyPlus methodology.
1607 :
1608 : // REFERENCES:
1609 : // na
1610 :
1611 : // USE STATEMENTS:
1612 :
1613 : // Locals
1614 : // SUBROUTINE ARGUMENT DEFINITIONS:
1615 :
1616 : // SUBROUTINE PARAMETER DEFINITIONS:
1617 : // na
1618 :
1619 : // INTERFACE BLOCK SPECIFICATIONS
1620 : // na
1621 :
1622 : // DERIVED TYPE DEFINITIONS
1623 : // na
1624 :
1625 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1626 :
1627 : // update flows and temps from module variables
1628 58100 : this->FluidInletTemp = state.dataPipeHT->nsvInletTemp;
1629 58100 : this->FluidOutletTemp = state.dataPipeHT->nsvOutletTemp;
1630 58100 : this->MassFlowRate = state.dataPipeHT->nsvMassFlowRate;
1631 58100 : this->VolumeFlowRate = state.dataPipeHT->nsvVolumeFlowRate;
1632 :
1633 : // update other variables from module variables
1634 58100 : this->FluidHeatLossRate = state.dataPipeHT->nsvFluidHeatLossRate;
1635 58100 : this->FluidHeatLossEnergy = state.dataPipeHT->nsvFluidHeatLossRate * state.dataPipeHT->nsvDeltaTime; // DeltaTime is in seconds
1636 58100 : this->PipeInletTemp = this->PipeTemp(1);
1637 58100 : this->PipeOutletTemp = this->PipeTemp(this->NumSections);
1638 :
1639 : // need to average the heat rate because it is now summing over multiple inner time steps
1640 58100 : this->EnvironmentHeatLossRate = state.dataPipeHT->nsvEnvHeatLossRate / state.dataPipeHT->nsvNumInnerTimeSteps;
1641 58100 : this->EnvHeatLossEnergy = this->EnvironmentHeatLossRate * state.dataPipeHT->nsvDeltaTime;
1642 :
1643 : // for zone heat gains, we assign the averaged heat rate over all inner time steps
1644 58100 : if (this->EnvironmentPtr == EnvrnPtr::ZoneEnv) {
1645 14525 : this->ZoneHeatGainRate = this->EnvironmentHeatLossRate;
1646 : }
1647 58100 : }
1648 :
1649 : //==============================================================================
1650 :
1651 2804678 : void PipeHTData::CalcZonePipesHeatGain(EnergyPlusData &state)
1652 : {
1653 :
1654 : // SUBROUTINE INFORMATION:
1655 : // AUTHOR Edwin Lee
1656 : // DATE WRITTEN September 2008
1657 : // MODIFIED
1658 : // RE-ENGINEERED na
1659 :
1660 : // PURPOSE OF THIS SUBROUTINE:
1661 : // Calculates the zone internal gains due to pipe heat transfer objects.
1662 :
1663 : // METHODOLOGY EMPLOYED:
1664 : // Sums the heat losses from all of the water heaters in the zone to add as a gain to the zone.
1665 :
1666 : // Using/Aliasing
1667 2804678 : if (state.dataPipeHT->nsvNumOfPipeHT == 0) return;
1668 :
1669 10808 : if (state.dataGlobal->BeginEnvrnFlag && state.dataPipeHT->MyEnvrnFlag) {
1670 64 : for (auto &e : state.dataPipeHT->PipeHT)
1671 32 : e.ZoneHeatGainRate = 0.0;
1672 32 : state.dataPipeHT->MyEnvrnFlag = false;
1673 : }
1674 :
1675 10808 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataPipeHT->MyEnvrnFlag = true;
1676 : }
1677 :
1678 : //==============================================================================
1679 :
1680 4467317 : Real64 PipeHTData::CalcPipeHeatTransCoef(EnergyPlusData &state,
1681 : Real64 const Temperature, // Temperature of water entering the surface, in C
1682 : Real64 const MassFlowRate, // Mass flow rate, in kg/s
1683 : Real64 const Diameter // Pipe diameter, m
1684 : )
1685 : {
1686 :
1687 : // FUNCTION INFORMATION:
1688 : // AUTHOR Simon Rees
1689 : // DATE WRITTEN July 2007
1690 : // MODIFIED na
1691 : // RE-ENGINEERED na
1692 :
1693 : // PURPOSE OF THIS SUBROUTINE:
1694 : // This subroutine calculates pipe/fluid heat transfer coefficients.
1695 : // This routine is adapted from that in the low temp radiant surface model.
1696 :
1697 : // METHODOLOGY EMPLOYED:
1698 : // Currently assumes water data when calculating Pr and Re
1699 :
1700 : // REFERENCES:
1701 : // See RadiantSystemLowTemp module.
1702 : // Property data for water shown below as parameters taken from
1703 : // Incropera and DeWitt, Introduction to Heat Transfer, Table A.6.
1704 : // Heat exchanger information also from Incropera and DeWitt.
1705 : // Code based loosely on code from IBLAST program (research version)
1706 :
1707 : // Using/Aliasing
1708 : using FluidProperties::GetConductivityGlycol;
1709 : using FluidProperties::GetViscosityGlycol;
1710 :
1711 : // Return value
1712 : Real64 CalcPipeHeatTransCoef;
1713 :
1714 : // Locals
1715 : // SUBROUTINE ARGUMENT DEFINITIONS:
1716 :
1717 : // SUBROUTINE PARAMETER DEFINITIONS:
1718 : static constexpr std::string_view RoutineName("PipeHeatTransfer::CalcPipeHeatTransCoef: ");
1719 4467317 : Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
1720 4467317 : int constexpr NumOfPropDivisions(13); // intervals in property correlation
1721 : static constexpr std::array<Real64, NumOfPropDivisions> Temps = {
1722 : 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
1723 : static constexpr std::array<Real64, NumOfPropDivisions> Pr = {
1724 : 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)
1725 :
1726 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1727 : Real64 InterpFrac;
1728 : Real64 NuD;
1729 : Real64 ReD;
1730 : Real64 Kactual;
1731 : Real64 MUactual;
1732 : Real64 PRactual;
1733 : int LoopNum;
1734 :
1735 : // retrieve loop index for this component so we can look up fluid properties
1736 4467317 : LoopNum = this->plantLoc.loopNum;
1737 :
1738 : // since the fluid properties routine doesn't have Prandtl, we'll just use water values
1739 4467317 : int idx = 0;
1740 20129911 : while (idx < NumOfPropDivisions) {
1741 20129911 : if (Temperature < Temps[idx]) {
1742 4467317 : break;
1743 : }
1744 15662594 : ++idx;
1745 : }
1746 :
1747 4467317 : if (idx == 0) {
1748 0 : PRactual = Pr[idx];
1749 4467317 : } else if (idx >= NumOfPropDivisions) {
1750 0 : PRactual = Pr[NumOfPropDivisions - 1];
1751 : } else {
1752 4467317 : InterpFrac = (Temperature - Temps[idx - 1]) / (Temps[idx] - Temps[idx - 1]);
1753 4467317 : PRactual = Pr[idx - 1] + InterpFrac * (Pr[idx] - Pr[idx - 1]);
1754 : }
1755 :
1756 : // look up conductivity and viscosity
1757 4467317 : Kactual = GetConductivityGlycol(
1758 4467317 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, this->FluidTemp(0), state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName); // W/m-K
1759 4467317 : MUactual =
1760 4467317 : GetViscosityGlycol(
1761 4467317 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, this->FluidTemp(0), state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName) /
1762 : 1000.0; // Note fluid properties routine returns mPa-s, we need Pa-s
1763 :
1764 : // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter) - as RadiantSysLowTemp
1765 4467317 : ReD = 4.0 * MassFlowRate / (Constant::Pi * MUactual * Diameter);
1766 :
1767 4467317 : if (ReD == 0.0) { // No flow
1768 :
1769 : // For now just leave it how it was doing it before
1770 1938541 : NuD = 3.66;
1771 : // Although later it would be nice to have a natural convection correlation
1772 :
1773 : } else { // Calculate the Nusselt number based on what flow regime one is in
1774 :
1775 2528776 : if (ReD >= MaxLaminarRe) { // Turbulent flow --> use Colburn equation
1776 2528776 : NuD = 0.023 * std::pow(ReD, 0.8) * std::pow(PRactual, 1.0 / 3.0);
1777 : } else { // Laminar flow --> use constant surface temperature relation
1778 0 : NuD = 3.66;
1779 : }
1780 : }
1781 :
1782 4467317 : CalcPipeHeatTransCoef = Kactual * NuD / Diameter;
1783 :
1784 4467317 : return CalcPipeHeatTransCoef;
1785 : }
1786 :
1787 : //==============================================================================
1788 :
1789 558537 : Real64 PipeHTData::OutsidePipeHeatTransCoef(EnergyPlusData &state)
1790 : {
1791 :
1792 : // FUNCTION INFORMATION:
1793 : // AUTHOR Dan Fisher
1794 : // DATE WRITTEN July 2007
1795 : // MODIFIED na
1796 : // RE-ENGINEERED na
1797 :
1798 : // PURPOSE OF THIS SUBROUTINE:
1799 : // This subroutine calculates the convection heat transfer
1800 : // coefficient for a cylinder in cross flow.
1801 :
1802 : // REFERENCES:
1803 : // Fundamentals of Heat and Mass Transfer: Incropera and DeWitt, 4th ed.
1804 : // p. 369-370 (Eq. 7:55b)
1805 :
1806 : // Using/Aliasing
1807 : using ScheduleManager::GetCurrentScheduleValue;
1808 :
1809 : // Return value
1810 : Real64 OutsidePipeHeatTransCoef;
1811 :
1812 : // SUBROUTINE PARAMETER DEFINITIONS:
1813 558537 : Real64 constexpr Pr(0.7); // Prandl number for air (assume constant)
1814 558537 : Real64 constexpr CondAir(0.025); // thermal conductivity of air (assume constant) [W/m.K]
1815 558537 : Real64 constexpr RoomAirVel(0.381); // room air velocity of 75 ft./min [m/s]
1816 558537 : Real64 constexpr NaturalConvNusselt(0.36);
1817 : // Nusselt for natural convection for horizontal cylinder
1818 : // from: Correlations for Convective Heat Transfer
1819 : // Dr. Bernhard Spang
1820 : // Chemical Engineers' Resource Page: http://www.cheresources.com/convection.pdf
1821 558537 : int constexpr NumOfParamDivisions(5); // intervals in property correlation
1822 558537 : int constexpr NumOfPropDivisions(12); // intervals in property correlation
1823 :
1824 : static constexpr std::array<Real64, NumOfParamDivisions> CCoef = {0.989, 0.911, 0.683, 0.193, 0.027}; // correlation coefficient
1825 : static constexpr std::array<Real64, NumOfParamDivisions> mExp = {0.33, 0.385, 0.466, 0.618, 0.805}; // exponent
1826 : static constexpr std::array<Real64, NumOfParamDivisions> UpperBound = {4.0, 40.0, 4000.0, 40000.0, 400000.0}; // upper bound of correlation range
1827 : static constexpr std::array<Real64, NumOfPropDivisions> Temperature = {
1828 : -73.0, -23.0, -10.0, 0.0, 10.0, 20.0, 27.0, 30.0, 40.0, 50.0, 76.85, 126.85}; // temperature [C]
1829 : static constexpr std::array<Real64, NumOfPropDivisions> DynVisc = {
1830 : 75.52e-7, 11.37e-6, 12.44e-6, 13.3e-6, 14.18e-6, 15.08e-6, 15.75e-6, 16e-6, 16.95e-6, 17.91e-6, 20.92e-6, 26.41e-6}; // dynamic
1831 : // viscosity
1832 : // [m^2/s]
1833 :
1834 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1835 : int idx;
1836 : Real64 NuD;
1837 : Real64 ReD;
1838 : Real64 Coef;
1839 : Real64 rExp;
1840 : Real64 AirVisc;
1841 : Real64 AirVel;
1842 : Real64 AirTemp;
1843 : Real64 PipeOD;
1844 : bool ViscositySet;
1845 : bool CoefSet;
1846 :
1847 : // Set environmental variables
1848 558537 : switch (this->Type) {
1849 372358 : case DataPlant::PlantEquipmentType::PipeInterior: {
1850 372358 : switch (this->EnvironmentPtr) {
1851 186179 : case EnvrnPtr::ScheduleEnv: {
1852 186179 : AirTemp = GetCurrentScheduleValue(state, this->EnvrSchedPtr);
1853 186179 : AirVel = GetCurrentScheduleValue(state, this->EnvrVelSchedPtr);
1854 186179 : } break;
1855 186179 : case EnvrnPtr::ZoneEnv: {
1856 186179 : AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->EnvrZonePtr).MAT;
1857 186179 : AirVel = RoomAirVel;
1858 186179 : } break;
1859 0 : default:
1860 0 : break;
1861 : }
1862 372358 : } break;
1863 186179 : case DataPlant::PlantEquipmentType::PipeExterior: {
1864 186179 : switch (this->EnvironmentPtr) {
1865 186179 : case EnvrnPtr::OutsideAirEnv: {
1866 186179 : AirTemp = state.dataLoopNodes->Node(this->EnvrAirNodeNum).Temp;
1867 186179 : AirVel = state.dataEnvrn->WindSpeed;
1868 186179 : } break;
1869 0 : default:
1870 0 : break;
1871 : }
1872 186179 : } break;
1873 0 : default:
1874 0 : break;
1875 : }
1876 :
1877 558537 : PipeOD = this->InsulationOD;
1878 :
1879 558537 : ViscositySet = false;
1880 3702653 : for (idx = 0; idx < NumOfPropDivisions; ++idx) {
1881 3702653 : if (AirTemp <= Temperature[idx]) {
1882 558537 : AirVisc = DynVisc[idx];
1883 558537 : ViscositySet = true;
1884 558537 : break;
1885 : }
1886 : }
1887 :
1888 558537 : if (!ViscositySet) {
1889 0 : AirVisc = DynVisc[NumOfPropDivisions - 1];
1890 0 : if (AirTemp > Temperature[NumOfPropDivisions - 1]) {
1891 0 : ShowWarningError(state,
1892 0 : format("Heat Transfer Pipe = {}Viscosity out of range, air temperature too high, setting to upper limit.", this->Name));
1893 : }
1894 : }
1895 :
1896 : // Calculate the Reynold's number
1897 558537 : CoefSet = false;
1898 558537 : if (AirVisc > 0.0) {
1899 558537 : ReD = AirVel * PipeOD / (AirVisc);
1900 : }
1901 :
1902 2212153 : for (idx = 0; idx < NumOfParamDivisions; ++idx) {
1903 2212153 : if (ReD <= UpperBound[idx]) {
1904 558537 : Coef = CCoef[idx];
1905 558537 : rExp = mExp[idx];
1906 558537 : CoefSet = true;
1907 558537 : break;
1908 : }
1909 : }
1910 :
1911 558537 : if (!CoefSet) {
1912 0 : Coef = CCoef[NumOfParamDivisions - 1];
1913 0 : rExp = mExp[NumOfParamDivisions - 1];
1914 0 : if (ReD > UpperBound[NumOfParamDivisions - 1]) {
1915 0 : ShowWarningError(state, format("Heat Transfer Pipe = {}Reynolds Number out of range, setting coefficients to upper limit.", this->Name));
1916 : }
1917 : }
1918 :
1919 : // Calculate the Nusselt number
1920 558537 : NuD = Coef * std::pow(ReD, rExp) * std::pow(Pr, 1.0 / 3.0);
1921 :
1922 : // If the wind speed is too small, we need to use natural convection behavior:
1923 558537 : NuD = max(NuD, NaturalConvNusselt);
1924 :
1925 : // h = (k)(Nu)/D
1926 558537 : OutsidePipeHeatTransCoef = CondAir * NuD / PipeOD;
1927 :
1928 558537 : return OutsidePipeHeatTransCoef;
1929 : }
1930 :
1931 : //==============================================================================
1932 :
1933 1316400 : Real64 PipeHTData::TBND(EnergyPlusData &state,
1934 : Real64 const z // Current Depth
1935 : )
1936 : {
1937 :
1938 : // AUTHOR Edwin Lee
1939 : // DATE WRITTEN December 2007
1940 : // MODIFIED na
1941 : // RE-ENGINEERED na
1942 :
1943 : // PURPOSE OF THIS FUNCTION:
1944 : // Returns a temperature to be used on the boundary of the buried pipe model domain
1945 :
1946 : // METHODOLOGY EMPLOYED:
1947 :
1948 : // REFERENCES: See Module Level Description
1949 :
1950 : // Using/Aliasing
1951 1316400 : Real64 curSimTime = state.dataGlobal->DayOfSim * Constant::SecsInDay;
1952 : Real64 TBND;
1953 :
1954 1316400 : TBND = this->groundTempModel->getGroundTempAtTimeInSeconds(state, z, curSimTime);
1955 :
1956 1316400 : return TBND;
1957 : }
1958 :
1959 0 : void PipeHTData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
1960 : {
1961 0 : }
1962 :
1963 : //===============================================================================
1964 :
1965 : //===============================================================================
1966 :
1967 : } // namespace EnergyPlus::PipeHeatTransfer
|