Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cassert>
50 : #include <cmath>
51 : #include <memory>
52 : #include <set>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array.functions.hh>
56 : #include <ObjexxFCL/Fmath.hh>
57 : #include <ObjexxFCL/floops.hh>
58 : #include <ObjexxFCL/string.functions.hh>
59 :
60 : // EnergyPlus Headers
61 : #include <EnergyPlus/BranchNodeConnections.hh>
62 : #include <EnergyPlus/Data/EnergyPlusData.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataHVACGlobals.hh>
65 : #include <EnergyPlus/DataHeatBalSurface.hh>
66 : #include <EnergyPlus/DataHeatBalance.hh>
67 : #include <EnergyPlus/DataIPShortCuts.hh>
68 : #include <EnergyPlus/DataLoopNode.hh>
69 : #include <EnergyPlus/DataSurfaces.hh>
70 : #include <EnergyPlus/FluidProperties.hh>
71 : #include <EnergyPlus/General.hh>
72 : #include <EnergyPlus/GlobalNames.hh>
73 : #include <EnergyPlus/GroundTemperatureModeling/GroundTemperatureModelManager.hh>
74 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
75 : #include <EnergyPlus/Material.hh>
76 : #include <EnergyPlus/NodeInputManager.hh>
77 : #include <EnergyPlus/OutputProcessor.hh>
78 : #include <EnergyPlus/Plant/DataPlant.hh>
79 : #include <EnergyPlus/PlantPipingSystemsManager.hh>
80 : #include <EnergyPlus/PlantUtilities.hh>
81 : #include <EnergyPlus/UtilityRoutines.hh>
82 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
83 :
84 : namespace EnergyPlus {
85 :
86 : namespace PlantPipingSystemsManager {
87 :
88 : // Module containing the routines dealing with the PipingSystems
89 :
90 : // MODULE INFORMATION:
91 : // AUTHOR Edwin Lee
92 : // DATE WRITTEN Summer 2011
93 : // MODIFIED na
94 : // RE-ENGINEERED na
95 :
96 : // PURPOSE OF THIS MODULE:
97 : // Simulate all cases of plant "piping systems"
98 : // PipingSystem:Underground
99 : // PipingSystem:Generalized
100 :
101 : // METHODOLOGY EMPLOYED:
102 : // A 3D mesh is established, with full 3D conduction being employed
103 : // For ground simulation, moisture content and soil freezing is included
104 : // The mesh can include any number of pipe circuits placed within the domain
105 : // The mesh can interact with basement walls also
106 :
107 : constexpr std::array<std::string_view, (int)SlabPosition::Num> slabPositionNamesUC = {"INGRADE", "ONGRADE"};
108 :
109 : constexpr std::array<std::string_view, (int)HorizInsulation::Num> horizInsulationNamesUC = {"NONE", "PERIMETER", "FULL"};
110 :
111 : #pragma clang diagnostic push
112 : #pragma ide diagnostic ignored "cert-err58-cpp"
113 : // MODULE PARAMETER DEFINITIONS:
114 : std::string const ObjName_ug_GeneralDomain("PipingSystem:Underground:Domain");
115 : std::string const ObjName_Circuit("PipingSystem:Underground:PipeCircuit");
116 : std::string const ObjName_Segment("PipingSystem:Underground:PipeSegment");
117 : std::string const ObjName_HorizTrench("GroundHeatExchanger:HorizontalTrench");
118 : std::string const ObjName_ZoneCoupled_Slab("Site:GroundDomain:Slab");
119 : std::string const ObjName_ZoneCoupled_Basement("Site:GroundDomain:Basement");
120 : constexpr std::array<std::string_view, static_cast<int>(SegmentFlow::Num)> flowDirectionNamesUC = {"INCREASINGZ", "DECREASINGZ"};
121 :
122 : #pragma clang diagnostic pop
123 :
124 75 : void CheckIfAnySlabs(EnergyPlusData &state)
125 : {
126 : // SUBROUTINE INFORMATION:
127 : // AUTHOR Matt Mitchell
128 : // DATE WRITTEN May 2014
129 : // MODIFIED na
130 : // RE-ENGINEERED na
131 75 : int numSlabsCheck(state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_ZoneCoupled_Slab));
132 75 : state.dataGlobal->AnySlabsInModel = (numSlabsCheck > 0);
133 75 : }
134 :
135 75 : void CheckIfAnyBasements(EnergyPlusData &state)
136 : {
137 : // SUBROUTINE INFORMATION:
138 : // AUTHOR Matt Mitchell
139 : // DATE WRITTEN May 2014
140 : // MODIFIED na
141 : // RE-ENGINEERED na
142 75 : int const numBasementsCheck(state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_ZoneCoupled_Basement));
143 75 : state.dataGlobal->AnyBasementsInModel = (numBasementsCheck > 0);
144 75 : }
145 :
146 1 : PlantComponent *Circuit::factory(EnergyPlusData &state, [[maybe_unused]] DataPlant::PlantEquipmentType objectType, const std::string &objectName)
147 : {
148 : // Process the input data for circuits if it hasn't been done already
149 1 : if (state.dataPlantPipingSysMgr->GetInputFlag) {
150 1 : GetPipingSystemsAndGroundDomainsInput(state);
151 1 : state.dataPlantPipingSysMgr->GetInputFlag = false;
152 : }
153 : // Now look for this particular pipe in the list
154 1 : for (auto &circuit : state.dataPlantPipingSysMgr->circuits) {
155 1 : if (circuit.Name == objectName) {
156 1 : return &circuit;
157 : }
158 : }
159 : // If we didn't find it, fatal
160 : ShowFatalError(state, format("PipeCircuitInfoFactory: Error getting inputs for circuit named: {}", objectName)); // LCOV_EXCL_LINE
161 : // Shut up the compiler
162 : return nullptr; // LCOV_EXCL_LINE
163 : }
164 :
165 1 : void Circuit::simulate(EnergyPlusData &state,
166 : [[maybe_unused]] const PlantLocation &calledFromLocation,
167 : [[maybe_unused]] bool const FirstHVACIteration,
168 : [[maybe_unused]] Real64 &CurLoad,
169 : [[maybe_unused]] bool const RunFlag)
170 : {
171 : // Retrieve the parent domain index for this pipe circuit
172 1 : auto &thisDomain = state.dataPlantPipingSysMgr->domains[this->ParentDomainIndex];
173 :
174 : // Do any initialization here
175 1 : thisDomain.InitPipingSystems(state, this);
176 :
177 : // Update the temperature field
178 1 : thisDomain.PerformIterationLoop(state, this);
179 :
180 : // Update outlet nodes, etc.
181 1 : thisDomain.UpdatePipingSystems(state, this);
182 1 : }
183 :
184 1 : void SimulateGroundDomains(EnergyPlusData &state, bool initOnly)
185 : {
186 :
187 : // SUBROUTINE INFORMATION:
188 : // AUTHOR Matt Mitchell
189 : // DATE WRITTEN Spring 2014
190 : // MODIFIED by Sushobhit Acharya, March 2015
191 : // RE-ENGINEERED na
192 :
193 : // Read input if necessary
194 1 : if (state.dataPlantPipingSysMgr->GetInputFlag) {
195 0 : GetPipingSystemsAndGroundDomainsInput(state);
196 0 : state.dataPlantPipingSysMgr->GetInputFlag = false;
197 : }
198 :
199 3 : for (auto &thisDomain : state.dataPlantPipingSysMgr->domains) {
200 :
201 : // if the domain contains a pipe circuit, it shouldn't be initialized here, it has its own entry point
202 2 : if (thisDomain.HasAPipeCircuit) continue;
203 :
204 1 : if (thisDomain.DomainNeedsToBeMeshed) {
205 1 : thisDomain.developMesh(state);
206 : }
207 :
208 1 : thisDomain.DomainNeedsToBeMeshed = false;
209 :
210 : // The time init should be done here before we DoOneTimeInits because the DoOneTimeInits
211 : // includes a ground temperature initialization, which is based on the Cur%CurSimTimeSeconds variable
212 : // which would be carried over from the previous environment
213 1 : thisDomain.Cur.CurSimTimeStepSize = state.dataGlobal->TimeStepZone * Constant::rSecsInHour;
214 1 : thisDomain.Cur.CurSimTimeSeconds =
215 1 : ((state.dataGlobal->DayOfSim - 1) * Constant::iHoursInDay + (state.dataGlobal->HourOfDay - 1) +
216 1 : (state.dataGlobal->TimeStep - 1) * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed) *
217 : Constant::rSecsInHour;
218 :
219 : // There are also some inits that are "close to one time" inits...( one-time in standalone, each envrn in E+ )
220 1 : if ((state.dataGlobal->BeginSimFlag && thisDomain.BeginSimInit) || (state.dataGlobal->BeginEnvrnFlag && thisDomain.BeginSimEnvironment)) {
221 :
222 1 : thisDomain.DoOneTimeInitializations(state, nullptr);
223 :
224 1 : if (thisDomain.HasZoneCoupledSlab) {
225 0 : int Xmax = ubound(thisDomain.Cells, 1);
226 : // int yMax = ubound( thisDomain.Cells, 2 );
227 0 : int Zmax = ubound(thisDomain.Cells, 3);
228 :
229 0 : thisDomain.WeightingFactor.allocate({0, Xmax}, {0, Zmax});
230 0 : thisDomain.WeightedHeatFlux.allocate({0, Xmax}, {0, Zmax});
231 : }
232 :
233 1 : thisDomain.BeginSimInit = false;
234 1 : thisDomain.BeginSimEnvironment = false;
235 : }
236 1 : if (!state.dataGlobal->BeginSimFlag) thisDomain.BeginSimInit = true;
237 1 : if (!state.dataGlobal->BeginEnvrnFlag) thisDomain.BeginSimEnvironment = true;
238 :
239 : // Reset the heat fluxes if domain update has been completed
240 1 : if (thisDomain.ResetHeatFluxFlag) {
241 1 : thisDomain.AggregateHeatFlux = 0;
242 1 : thisDomain.AggregateWallHeatFlux = 0;
243 1 : thisDomain.AggregateFloorHeatFlux = 0;
244 1 : thisDomain.NumHeatFlux = 0;
245 1 : thisDomain.ResetHeatFluxFlag = false;
246 : }
247 :
248 1 : if (!initOnly) {
249 : // Aggregate the heat flux
250 : // Zone-coupled slab
251 1 : if (thisDomain.HasZoneCoupledSlab) {
252 0 : thisDomain.AggregateHeatFlux += thisDomain.GetZoneInterfaceHeatFlux(state);
253 0 : thisDomain.NumHeatFlux += 1;
254 0 : thisDomain.HeatFlux = thisDomain.AggregateHeatFlux / thisDomain.NumHeatFlux;
255 : } else { // Coupled basement
256 :
257 : // basement walls
258 1 : thisDomain.AggregateWallHeatFlux += thisDomain.GetBasementWallHeatFlux(state);
259 : // basement floor
260 1 : thisDomain.AggregateFloorHeatFlux += thisDomain.GetBasementFloorHeatFlux(state);
261 :
262 1 : thisDomain.NumHeatFlux += 1;
263 1 : thisDomain.WallHeatFlux = thisDomain.AggregateWallHeatFlux / thisDomain.NumHeatFlux;
264 1 : thisDomain.FloorHeatFlux = thisDomain.AggregateFloorHeatFlux / thisDomain.NumHeatFlux;
265 : }
266 :
267 : // Aggregate the heat flux
268 : // Zone-coupled slab
269 1 : if (thisDomain.HasZoneCoupledSlab) {
270 0 : thisDomain.AggregateHeatFlux += thisDomain.GetZoneInterfaceHeatFlux(state);
271 0 : thisDomain.NumHeatFlux += 1;
272 0 : thisDomain.HeatFlux = thisDomain.AggregateHeatFlux / thisDomain.NumHeatFlux;
273 1 : } else if (thisDomain.HasZoneCoupledBasement) { // Coupled basement
274 : // basement walls
275 1 : thisDomain.AggregateWallHeatFlux += thisDomain.GetBasementWallHeatFlux(state);
276 : // basement floor
277 1 : thisDomain.AggregateFloorHeatFlux += thisDomain.GetBasementFloorHeatFlux(state);
278 :
279 1 : thisDomain.NumHeatFlux += 1;
280 1 : thisDomain.WallHeatFlux = thisDomain.AggregateWallHeatFlux / thisDomain.NumHeatFlux;
281 1 : thisDomain.FloorHeatFlux = thisDomain.AggregateFloorHeatFlux / thisDomain.NumHeatFlux;
282 : }
283 :
284 : // Zone-coupled slab
285 1 : if (thisDomain.HasZoneCoupledSlab) {
286 :
287 0 : thisDomain.HeatFlux = thisDomain.AggregateHeatFlux / thisDomain.NumHeatFlux;
288 :
289 0 : Real64 ZoneTemp = 0.0;
290 :
291 : // Set ZoneTemp equal to the average air temperature of the zones the coupled surfaces are part of.
292 0 : for (auto const &z : thisDomain.ZoneCoupledSurfaces) {
293 0 : int ZoneNum = z.Zone;
294 0 : ZoneTemp += state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).ZTAV;
295 : }
296 :
297 0 : ZoneTemp = ZoneTemp / thisDomain.ZoneCoupledSurfaces.size();
298 0 : Real64 AvgSlabTemp = thisDomain.GetAverageTempByType(state, CellType::ZoneGroundInterface);
299 :
300 0 : int yMax = ubound(thisDomain.Cells, 2);
301 :
302 0 : for (int Z = lbound(thisDomain.Cells, 3); Z <= ubound(thisDomain.Cells, 3); ++Z) {
303 0 : for (int X = lbound(thisDomain.Cells, 1); X <= ubound(thisDomain.Cells, 1); ++X) {
304 : // Zone interface cells
305 0 : if (thisDomain.Cells(X, yMax, Z).cellType == CellType::ZoneGroundInterface) {
306 0 : thisDomain.WeightingFactor(X, Z) =
307 0 : std::abs((ZoneTemp - thisDomain.Cells(X, yMax, Z).Temperature_PrevTimeStep) / (ZoneTemp - AvgSlabTemp));
308 : }
309 : }
310 : }
311 :
312 : // Set initial weighted heat flux
313 0 : for (int Z = lbound(thisDomain.Cells, 3); Z <= ubound(thisDomain.Cells, 3); ++Z) {
314 0 : for (int X = lbound(thisDomain.Cells, 1); X <= ubound(thisDomain.Cells, 1); ++X) {
315 : // Zone interface cells
316 0 : if (thisDomain.Cells(X, yMax, Z).cellType == CellType::ZoneGroundInterface) {
317 0 : thisDomain.WeightedHeatFlux(X, Z) = thisDomain.WeightingFactor(X, Z) * thisDomain.HeatFlux;
318 : }
319 : }
320 : }
321 :
322 : // Weighted heat flux and uniform heat flux balance energy may not balance exactly
323 : // Calculate difference and adjust
324 0 : thisDomain.TotalEnergyUniformHeatFlux = thisDomain.HeatFlux * thisDomain.SlabArea * thisDomain.Cur.CurSimTimeStepSize;
325 0 : thisDomain.TotalEnergyWeightedHeatFlux = 0.0;
326 :
327 0 : for (int Z = lbound(thisDomain.Cells, 3); Z <= ubound(thisDomain.Cells, 3); ++Z) {
328 0 : for (int X = lbound(thisDomain.Cells, 1); X <= ubound(thisDomain.Cells, 1); ++X) {
329 : // Zone interface cells
330 0 : if (thisDomain.Cells(X, yMax, Z).cellType == CellType::ZoneGroundInterface) {
331 0 : auto &cell = thisDomain.Cells(X, yMax, Z);
332 0 : thisDomain.TotalEnergyWeightedHeatFlux +=
333 0 : thisDomain.WeightedHeatFlux(X, Z) * cell.width() * cell.depth() * thisDomain.Cur.CurSimTimeStepSize;
334 : }
335 : }
336 : }
337 :
338 0 : thisDomain.HeatFluxWeightingFactor = thisDomain.TotalEnergyWeightedHeatFlux / thisDomain.TotalEnergyUniformHeatFlux;
339 0 : thisDomain.TotalEnergyWeightedHeatFlux = 0.0;
340 :
341 : // Finally, adjust the weighted heat flux so that energy balances
342 0 : for (int Z = lbound(thisDomain.Cells, 3); Z <= ubound(thisDomain.Cells, 3); ++Z) {
343 0 : for (int X = lbound(thisDomain.Cells, 1); X <= ubound(thisDomain.Cells, 1); ++X) {
344 : // Zone interface cells
345 0 : if (thisDomain.Cells(X, yMax, Z).cellType == CellType::ZoneGroundInterface) {
346 0 : auto &cell = thisDomain.Cells(X, yMax, Z);
347 0 : thisDomain.WeightedHeatFlux(X, Z) = thisDomain.WeightedHeatFlux(X, Z) / thisDomain.HeatFluxWeightingFactor;
348 0 : thisDomain.TotalEnergyWeightedHeatFlux +=
349 0 : thisDomain.WeightedHeatFlux(X, Z) * cell.width() * cell.depth() * thisDomain.Cur.CurSimTimeStepSize;
350 : }
351 : }
352 : }
353 :
354 : } else { // Coupled basement
355 1 : thisDomain.WallHeatFlux = thisDomain.AggregateWallHeatFlux / thisDomain.NumHeatFlux;
356 1 : thisDomain.FloorHeatFlux = thisDomain.AggregateFloorHeatFlux / thisDomain.NumHeatFlux;
357 : }
358 :
359 : // Shift history arrays only if necessary
360 1 : if (std::abs(thisDomain.Cur.CurSimTimeSeconds - thisDomain.Cur.PrevSimTimeSeconds) > 1.0e-6) {
361 1 : thisDomain.Cur.PrevSimTimeSeconds = thisDomain.Cur.CurSimTimeSeconds;
362 1 : thisDomain.ShiftTemperaturesForNewTimeStep();
363 1 : thisDomain.DomainNeedsSimulation = true;
364 : }
365 1 : thisDomain.PerformIterationLoop(state);
366 : }
367 : }
368 :
369 1 : if (state.dataPlantPipingSysMgr->WriteEIOFlag) {
370 : // Write eio header
371 : static constexpr std::string_view DomainCellsToEIOHeader(
372 : "! <Domain Name>, Total Number of Domain Cells, Total Number of Ground Surface Cells, Total Number of Insulation Cells\n");
373 1 : print(state.files.eio, DomainCellsToEIOHeader);
374 :
375 : // Write eio data
376 3 : for (auto &thisDomain : state.dataPlantPipingSysMgr->domains) {
377 : static constexpr std::string_view DomainCellsToEIO("{},{:5},{:5},{:5}\n");
378 2 : print(state.files.eio,
379 : DomainCellsToEIO,
380 2 : thisDomain.Name,
381 2 : thisDomain.NumDomainCells,
382 2 : thisDomain.NumGroundSurfCells,
383 2 : thisDomain.NumInsulationCells);
384 : }
385 1 : state.dataPlantPipingSysMgr->WriteEIOFlag = false;
386 : }
387 1 : }
388 :
389 1 : void GetPipingSystemsAndGroundDomainsInput(EnergyPlusData &state)
390 : {
391 :
392 : // SUBROUTINE INFORMATION:
393 : // AUTHOR Edwin Lee
394 : // DATE WRITTEN Summer 2011
395 : // MODIFIED na
396 : // RE-ENGINEERED na
397 :
398 : static constexpr std::string_view RoutineName("GetPipingSystemsAndGroundDomainsInput");
399 :
400 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
401 1 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
402 :
403 : // Read number of objects and allocate main data structures - first domains
404 1 : int NumGeneralizedDomains = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_ug_GeneralDomain);
405 1 : int NumHorizontalTrenches = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_HorizTrench);
406 1 : int NumZoneCoupledDomains = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_ZoneCoupled_Slab);
407 1 : int NumBasements = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_ZoneCoupled_Basement);
408 1 : int TotalNumDomains = NumGeneralizedDomains + NumHorizontalTrenches + NumZoneCoupledDomains + NumBasements;
409 1 : state.dataPlantPipingSysMgr->domains.resize(TotalNumDomains);
410 :
411 : // then circuits
412 1 : int NumPipeCircuits = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_Circuit);
413 :
414 : // Read in raw inputs, don't try to interpret dependencies yet
415 1 : ReadGeneralDomainInputs(state, 1, NumGeneralizedDomains, ErrorsFound);
416 : // ReadPipeCircuitInputs(ErrorsFound);
417 1 : ReadHorizontalTrenchInputs(state, NumGeneralizedDomains + 1, NumPipeCircuits + 1, ErrorsFound);
418 :
419 : // This is heavily dependent on the order of the domains in the main array.
420 1 : ReadZoneCoupledDomainInputs(state, NumGeneralizedDomains + NumHorizontalTrenches + 1, NumZoneCoupledDomains, ErrorsFound);
421 :
422 : // This is heavily dependent on the order of the domains in the main array.
423 1 : ReadBasementInputs(state, NumGeneralizedDomains + NumHorizontalTrenches + NumZoneCoupledDomains + 1, NumBasements, ErrorsFound);
424 :
425 : // Report errors that are purely input problems
426 1 : if (ErrorsFound) ShowFatalError(state, format("{}: Preceding input errors cause program termination.", RoutineName));
427 :
428 : // Setup output variables
429 1 : SetupPipingSystemOutputVariables(state);
430 :
431 : // Validate DOMAIN-CIRCUIT cross references
432 3 : for (int DomainNum = 0; DomainNum < TotalNumDomains; ++DomainNum) {
433 :
434 : // Convenience
435 2 : auto const &thisDomain = state.dataPlantPipingSysMgr->domains[DomainNum];
436 :
437 : // validate pipe domain-circuit name-to-index references
438 3 : for (auto &thisCircuit : thisDomain.circuits) {
439 1 : thisCircuit->ParentDomainIndex = DomainNum;
440 : }
441 :
442 : // correct segment locations for: INTERNAL DATA STRUCTURE Y VALUE MEASURED FROM BOTTOM OF DOMAIN,
443 : // INPUT WAS MEASURED FROM GROUND SURFACE
444 3 : for (auto const &thisCircuit : thisDomain.circuits) {
445 3 : for (auto &thisSegment : thisCircuit->pipeSegments) {
446 2 : thisSegment->PipeLocation.Y = thisDomain.Extents.yMax - thisSegment->PipeLocation.Y;
447 : }
448 : }
449 :
450 : // correct segment locations for: BASEMENT X SHIFT
451 2 : if (thisDomain.HasBasement && thisDomain.BasementZone.ShiftPipesByWidth) {
452 0 : for (auto const &thisCircuit : thisDomain.circuits) {
453 0 : for (auto &thisSegment : thisCircuit->pipeSegments) {
454 0 : thisSegment->PipeLocation.X += thisDomain.BasementZone.Width;
455 : }
456 : }
457 : }
458 :
459 : // now we will have good values of pipe segment locations, we can validate them
460 3 : for (auto const &thisCircuit : thisDomain.circuits) {
461 : // check to make sure it isn't outside the domain
462 3 : for (auto const &thisSegment : thisCircuit->pipeSegments) {
463 2 : if ((thisSegment->PipeLocation.X > thisDomain.Extents.xMax) || (thisSegment->PipeLocation.X < 0.0) ||
464 2 : (thisSegment->PipeLocation.Y > thisDomain.Extents.yMax) || (thisSegment->PipeLocation.Y < 0.0)) {
465 0 : ShowSevereError(state,
466 0 : format("PipingSystems::{}: A pipe was outside of the domain extents after performing corrections for "
467 : "basement or burial depth.",
468 : RoutineName));
469 0 : ShowContinueError(state, format("Pipe segment name:{}", thisSegment->Name));
470 0 : ShowContinueError(
471 : state,
472 0 : format("Corrected pipe location: ( x,y )=( {:.2T},{:.2T} )", thisSegment->PipeLocation.X, thisSegment->PipeLocation.Y));
473 : }
474 : } // segment loop
475 : } // circuit loop
476 :
477 : } // domain loop
478 :
479 : // If we encountered any other errors that we couldn't handle separately than stop now
480 1 : if (ErrorsFound) {
481 0 : ShowFatalError(state, format("{}:{}: Errors found in input.", RoutineName, ObjName_ug_GeneralDomain));
482 : }
483 1 : }
484 :
485 1 : void ReadGeneralDomainInputs(EnergyPlusData &state, int const IndexStart, int const NumGeneralizedDomains, bool &ErrorsFound)
486 : {
487 :
488 : // SUBROUTINE INFORMATION:
489 : // AUTHOR Edwin Lee
490 : // DATE WRITTEN Summer 2011
491 : // MODIFIED na
492 : // RE-ENGINEERED na
493 :
494 : // SUBROUTINE PARAMETER DEFINITIONS:
495 : static constexpr std::string_view routineName = "ReadGeneralDomainInputs";
496 :
497 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
498 : int NumAlphas; // Number of Alphas for each GetObjectItem call
499 : int NumNumbers; // Number of Numbers for each GetObjectItem call
500 : int IOStatus; // Used in GetObjectItem
501 : int CurIndex;
502 :
503 2 : for (int DomainNum = IndexStart; DomainNum <= NumGeneralizedDomains; ++DomainNum) {
504 :
505 : // Set up all the inputs for this domain object
506 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
507 : ObjName_ug_GeneralDomain,
508 : DomainNum,
509 1 : state.dataIPShortCut->cAlphaArgs,
510 : NumAlphas,
511 1 : state.dataIPShortCut->rNumericArgs,
512 : NumNumbers,
513 : IOStatus,
514 1 : state.dataIPShortCut->lNumericFieldBlanks,
515 1 : state.dataIPShortCut->lAlphaFieldBlanks,
516 1 : state.dataIPShortCut->cAlphaFieldNames,
517 1 : state.dataIPShortCut->cNumericFieldNames);
518 :
519 1 : ErrorObjectHeader eoh{routineName, ObjName_ug_GeneralDomain, state.dataIPShortCut->cAlphaArgs(1)};
520 :
521 1 : auto &thisDomain = state.dataPlantPipingSysMgr->domains[DomainNum - 1];
522 :
523 : // Get the name, validate
524 1 : thisDomain.Name = state.dataIPShortCut->cAlphaArgs(1);
525 1 : Util::IsNameEmpty(state, state.dataIPShortCut->cAlphaArgs(1), state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
526 : // Mesh extents, validated by IP
527 1 : thisDomain.Extents.xMax = state.dataIPShortCut->rNumericArgs(1);
528 1 : thisDomain.Extents.yMax = state.dataIPShortCut->rNumericArgs(2);
529 1 : thisDomain.Extents.zMax = state.dataIPShortCut->rNumericArgs(3);
530 :
531 : // X direction mesh inputs, validated by IP
532 1 : thisDomain.Mesh.X.RegionMeshCount = static_cast<int>(state.dataIPShortCut->rNumericArgs(4));
533 : {
534 1 : std::string const &meshDistribution = state.dataIPShortCut->cAlphaArgs(2);
535 1 : if (meshDistribution == "UNIFORM") {
536 1 : thisDomain.Mesh.X.thisMeshDistribution = MeshDistribution::Uniform;
537 0 : } else if (meshDistribution == "SYMMETRICGEOMETRIC") {
538 0 : thisDomain.Mesh.X.thisMeshDistribution = MeshDistribution::SymmetricGeometric;
539 0 : if (mod(thisDomain.Mesh.X.RegionMeshCount, 2) != 0) {
540 0 : ShowWarningError(state, format("PipingSystems:{}: Invalid mesh type-count combination.", routineName));
541 0 : ShowContinueError(state, format("Instance:{}={}", ObjName_ug_GeneralDomain, thisDomain.Name));
542 0 : ShowContinueError(state, "An ODD-valued X mesh count was found in the input for symmetric geometric configuration.");
543 0 : ShowContinueError(state, "This is invalid, mesh count incremented UP by one to next EVEN value.");
544 0 : ++thisDomain.Mesh.X.RegionMeshCount;
545 0 : thisDomain.Mesh.X.GeometricSeriesCoefficient = state.dataIPShortCut->rNumericArgs(5);
546 : } else {
547 0 : thisDomain.Mesh.X.GeometricSeriesCoefficient = 1.0;
548 : }
549 : } else {
550 0 : IssueSevereInputFieldError(state,
551 : routineName,
552 : ObjName_ug_GeneralDomain,
553 0 : state.dataIPShortCut->cAlphaArgs(1),
554 0 : state.dataIPShortCut->cAlphaFieldNames(2),
555 0 : state.dataIPShortCut->cAlphaArgs(2),
556 : "Use a choice from the available mesh type keys.",
557 : ErrorsFound);
558 : }
559 : }
560 :
561 : // Y direction mesh inputs, validated by IP
562 1 : thisDomain.Mesh.Y.RegionMeshCount = static_cast<int>(state.dataIPShortCut->rNumericArgs(6));
563 : {
564 1 : std::string const meshDistribution = stripped(state.dataIPShortCut->cAlphaArgs(3));
565 1 : if (meshDistribution == "UNIFORM") {
566 1 : thisDomain.Mesh.Y.thisMeshDistribution = MeshDistribution::Uniform;
567 0 : } else if (meshDistribution == "SYMMETRICGEOMETRIC") {
568 0 : thisDomain.Mesh.Y.thisMeshDistribution = MeshDistribution::SymmetricGeometric;
569 0 : if (mod(thisDomain.Mesh.Y.RegionMeshCount, 2) != 0) {
570 0 : ShowWarningError(state, format("PipingSystems:{}: Invalid mesh type-count combination.", routineName));
571 0 : ShowContinueError(state, format("Instance:{}={}", ObjName_ug_GeneralDomain, thisDomain.Name));
572 0 : ShowContinueError(state, "An ODD-valued Y mesh count was found in the input for symmetric geometric configuration.");
573 0 : ShowContinueError(state, "This is invalid, mesh count incremented UP by one to next EVEN value.");
574 0 : ++thisDomain.Mesh.Y.RegionMeshCount;
575 0 : thisDomain.Mesh.Y.GeometricSeriesCoefficient = state.dataIPShortCut->rNumericArgs(7);
576 : } else {
577 0 : thisDomain.Mesh.Y.GeometricSeriesCoefficient = 1.0;
578 : }
579 : } else {
580 0 : IssueSevereInputFieldError(state,
581 : routineName,
582 : ObjName_ug_GeneralDomain,
583 0 : state.dataIPShortCut->cAlphaArgs(1),
584 0 : state.dataIPShortCut->cAlphaFieldNames(3),
585 0 : state.dataIPShortCut->cAlphaArgs(3),
586 : "Use a choice from the available mesh type keys.",
587 : ErrorsFound);
588 : }
589 1 : }
590 :
591 : // Z direction mesh inputs, validated by IP
592 1 : thisDomain.Mesh.Z.RegionMeshCount = static_cast<int>(state.dataIPShortCut->rNumericArgs(8));
593 : {
594 1 : std::string const meshDistribution = stripped(state.dataIPShortCut->cAlphaArgs(4));
595 1 : if (meshDistribution == "UNIFORM") {
596 1 : thisDomain.Mesh.Z.thisMeshDistribution = MeshDistribution::Uniform;
597 0 : } else if (meshDistribution == "SYMMETRICGEOMETRIC") {
598 0 : thisDomain.Mesh.Z.thisMeshDistribution = MeshDistribution::SymmetricGeometric;
599 0 : if (mod(thisDomain.Mesh.Z.RegionMeshCount, 2) != 0) {
600 0 : ShowWarningError(state, format("PipingSystems:{}: Invalid mesh type-count combination.", routineName));
601 0 : ShowContinueError(state, format("Instance:{}={}", ObjName_ug_GeneralDomain, thisDomain.Name));
602 0 : ShowContinueError(state, "An ODD-valued Z mesh count was found in the input for symmetric geometric configuration.");
603 0 : ShowContinueError(state, "This is invalid, mesh count incremented UP by one to next EVEN value.");
604 0 : ++thisDomain.Mesh.Z.RegionMeshCount;
605 0 : thisDomain.Mesh.Z.GeometricSeriesCoefficient = state.dataIPShortCut->rNumericArgs(9);
606 : } else {
607 0 : thisDomain.Mesh.Z.GeometricSeriesCoefficient = 1.0;
608 : }
609 : } else {
610 0 : IssueSevereInputFieldError(state,
611 : routineName,
612 : ObjName_ug_GeneralDomain,
613 0 : state.dataIPShortCut->cAlphaArgs(1),
614 0 : state.dataIPShortCut->cAlphaFieldNames(4),
615 0 : state.dataIPShortCut->cAlphaArgs(4),
616 : "Use a choice from the available mesh type keys.",
617 : ErrorsFound);
618 : }
619 1 : }
620 :
621 : // Soil properties, validated min/max by IP
622 1 : thisDomain.GroundProperties.Conductivity = state.dataIPShortCut->rNumericArgs(10);
623 1 : thisDomain.GroundProperties.Density = state.dataIPShortCut->rNumericArgs(11);
624 1 : thisDomain.GroundProperties.SpecificHeat = state.dataIPShortCut->rNumericArgs(12);
625 :
626 : // Moisture properties, validated min/max by IP, and converted to a fraction for computation here
627 1 : thisDomain.Moisture.Theta_liq = state.dataIPShortCut->rNumericArgs(13) / 100.0;
628 1 : thisDomain.Moisture.Theta_sat = state.dataIPShortCut->rNumericArgs(14) / 100.0;
629 :
630 : // check if there is a basement
631 1 : if (state.dataIPShortCut->lAlphaFieldBlanks(7)) {
632 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(7));
633 0 : ErrorsFound = true;
634 1 : } else if (BooleanSwitch bs = getYesNoValue(state.dataIPShortCut->cAlphaArgs(7)); bs != BooleanSwitch::Invalid) {
635 1 : thisDomain.HasBasement = static_cast<bool>(bs);
636 : } else {
637 0 : ShowSevereInvalidBool(state, eoh, state.dataIPShortCut->cAlphaFieldNames(7), state.dataIPShortCut->cAlphaArgs(7));
638 0 : ErrorsFound = true;
639 : }
640 :
641 : // more work to do if there is a basement
642 1 : if (thisDomain.HasBasement) {
643 :
644 : // check if there are blank inputs related to the basement,
645 : // IP can't catch this because they are inherently optional if there ISN'T a basement
646 0 : if (state.dataIPShortCut->lNumericFieldBlanks(15) || state.dataIPShortCut->lNumericFieldBlanks(16) ||
647 0 : state.dataIPShortCut->lAlphaFieldBlanks(8) || state.dataIPShortCut->lAlphaFieldBlanks(9) ||
648 0 : state.dataIPShortCut->lAlphaFieldBlanks(10)) {
649 0 : ShowSevereError(state,
650 0 : format("Erroneous basement inputs for {}={}", ObjName_ug_GeneralDomain, state.dataIPShortCut->cAlphaArgs(1)));
651 0 : ShowContinueError(state, "Object specified to have a basement, while at least one basement input was left blank.");
652 0 : ErrorsFound = true;
653 : }
654 :
655 : // get dimensions for meshing
656 0 : CurIndex = 15;
657 0 : thisDomain.BasementZone.Width = state.dataIPShortCut->rNumericArgs(CurIndex);
658 0 : if (thisDomain.BasementZone.Width <= 0.0) {
659 0 : IssueSevereInputFieldError(state,
660 : routineName,
661 : ObjName_ug_GeneralDomain,
662 0 : state.dataIPShortCut->cAlphaArgs(1),
663 0 : state.dataIPShortCut->cNumericFieldNames(CurIndex),
664 0 : state.dataIPShortCut->rNumericArgs(CurIndex),
665 : "Basement width must be a positive nonzero value.",
666 : ErrorsFound);
667 : }
668 :
669 0 : CurIndex = 16;
670 0 : thisDomain.BasementZone.Depth = state.dataIPShortCut->rNumericArgs(CurIndex);
671 0 : if (thisDomain.BasementZone.Depth <= 0.0) {
672 0 : IssueSevereInputFieldError(state,
673 : routineName,
674 : ObjName_ug_GeneralDomain,
675 0 : state.dataIPShortCut->cAlphaArgs(1),
676 0 : state.dataIPShortCut->cNumericFieldNames(CurIndex),
677 0 : state.dataIPShortCut->rNumericArgs(CurIndex),
678 : "Basement depth must be a positive nonzero value.",
679 : ErrorsFound);
680 : }
681 :
682 : // check for dimension shift
683 0 : CurIndex = 8;
684 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(CurIndex)) {
685 0 : ShowSevereEmptyField(state, eoh, state.dataIPShortCut->cAlphaFieldNames(CurIndex));
686 0 : ErrorsFound = true;
687 0 : } else if (BooleanSwitch bs = getYesNoValue(state.dataIPShortCut->cAlphaArgs(CurIndex)); bs != BooleanSwitch::Invalid) {
688 0 : thisDomain.BasementZone.ShiftPipesByWidth = static_cast<bool>(bs);
689 : } else {
690 0 : ShowSevereInvalidBool(state, eoh, state.dataIPShortCut->cAlphaFieldNames(CurIndex), state.dataIPShortCut->cAlphaArgs(CurIndex));
691 0 : ErrorsFound = true;
692 : }
693 :
694 : // get boundary condition model names and indices --error check
695 0 : CurIndex = 9;
696 0 : thisDomain.BasementZone.WallBoundaryOSCMName = state.dataIPShortCut->cAlphaArgs(CurIndex);
697 0 : thisDomain.BasementZone.WallBoundaryOSCMIndex =
698 0 : Util::FindItemInList(thisDomain.BasementZone.WallBoundaryOSCMName, state.dataSurface->OSCM);
699 0 : if (thisDomain.BasementZone.WallBoundaryOSCMIndex <= 0) {
700 0 : IssueSevereInputFieldError(state,
701 : routineName,
702 : ObjName_ug_GeneralDomain,
703 0 : state.dataIPShortCut->cAlphaArgs(1),
704 0 : state.dataIPShortCut->cAlphaFieldNames(CurIndex),
705 0 : state.dataIPShortCut->cAlphaArgs(CurIndex),
706 : "Could not match with an Other Side Conditions Model input object.",
707 : ErrorsFound);
708 : } else {
709 0 : auto const &wallIndexes = GetSurfaceIndecesForOSCM(state, thisDomain.BasementZone.WallBoundaryOSCMIndex);
710 0 : if (wallIndexes.empty()) {
711 0 : IssueSevereInputFieldError(
712 : state,
713 : routineName,
714 : ObjName_ug_GeneralDomain,
715 0 : state.dataIPShortCut->cAlphaArgs(1),
716 0 : state.dataIPShortCut->cAlphaFieldNames(CurIndex),
717 0 : state.dataIPShortCut->cAlphaArgs(CurIndex),
718 : "Entry matched an Other Side Conditions Model, but no surfaces were found to be using this Other Side Conditions Model.",
719 : ErrorsFound);
720 : } else {
721 0 : thisDomain.BasementZone.WallSurfacePointers = wallIndexes;
722 : }
723 0 : }
724 :
725 0 : CurIndex = 10;
726 0 : thisDomain.BasementZone.FloorBoundaryOSCMName = state.dataIPShortCut->cAlphaArgs(CurIndex);
727 0 : thisDomain.BasementZone.FloorBoundaryOSCMIndex =
728 0 : Util::FindItemInList(thisDomain.BasementZone.FloorBoundaryOSCMName, state.dataSurface->OSCM);
729 0 : if (thisDomain.BasementZone.FloorBoundaryOSCMIndex <= 0) {
730 0 : IssueSevereInputFieldError(state,
731 : routineName,
732 : ObjName_ug_GeneralDomain,
733 0 : state.dataIPShortCut->cAlphaArgs(1),
734 0 : state.dataIPShortCut->cAlphaFieldNames(CurIndex),
735 0 : state.dataIPShortCut->cAlphaArgs(CurIndex),
736 : "Could not match with an Other Side Conditions Model input object.",
737 : ErrorsFound);
738 : } else {
739 0 : auto const &floorIndexes = GetSurfaceIndecesForOSCM(state, thisDomain.BasementZone.FloorBoundaryOSCMIndex);
740 0 : if (floorIndexes.empty()) {
741 0 : IssueSevereInputFieldError(
742 : state,
743 : routineName,
744 : ObjName_ug_GeneralDomain,
745 0 : state.dataIPShortCut->cAlphaArgs(1),
746 0 : state.dataIPShortCut->cAlphaFieldNames(CurIndex),
747 0 : state.dataIPShortCut->cAlphaArgs(CurIndex),
748 : "Entry matched an Other Side Conditions Model, but no surfaces were found to be using this Other Side Conditions Model.",
749 : ErrorsFound);
750 : } else {
751 0 : thisDomain.BasementZone.FloorSurfacePointers = floorIndexes;
752 : }
753 0 : }
754 : }
755 :
756 : // get some convergence tolerances, minimum/maximum are enforced by the IP, along with default values if user left them blank
757 1 : thisDomain.SimControls.Convergence_CurrentToPrevIteration = state.dataIPShortCut->rNumericArgs(17);
758 1 : thisDomain.SimControls.MaxIterationsPerTS = static_cast<int>(state.dataIPShortCut->rNumericArgs(18));
759 :
760 : // additional evapotranspiration parameter, min/max validated by IP
761 1 : thisDomain.Moisture.GroundCoverCoefficient = state.dataIPShortCut->rNumericArgs(19);
762 :
763 : // Allocate the circuit placeholder arrays
764 1 : int const NumCircuitsInThisDomain = int(state.dataIPShortCut->rNumericArgs(20));
765 :
766 : // Need to store the ground temp stuff because it will get wiped out in the call to the circuit factory
767 : GroundTemp::ModelType gtmType =
768 1 : static_cast<GroundTemp::ModelType>(getEnumValue(GroundTemp::modelTypeNamesUC, state.dataIPShortCut->cAlphaArgs(5)));
769 1 : if (gtmType == GroundTemp::ModelType::Invalid) {
770 0 : ShowSevereInvalidKey(state, eoh, state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5));
771 0 : ErrorsFound = true;
772 : }
773 :
774 1 : std::string const groundTempName = state.dataIPShortCut->cAlphaArgs(6);
775 :
776 : // Need to loop once to store the names ahead of time because calling the segment factory will override cAlphaArgs
777 1 : std::vector<std::string> circuitNamesToFind;
778 1 : int constexpr NumAlphasBeforePipeCircOne = 10;
779 2 : for (int CircuitCtr = 1; CircuitCtr <= NumCircuitsInThisDomain; ++CircuitCtr) {
780 1 : CurIndex = CircuitCtr + NumAlphasBeforePipeCircOne;
781 1 : if (state.dataIPShortCut->lAlphaFieldBlanks(CurIndex)) {
782 0 : IssueSevereInputFieldError(state,
783 : routineName,
784 : ObjName_Segment,
785 0 : state.dataIPShortCut->cAlphaArgs(1),
786 0 : state.dataIPShortCut->cAlphaFieldNames(CurIndex),
787 0 : state.dataIPShortCut->cAlphaArgs(CurIndex),
788 : "Expected a pipe circuit name, check pipe circuit count input field.",
789 : ErrorsFound);
790 : }
791 1 : circuitNamesToFind.push_back(state.dataIPShortCut->cAlphaArgs(CurIndex));
792 : }
793 : // then we can loop through and allow the factory to be called and carry on
794 2 : for (auto &circuitNameToFind : circuitNamesToFind) {
795 1 : thisDomain.circuits.push_back(Circuit::factory(state, circuitNameToFind, ErrorsFound));
796 : }
797 :
798 : // Initialize ground temperature model and get pointer reference
799 1 : thisDomain.groundTempModel = GroundTemp::GetGroundTempModelAndInit(state, gtmType, groundTempName);
800 1 : }
801 1 : }
802 :
803 15 : void ReadZoneCoupledDomainInputs(EnergyPlusData &state, int const StartingDomainNumForZone, int const NumZoneCoupledDomains, bool &ErrorsFound)
804 : {
805 :
806 : // SUBROUTINE INFORMATION:
807 : // AUTHOR Edwin Lee
808 : // DATE WRITTEN Summer 2011
809 : // MODIFIED Spring 2014 by Matt Mitchell and Sushobhit Acharya to accommodate ground coupled calculations
810 : // RE-ENGINEERED na
811 :
812 : // SUBROUTINE PARAMETER DEFINITIONS:
813 : static constexpr std::string_view routineName = "ReadZoneCoupledDomainInputs";
814 :
815 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
816 : int NumAlphas; // Number of Alphas for each GetObjectItem call
817 : int NumNumbers; // Number of Numbers for each GetObjectItem call
818 : int IOStatus; // Used in GetObjectItem
819 :
820 : // initialize these counters properly so they can be incremented within the DO loop
821 15 : int DomainCtr = StartingDomainNumForZone - 1;
822 :
823 : // For each domain, we need to process the inputs into a local array of derived type, then resolve each one, creating definitions for a zone
824 : // coupled domain. This way, the outer get input routines can handle it as though they were generalized routines
825 :
826 15 : auto &s_ipsc = state.dataIPShortCut;
827 15 : auto &s_mat = state.dataMaterial;
828 :
829 29 : for (int ZoneCoupledDomainCtr = 1; ZoneCoupledDomainCtr <= NumZoneCoupledDomains; ++ZoneCoupledDomainCtr) {
830 :
831 : // Increment the domain counters here
832 14 : ++DomainCtr;
833 :
834 : // Read all the inputs for this domain object
835 28 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
836 : ObjName_ZoneCoupled_Slab,
837 : ZoneCoupledDomainCtr,
838 14 : s_ipsc->cAlphaArgs,
839 : NumAlphas,
840 14 : s_ipsc->rNumericArgs,
841 : NumNumbers,
842 : IOStatus,
843 14 : s_ipsc->lNumericFieldBlanks,
844 14 : s_ipsc->lAlphaFieldBlanks,
845 14 : s_ipsc->cAlphaFieldNames,
846 14 : s_ipsc->cNumericFieldNames);
847 :
848 14 : ErrorObjectHeader eoh{routineName, ObjName_ZoneCoupled_Slab, s_ipsc->cAlphaArgs(1)};
849 :
850 14 : auto &thisDomain = state.dataPlantPipingSysMgr->domains[DomainCtr - 1];
851 :
852 : // Get the name, validate
853 : // Domain name
854 14 : thisDomain.Name = s_ipsc->cAlphaArgs(1);
855 :
856 14 : GlobalNames::VerifyUniqueInterObjectName(state,
857 14 : state.dataPlantPipingSysMgr->GroundDomainUniqueNames,
858 14 : s_ipsc->cAlphaArgs(1),
859 : ObjName_ZoneCoupled_Slab,
860 14 : s_ipsc->cAlphaFieldNames(1),
861 : ErrorsFound);
862 :
863 : // Read in the rest of the inputs into the local type for clarity during transition
864 14 : thisDomain.Extents.yMax = s_ipsc->rNumericArgs(1);
865 14 : thisDomain.PerimeterOffset = s_ipsc->rNumericArgs(3);
866 14 : thisDomain.GroundProperties.Conductivity = s_ipsc->rNumericArgs(4);
867 14 : thisDomain.GroundProperties.Density = s_ipsc->rNumericArgs(5);
868 14 : thisDomain.GroundProperties.SpecificHeat = s_ipsc->rNumericArgs(6);
869 14 : thisDomain.Moisture.Theta_liq = s_ipsc->rNumericArgs(7) / 100.0;
870 14 : thisDomain.Moisture.Theta_sat = s_ipsc->rNumericArgs(8) / 100.0;
871 14 : thisDomain.Moisture.GroundCoverCoefficient = s_ipsc->rNumericArgs(9);
872 14 : thisDomain.HorizInsWidth = s_ipsc->rNumericArgs(10);
873 14 : thisDomain.VertInsDepth = s_ipsc->rNumericArgs(11);
874 :
875 : // Set flag for slab in-grade or slab on-grade
876 14 : if (s_ipsc->lAlphaFieldBlanks(5)) {
877 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
878 14 : } else if ((thisDomain.slabPosition = static_cast<SlabPosition>(getEnumValue(slabPositionNamesUC, s_ipsc->cAlphaArgs(5)))) ==
879 : SlabPosition::Invalid) {
880 1 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
881 1 : ErrorsFound = true;
882 : }
883 :
884 : // Get slab material properties
885 14 : if (thisDomain.slabPosition == SlabPosition::InGrade) {
886 12 : thisDomain.SlabMaterialNum = Material::GetMaterialNum(state, s_ipsc->cAlphaArgs(6));
887 12 : if (thisDomain.SlabMaterialNum == 0) {
888 1 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
889 1 : ErrorsFound = true;
890 : } else {
891 11 : auto const *mat = s_mat->materials(thisDomain.SlabMaterialNum);
892 11 : thisDomain.SlabThickness = mat->Thickness;
893 11 : thisDomain.SlabProperties.Density = mat->Density;
894 11 : thisDomain.SlabProperties.SpecificHeat = mat->SpecHeat;
895 11 : thisDomain.SlabProperties.Conductivity = mat->Conductivity;
896 : }
897 : }
898 :
899 : // set flag for horizontal insulation
900 14 : if (thisDomain.slabPosition == SlabPosition::InGrade) {
901 12 : if (s_ipsc->lAlphaFieldBlanks(7)) {
902 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(7));
903 0 : ErrorsFound = true;
904 12 : } else if (BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(7)); bs != BooleanSwitch::Invalid) {
905 11 : thisDomain.HorizIns = static_cast<bool>(bs) ? HorizInsulation::Perimeter : HorizInsulation::None;
906 : } else {
907 1 : ShowSevereInvalidBool(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
908 1 : ErrorsFound = true;
909 : }
910 : }
911 :
912 : // Get horizontal insulation material properties
913 14 : if (thisDomain.HorizIns != HorizInsulation::Invalid && thisDomain.HorizIns != HorizInsulation::None) {
914 11 : thisDomain.HorizInsMaterialNum = Material::GetMaterialNum(state, s_ipsc->cAlphaArgs(8));
915 11 : if (thisDomain.HorizInsMaterialNum == 0) {
916 1 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8));
917 1 : ErrorsFound = true;
918 : } else {
919 10 : auto const *mat = s_mat->materials(thisDomain.HorizInsMaterialNum);
920 10 : thisDomain.HorizInsThickness = mat->Thickness;
921 10 : thisDomain.HorizInsProperties.Density = mat->Density;
922 10 : thisDomain.HorizInsProperties.SpecificHeat = mat->SpecHeat;
923 10 : thisDomain.HorizInsProperties.Conductivity = mat->Conductivity;
924 10 : if (SiteGroundDomainUsingNoMassMat(state, thisDomain.HorizInsThickness, thisDomain.HorizInsMaterialNum)) {
925 0 : ErrorsFound = true;
926 0 : SiteGroundDomainNoMassMatError(state, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8), thisDomain.Name);
927 : }
928 : }
929 :
930 11 : if (s_ipsc->lAlphaFieldBlanks(9)) {
931 0 : if (thisDomain.HorizIns != HorizInsulation::None) {
932 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(9));
933 0 : ErrorsFound = true;
934 : }
935 11 : } else if ((thisDomain.HorizIns = static_cast<HorizInsulation>(getEnumValue(horizInsulationNamesUC, s_ipsc->cAlphaArgs(9)))) ==
936 : HorizInsulation::Invalid) {
937 1 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(9), s_ipsc->cAlphaArgs(9));
938 1 : ErrorsFound = true;
939 10 : } else if (thisDomain.HorizIns == HorizInsulation::Perimeter) {
940 : // Horizontal insulation perimeter width
941 1 : if (thisDomain.HorizInsWidth <= 0.0) {
942 1 : ShowSevereError(state, format("Invalid {}", s_ipsc->cNumericFieldNames(10)));
943 1 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
944 1 : ErrorsFound = true;
945 : }
946 : }
947 : }
948 :
949 : // set flag for vertical insulation
950 14 : if (s_ipsc->lAlphaFieldBlanks(10)) {
951 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(10));
952 0 : ErrorsFound = true;
953 14 : } else if (BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(10)); bs != BooleanSwitch::Invalid) {
954 13 : thisDomain.VertInsPresentFlag = static_cast<bool>(bs);
955 : } else {
956 1 : ShowSevereInvalidBool(state, eoh, s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
957 1 : ErrorsFound = true;
958 : }
959 :
960 : // Get vertical insulation material properties
961 14 : if (thisDomain.VertInsPresentFlag) {
962 12 : thisDomain.VertInsMaterialNum = Material::GetMaterialNum(state, s_ipsc->cAlphaArgs(11));
963 12 : if (thisDomain.VertInsMaterialNum == 0) {
964 1 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(11), s_ipsc->cAlphaArgs(11));
965 1 : ErrorsFound = true;
966 : } else {
967 11 : auto const *mat = s_mat->materials(thisDomain.VertInsMaterialNum);
968 11 : thisDomain.VertInsThickness = mat->Thickness;
969 11 : thisDomain.VertInsProperties.Density = mat->Density;
970 11 : thisDomain.VertInsProperties.SpecificHeat = mat->SpecHeat;
971 11 : thisDomain.VertInsProperties.Conductivity = mat->Conductivity;
972 11 : if (SiteGroundDomainUsingNoMassMat(state, thisDomain.VertInsThickness, thisDomain.VertInsMaterialNum)) {
973 0 : ErrorsFound = true;
974 0 : SiteGroundDomainNoMassMatError(state, s_ipsc->cAlphaFieldNames(11), s_ipsc->cAlphaArgs(11), thisDomain.Name);
975 : }
976 : }
977 :
978 : // vertical insulation depth
979 12 : if (thisDomain.VertInsDepth > thisDomain.Extents.yMax || thisDomain.VertInsDepth <= 0.0) {
980 1 : ShowSevereError(state, format("Invalid {}", s_ipsc->cNumericFieldNames(11)));
981 1 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
982 1 : ErrorsFound = true;
983 : }
984 : }
985 :
986 : #ifdef GET_OUT
987 : // This does not appear to be used anywhere
988 : // Set simulation interval flag
989 : if (Util::SameString(s_ipsc->cAlphaArgs(12), "TIMESTEP")) {
990 : thisDomain.SimTimeStepFlag = true;
991 : } else if (Util::SameString(s_ipsc->cAlphaArgs(12), "HOURLY")) {
992 : thisDomain.SimHourlyFlag = true;
993 : } else {
994 : ShowSevereError(state, format("Invalid {}={}", s_ipsc->cAlphaFieldNames(12), s_ipsc->cAlphaArgs(12)));
995 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
996 : ErrorsFound = true;
997 : }
998 : #endif // GET_OUT
999 :
1000 : //******* We'll first set up the domain ********
1001 14 : thisDomain.IsActuallyPartOfAHorizontalTrench = false;
1002 14 : thisDomain.HasAPipeCircuit = false;
1003 14 : thisDomain.HasZoneCoupledSlab = true;
1004 :
1005 : // get boundary condition model names and indices -- error check
1006 14 : thisDomain.ZoneCoupledOSCMIndex = Util::FindItemInList(s_ipsc->cAlphaArgs(4), state.dataSurface->OSCM);
1007 14 : if (thisDomain.ZoneCoupledOSCMIndex <= 0) {
1008 2 : IssueSevereInputFieldError(state,
1009 : routineName,
1010 : ObjName_ZoneCoupled_Slab,
1011 1 : s_ipsc->cAlphaArgs(1),
1012 1 : s_ipsc->cAlphaFieldNames(4),
1013 1 : s_ipsc->cAlphaArgs(4),
1014 : "Could not match with an Other Side Conditions Model input object.",
1015 : ErrorsFound);
1016 1 : ErrorsFound = true;
1017 : } else {
1018 13 : int const NumSurfacesWithThisOSCM = GetSurfaceCountForOSCM(state, thisDomain.ZoneCoupledOSCMIndex);
1019 13 : if (NumSurfacesWithThisOSCM <= 0) {
1020 0 : IssueSevereInputFieldError(
1021 : state,
1022 : routineName,
1023 : ObjName_ZoneCoupled_Slab,
1024 0 : s_ipsc->cAlphaArgs(1),
1025 0 : s_ipsc->cAlphaFieldNames(4),
1026 0 : s_ipsc->cAlphaArgs(4),
1027 : "Entry matched an Other Side Conditions Model, but no surfaces were found to be using this Other Side Conditions Model.",
1028 : ErrorsFound);
1029 0 : ErrorsFound = true;
1030 : } else {
1031 13 : thisDomain.ZoneCoupledSurfaces = GetSurfaceDataForOSCM(state, thisDomain.ZoneCoupledOSCMIndex);
1032 : }
1033 : }
1034 :
1035 : // Total surface area
1036 13 : auto lambda = [](Real64 total, ZoneCoupledSurfaceData const &z) { return total + z.SurfaceArea; };
1037 14 : Real64 const ThisArea = std::accumulate(thisDomain.ZoneCoupledSurfaces.begin(), thisDomain.ZoneCoupledSurfaces.end(), 0.0, lambda);
1038 :
1039 14 : thisDomain.SlabArea = ThisArea / 4; // We are only interested in 1/4 of total area due to symmetry
1040 :
1041 : // Surface dimensions
1042 14 : Real64 thisAspectRatio = s_ipsc->rNumericArgs(2);
1043 14 : thisDomain.SlabWidth = std::sqrt(ThisArea / thisAspectRatio);
1044 14 : thisDomain.SlabLength = thisDomain.SlabWidth * thisAspectRatio;
1045 :
1046 : // Check horizontal insulation width so as to prevent overlapping insulation. VertInsThickness is used here since it is used for vertical
1047 : // partition thickness.
1048 14 : if (thisDomain.HorizIns == HorizInsulation::Perimeter && ThisArea > 0.0) {
1049 1 : if (2 * (thisDomain.HorizInsWidth + thisDomain.VertInsThickness) > thisDomain.SlabWidth ||
1050 1 : 2 * (thisDomain.HorizInsWidth + thisDomain.VertInsThickness) > thisDomain.SlabLength) {
1051 0 : ShowContinueError(state, format("{}: Perimeter insulation width is too large.", routineName));
1052 0 : ShowContinueError(state, "This would cause overlapping insulation. Check inputs.");
1053 0 : ShowContinueError(state, "Defaulting to full horizontal insulation.");
1054 0 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
1055 0 : thisDomain.HorizIns = HorizInsulation::Full;
1056 : }
1057 : }
1058 :
1059 : // Set ground domain dimensions
1060 14 : thisDomain.Extents.xMax = thisDomain.PerimeterOffset + thisDomain.SlabWidth / 2;
1061 : // thisDomain.Extents.yMax read above
1062 14 : thisDomain.Extents.zMax = thisDomain.PerimeterOffset + thisDomain.SlabLength / 2;
1063 :
1064 : // Get mesh parameters
1065 :
1066 : // Mesh inputs
1067 14 : thisDomain.Mesh.X.thisMeshDistribution = MeshDistribution::SymmetricGeometric;
1068 14 : thisDomain.Mesh.Y.thisMeshDistribution = MeshDistribution::SymmetricGeometric;
1069 14 : thisDomain.Mesh.Z.thisMeshDistribution = MeshDistribution::SymmetricGeometric;
1070 :
1071 14 : Real64 MeshCoefficient = s_ipsc->rNumericArgs(12);
1072 14 : if (MeshCoefficient == 0.0) MeshCoefficient = 1.6;
1073 14 : thisDomain.Mesh.X.GeometricSeriesCoefficient = MeshCoefficient;
1074 14 : thisDomain.Mesh.Y.GeometricSeriesCoefficient = MeshCoefficient;
1075 14 : thisDomain.Mesh.Z.GeometricSeriesCoefficient = MeshCoefficient;
1076 :
1077 14 : int MeshCount = static_cast<int>(s_ipsc->rNumericArgs(13));
1078 14 : if (MeshCount == 0.0) MeshCount = 6;
1079 14 : thisDomain.Mesh.X.RegionMeshCount = MeshCount;
1080 14 : thisDomain.Mesh.Y.RegionMeshCount = MeshCount;
1081 14 : thisDomain.Mesh.Z.RegionMeshCount = MeshCount;
1082 :
1083 14 : thisDomain.NumSlabCells = thisDomain.Mesh.Y.RegionMeshCount; // Need to clean this out at some point
1084 :
1085 14 : GroundTemp::ModelType gtmType = static_cast<GroundTemp::ModelType>(getEnumValue(GroundTemp::modelTypeNamesUC, s_ipsc->cAlphaArgs(2)));
1086 14 : if (gtmType == GroundTemp::ModelType::Invalid) {
1087 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
1088 0 : ErrorsFound = true;
1089 : }
1090 :
1091 : // Farfield model
1092 : // Ok, this is a finicky bug, but I have to make a copy here. GetGroundTempModelAndInit takes name (last param) by const ref&
1093 : // It then calls FiniteDiffGroundTempsModel::FiniteDiffGTMFactory which also takes objectName by const ref&
1094 : // But it calls getObjectItem with s_ipsc->cAlphaArgs which overrides it, then the comparison fails
1095 14 : std::string const groundTempModelName = s_ipsc->cAlphaArgs(3);
1096 14 : thisDomain.groundTempModel = GroundTemp::GetGroundTempModelAndInit(state, gtmType, groundTempModelName);
1097 :
1098 : // Other parameters
1099 14 : thisDomain.SimControls.Convergence_CurrentToPrevIteration = 0.001;
1100 14 : thisDomain.SimControls.MaxIterationsPerTS = 250;
1101 :
1102 : // setup output variables
1103 14 : thisDomain.SetupZoneCoupledOutputVariables(state);
1104 14 : }
1105 15 : }
1106 :
1107 10 : void ReadBasementInputs(EnergyPlusData &state, int const StartingDomainNumForBasement, int const NumBasements, bool &ErrorsFound)
1108 : {
1109 :
1110 : // SUBROUTINE INFORMATION:
1111 : // AUTHOR Edwin Lee
1112 : // DATE WRITTEN Summer 2011
1113 : // MODIFIED Summer 2014 Sushobhit Acharya to accommodate basement calculations
1114 : // RE-ENGINEERED na
1115 :
1116 : // SUBROUTINE PARAMETER DEFINITIONS:
1117 : static constexpr std::string_view routineName = "ReadBasementInputs";
1118 :
1119 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1120 : int NumAlphas; // Number of Alphas for each GetObjectItem call
1121 : int NumNumbers; // Number of Numbers for each GetObjectItem call
1122 : int IOStatus; // Used in GetObjectItem
1123 :
1124 : // initialize these counters properly so they can be incremented within the DO loop
1125 10 : int DomainNum = StartingDomainNumForBasement - 1;
1126 :
1127 : // For each domain, we need to process the inputs into a local array of derived type, then resolve each one, creating definitions for a zone
1128 : // coupled domain. This way, the outer get input routines can handle it as though they were generalized routines
1129 10 : auto &s_ipsc = state.dataIPShortCut;
1130 10 : auto &s_mat = state.dataMaterial;
1131 :
1132 20 : for (int BasementCtr = 1; BasementCtr <= NumBasements; ++BasementCtr) {
1133 :
1134 : // Increment the domain counters here
1135 10 : ++DomainNum;
1136 :
1137 : // Read all the inputs for this domain object
1138 20 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1139 : ObjName_ZoneCoupled_Basement,
1140 : BasementCtr,
1141 10 : s_ipsc->cAlphaArgs,
1142 : NumAlphas,
1143 10 : s_ipsc->rNumericArgs,
1144 : NumNumbers,
1145 : IOStatus,
1146 10 : s_ipsc->lNumericFieldBlanks,
1147 10 : s_ipsc->lAlphaFieldBlanks,
1148 10 : s_ipsc->cAlphaFieldNames,
1149 10 : s_ipsc->cNumericFieldNames);
1150 :
1151 10 : ErrorObjectHeader eoh{routineName, ObjName_ZoneCoupled_Basement, s_ipsc->cAlphaArgs(1)};
1152 10 : auto &thisDomain = state.dataPlantPipingSysMgr->domains[DomainNum - 1];
1153 :
1154 : // Get the name, validate
1155 10 : thisDomain.Name = s_ipsc->cAlphaArgs(1);
1156 10 : GlobalNames::VerifyUniqueInterObjectName(state,
1157 10 : state.dataPlantPipingSysMgr->GroundDomainUniqueNames,
1158 10 : s_ipsc->cAlphaArgs(1),
1159 : ObjName_ZoneCoupled_Basement,
1160 10 : s_ipsc->cAlphaFieldNames(1),
1161 : ErrorsFound);
1162 :
1163 : // Read in the some of the inputs into the local type for clarity during transition
1164 10 : thisDomain.Extents.yMax = s_ipsc->rNumericArgs(1);
1165 10 : Real64 const thisAspectRatio = s_ipsc->rNumericArgs(2);
1166 10 : thisDomain.PerimeterOffset = s_ipsc->rNumericArgs(3);
1167 10 : thisDomain.HorizInsWidth = s_ipsc->rNumericArgs(10);
1168 10 : thisDomain.VertInsDepth = s_ipsc->rNumericArgs(12);
1169 :
1170 : // Other inputs
1171 10 : thisDomain.Name = s_ipsc->cAlphaArgs(1);
1172 :
1173 : // Soil properties, validated min/max by IP
1174 10 : thisDomain.GroundProperties.Conductivity = s_ipsc->rNumericArgs(4);
1175 10 : thisDomain.GroundProperties.Density = s_ipsc->rNumericArgs(5);
1176 10 : thisDomain.GroundProperties.SpecificHeat = s_ipsc->rNumericArgs(6);
1177 :
1178 : // Moisture properties, validated min/max by IP, and converted to a fraction for computation here
1179 10 : thisDomain.Moisture.Theta_liq = s_ipsc->rNumericArgs(7) / 100.0;
1180 10 : thisDomain.Moisture.Theta_sat = s_ipsc->rNumericArgs(8) / 100.0;
1181 :
1182 : // check if there are blank inputs related to the basement,
1183 10 : if (s_ipsc->lNumericFieldBlanks(11) || s_ipsc->lAlphaFieldBlanks(5) || s_ipsc->lAlphaFieldBlanks(10)) {
1184 0 : ShowSevereError(state, format("Erroneous basement inputs for {}={}", ObjName_ZoneCoupled_Basement, s_ipsc->cAlphaArgs(1)));
1185 0 : ShowContinueError(state, "At least one basement input was left blank.");
1186 0 : ErrorsFound = true;
1187 : }
1188 :
1189 : // Basement zone depth
1190 10 : int CurIndex = 11;
1191 10 : thisDomain.BasementZone.Depth = s_ipsc->rNumericArgs(CurIndex);
1192 10 : if (thisDomain.BasementZone.Depth >= thisDomain.Extents.yMax || thisDomain.BasementZone.Depth <= 0.0) {
1193 1 : ShowSevereError(state, format("Invalid {}", s_ipsc->cNumericFieldNames(CurIndex)));
1194 1 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
1195 1 : ErrorsFound = true;
1196 : }
1197 :
1198 : // get boundary condition model names and indices --error check
1199 10 : CurIndex = 4;
1200 10 : thisDomain.BasementZone.FloorBoundaryOSCMName = s_ipsc->cAlphaArgs(CurIndex);
1201 10 : thisDomain.BasementZone.FloorBoundaryOSCMIndex =
1202 10 : Util::FindItemInList(thisDomain.BasementZone.FloorBoundaryOSCMName, state.dataSurface->OSCM);
1203 10 : if (thisDomain.BasementZone.FloorBoundaryOSCMIndex <= 0) {
1204 3 : IssueSevereInputFieldError(state,
1205 : routineName,
1206 : ObjName_ZoneCoupled_Basement,
1207 1 : s_ipsc->cAlphaArgs(1),
1208 1 : s_ipsc->cAlphaFieldNames(CurIndex),
1209 1 : s_ipsc->cAlphaArgs(CurIndex),
1210 : "Could not match with an Other Side Conditions Model input object.",
1211 : ErrorsFound);
1212 : } else {
1213 9 : auto const &floorIndexes = GetSurfaceIndecesForOSCM(state, thisDomain.BasementZone.FloorBoundaryOSCMIndex);
1214 9 : if (floorIndexes.empty()) {
1215 0 : IssueSevereInputFieldError(
1216 : state,
1217 : routineName,
1218 : ObjName_ZoneCoupled_Basement,
1219 0 : s_ipsc->cAlphaArgs(1),
1220 0 : s_ipsc->cAlphaFieldNames(CurIndex),
1221 0 : s_ipsc->cAlphaArgs(CurIndex),
1222 : "Entry matched an Other Side Conditions Model, but no surfaces were found to be using this Other Side Conditions Model.",
1223 : ErrorsFound);
1224 : } else {
1225 9 : thisDomain.BasementZone.FloorSurfacePointers = floorIndexes;
1226 : // Create GetSurfaceDataForOSCM function
1227 9 : thisDomain.ZoneCoupledSurfaces = GetSurfaceDataForOSCM(state, thisDomain.BasementZone.FloorBoundaryOSCMIndex);
1228 : }
1229 9 : }
1230 :
1231 10 : CurIndex = 8;
1232 10 : thisDomain.BasementZone.WallBoundaryOSCMName = s_ipsc->cAlphaArgs(CurIndex);
1233 10 : thisDomain.BasementZone.WallBoundaryOSCMIndex =
1234 10 : Util::FindItemInList(thisDomain.BasementZone.WallBoundaryOSCMName, state.dataSurface->OSCM);
1235 10 : if (thisDomain.BasementZone.WallBoundaryOSCMIndex <= 0) {
1236 2 : IssueSevereInputFieldError(state,
1237 : routineName,
1238 : ObjName_ZoneCoupled_Basement,
1239 1 : s_ipsc->cAlphaArgs(1),
1240 1 : s_ipsc->cAlphaFieldNames(CurIndex),
1241 1 : s_ipsc->cAlphaArgs(CurIndex),
1242 : "Could not match with an Other Side Conditions Model input object.",
1243 : ErrorsFound);
1244 1 : ErrorsFound = true;
1245 : } else {
1246 9 : auto const &wallIndexes = GetSurfaceIndecesForOSCM(state, thisDomain.BasementZone.WallBoundaryOSCMIndex);
1247 9 : if (wallIndexes.empty()) {
1248 0 : IssueSevereInputFieldError(
1249 : state,
1250 : routineName,
1251 : ObjName_ZoneCoupled_Basement,
1252 0 : s_ipsc->cAlphaArgs(1),
1253 0 : s_ipsc->cAlphaFieldNames(CurIndex),
1254 0 : s_ipsc->cAlphaArgs(CurIndex),
1255 : "Entry matched an Other Side Conditions Model, but no surfaces were found to be using this Other Side Conditions Model.",
1256 : ErrorsFound);
1257 0 : ErrorsFound = true;
1258 : } else {
1259 9 : thisDomain.BasementZone.WallSurfacePointers = wallIndexes;
1260 : }
1261 9 : }
1262 :
1263 : // get some convergence tolerances, minimum/maximum are enforced by the IP, along with default values if user left them blank
1264 10 : thisDomain.SimControls.Convergence_CurrentToPrevIteration = 0.01;
1265 10 : thisDomain.SimControls.MaxIterationsPerTS = 250;
1266 :
1267 : // additional evapotranspiration parameter, min/max validated by IP
1268 10 : thisDomain.Moisture.GroundCoverCoefficient = s_ipsc->rNumericArgs(9);
1269 :
1270 : // assign the mesh count
1271 : int meshCount;
1272 10 : if (s_ipsc->lNumericFieldBlanks(13)) {
1273 10 : meshCount = 4;
1274 : } else {
1275 0 : meshCount = static_cast<int>(s_ipsc->rNumericArgs(13));
1276 : }
1277 10 : thisDomain.Mesh.X.RegionMeshCount = meshCount;
1278 10 : thisDomain.Mesh.Y.RegionMeshCount = meshCount;
1279 10 : thisDomain.Mesh.Z.RegionMeshCount = meshCount;
1280 :
1281 10 : thisDomain.Mesh.X.thisMeshDistribution = MeshDistribution::Uniform;
1282 10 : thisDomain.Mesh.Y.thisMeshDistribution = MeshDistribution::Uniform;
1283 10 : thisDomain.Mesh.Z.thisMeshDistribution = MeshDistribution::Uniform;
1284 :
1285 : // Initialize properties for basement interface cells
1286 10 : thisDomain.BasementInterfaceProperties.Conductivity = 500.0;
1287 10 : thisDomain.BasementInterfaceProperties.SpecificHeat = 1.0;
1288 10 : thisDomain.BasementInterfaceProperties.Density = 1.0;
1289 :
1290 : // set flag for horizontal insulation
1291 : // Check s_ipsc->cAlphaArgs value
1292 10 : if (s_ipsc->lAlphaFieldBlanks(5)) {
1293 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
1294 0 : ErrorsFound = true;
1295 10 : } else if (BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(5)); bs != BooleanSwitch::Invalid) {
1296 9 : thisDomain.HorizIns = static_cast<bool>(bs) ? HorizInsulation::Perimeter : HorizInsulation::None;
1297 : } else {
1298 1 : ShowSevereInvalidBool(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
1299 1 : ErrorsFound = true;
1300 : }
1301 :
1302 : // Get horizontal insulation material properties
1303 10 : if (thisDomain.HorizIns != HorizInsulation::None) {
1304 10 : thisDomain.HorizInsMaterialNum = Material::GetMaterialNum(state, s_ipsc->cAlphaArgs(6));
1305 10 : if (thisDomain.HorizInsMaterialNum == 0) {
1306 1 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
1307 1 : ErrorsFound = true;
1308 : } else {
1309 9 : auto const *mat = s_mat->materials(thisDomain.HorizInsMaterialNum);
1310 9 : thisDomain.HorizInsThickness = mat->Thickness;
1311 9 : thisDomain.HorizInsProperties.Density = mat->Density;
1312 9 : thisDomain.HorizInsProperties.SpecificHeat = mat->SpecHeat;
1313 9 : thisDomain.HorizInsProperties.Conductivity = mat->Conductivity;
1314 9 : if (SiteGroundDomainUsingNoMassMat(state, thisDomain.HorizInsThickness, thisDomain.HorizInsMaterialNum)) {
1315 0 : ErrorsFound = true;
1316 0 : SiteGroundDomainNoMassMatError(state, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6), thisDomain.Name);
1317 : }
1318 : }
1319 :
1320 : // Set flag for horizontal insulation extents
1321 10 : if (s_ipsc->lAlphaFieldBlanks(7)) {
1322 0 : if (thisDomain.HorizIns != HorizInsulation::None) {
1323 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(7));
1324 0 : ErrorsFound = true;
1325 : }
1326 10 : } else if ((thisDomain.HorizIns = static_cast<HorizInsulation>(getEnumValue(horizInsulationNamesUC, s_ipsc->cAlphaArgs(7)))) ==
1327 : HorizInsulation::Invalid) {
1328 1 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
1329 1 : ErrorsFound = true;
1330 9 : } else if (thisDomain.HorizIns == HorizInsulation::Perimeter) {
1331 : // Horizontal insulation perimeter width
1332 9 : if (thisDomain.HorizInsWidth <= 0.0) {
1333 0 : ShowSevereError(state, format("Invalid {}", s_ipsc->cNumericFieldNames(10)));
1334 0 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
1335 0 : ErrorsFound = true;
1336 : }
1337 : }
1338 : }
1339 :
1340 : // set flag for vertical insulation
1341 10 : if (s_ipsc->lAlphaFieldBlanks(9)) {
1342 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(9));
1343 0 : ErrorsFound = true;
1344 10 : } else if (BooleanSwitch bs = getYesNoValue(s_ipsc->cAlphaArgs(9)); bs != BooleanSwitch::Invalid) {
1345 9 : thisDomain.VertInsPresentFlag = static_cast<bool>(bs);
1346 : } else {
1347 1 : ShowSevereInvalidBool(state, eoh, s_ipsc->cAlphaFieldNames(9), s_ipsc->cAlphaArgs(9));
1348 1 : ErrorsFound = true;
1349 : }
1350 :
1351 : // Get vertical insulation material properties
1352 10 : if (thisDomain.VertInsPresentFlag) {
1353 : // Check if vertical insulation is in domain
1354 9 : if (thisDomain.VertInsDepth >= thisDomain.Extents.yMax || thisDomain.VertInsDepth <= 0.0) {
1355 0 : ShowSevereError(state, format("Invalid {}", s_ipsc->cNumericFieldNames(12)));
1356 0 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
1357 0 : ErrorsFound = true;
1358 : }
1359 9 : thisDomain.VertInsMaterialNum = Material::GetMaterialNum(state, s_ipsc->cAlphaArgs(10));
1360 9 : if (thisDomain.VertInsMaterialNum == 0) {
1361 1 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10));
1362 1 : ErrorsFound = true;
1363 : } else {
1364 8 : auto const *mat = s_mat->materials(thisDomain.VertInsMaterialNum);
1365 8 : thisDomain.VertInsThickness = mat->Thickness;
1366 8 : thisDomain.VertInsProperties.Density = mat->Density;
1367 8 : thisDomain.VertInsProperties.SpecificHeat = mat->SpecHeat;
1368 8 : thisDomain.VertInsProperties.Conductivity = mat->Conductivity;
1369 8 : if (SiteGroundDomainUsingNoMassMat(state, thisDomain.VertInsThickness, thisDomain.VertInsMaterialNum)) {
1370 0 : ErrorsFound = true;
1371 0 : SiteGroundDomainNoMassMatError(state, s_ipsc->cAlphaFieldNames(10), s_ipsc->cAlphaArgs(10), thisDomain.Name);
1372 : }
1373 : }
1374 : }
1375 :
1376 : #ifdef GET_OUT
1377 : // This is not actually used
1378 : // Set simulation interval flag
1379 : if (Util::SameString(s_ipsc->cAlphaArgs(11), "TIMESTEP")) {
1380 : thisDomain.SimTimeStepFlag = true;
1381 : } else if (Util::SameString(s_ipsc->cAlphaArgs(11), "HOURLY")) {
1382 : thisDomain.SimHourlyFlag = true;
1383 : } else {
1384 : ShowSevereError(state, format("Invalid {}={}", s_ipsc->cAlphaFieldNames(11), s_ipsc->cAlphaArgs(11)));
1385 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
1386 : ErrorsFound = true;
1387 : }
1388 : #endif // GET_OUT
1389 :
1390 10 : GroundTemp::ModelType gtmType = static_cast<GroundTemp::ModelType>(getEnumValue(GroundTemp::modelTypeNamesUC, s_ipsc->cAlphaArgs(2)));
1391 10 : if (gtmType == GroundTemp::ModelType::Invalid) {
1392 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
1393 0 : ErrorsFound = true;
1394 : }
1395 : // Farfield ground temperature model -- note this will overwrite the DataIPShortCuts variables
1396 : // so any other processing below this line won't have access to the cAlphaArgs, etc., here
1397 10 : thisDomain.groundTempModel = GroundTemp::GetGroundTempModelAndInit(state, gtmType, s_ipsc->cAlphaArgs(3));
1398 :
1399 : // Total surface area
1400 10 : Real64 ThisArea = 0.0;
1401 19 : for (auto const &z : thisDomain.ZoneCoupledSurfaces) {
1402 9 : ThisArea += z.SurfaceArea;
1403 : }
1404 :
1405 : // Surface dimensions
1406 10 : thisDomain.BasementZone.Width = sqrt(ThisArea / thisAspectRatio);
1407 10 : thisDomain.BasementZone.Length = thisDomain.BasementZone.Width * thisAspectRatio;
1408 :
1409 : // Set ground domain dimensions
1410 : // get width and length from aspect ratio later
1411 10 : thisDomain.Extents.xMax = thisDomain.PerimeterOffset + thisDomain.BasementZone.Width / 2;
1412 10 : thisDomain.Extents.zMax = thisDomain.PerimeterOffset + thisDomain.BasementZone.Length / 2;
1413 :
1414 : // Check horizontal insulation width so as to prevent overlapping insulation. VertInsThickness is used here since it is used for vertical
1415 : // partition thickness.
1416 10 : if (thisDomain.HorizIns == HorizInsulation::Perimeter && ThisArea > 0.0) {
1417 8 : if ((thisDomain.HorizInsWidth + thisDomain.VertInsThickness) > thisDomain.BasementZone.Width / 2.0 ||
1418 8 : (thisDomain.HorizInsWidth + thisDomain.VertInsThickness) > thisDomain.BasementZone.Length / 2.0) {
1419 0 : ShowContinueError(state, format("{}: Perimeter insulation width is too large.", routineName));
1420 0 : ShowContinueError(state, "This would cause overlapping insulation. Check inputs.");
1421 0 : ShowContinueError(state, "Defaulting to full horizontal insulation.");
1422 0 : ShowContinueError(state, format("Found in: {}", thisDomain.Name));
1423 0 : thisDomain.HorizIns = HorizInsulation::Full;
1424 : }
1425 : }
1426 :
1427 : //******* We'll first set up the domain ********
1428 10 : thisDomain.IsActuallyPartOfAHorizontalTrench = false;
1429 10 : thisDomain.HasAPipeCircuit = false;
1430 10 : thisDomain.HasZoneCoupledSlab = false;
1431 10 : thisDomain.HasBasement = false;
1432 10 : thisDomain.HasZoneCoupledBasement = true;
1433 :
1434 : // setup output variables
1435 10 : thisDomain.SetupZoneCoupledOutputVariables(state);
1436 : }
1437 10 : }
1438 :
1439 42 : bool SiteGroundDomainUsingNoMassMat([[maybe_unused]] EnergyPlusData &state, Real64 const MaterialThickness, int const MaterialNum)
1440 : {
1441 :
1442 42 : if ((MaterialThickness <= 0.0) || (state.dataMaterial->materials(MaterialNum)->ROnly)) {
1443 3 : return true;
1444 : } else {
1445 39 : return false;
1446 : }
1447 : }
1448 :
1449 0 : void SiteGroundDomainNoMassMatError(EnergyPlusData &state,
1450 : std::string_view FieldName,
1451 : std::string const &UserInputField,
1452 : std::string const &ObjectName)
1453 : {
1454 :
1455 0 : ShowSevereError(state, format("Invalid {}={} was found in: {}", FieldName, UserInputField, ObjectName));
1456 0 : ShowContinueError(
1457 : state, "The user of no mass materials or ones with no thickness are not allowed for the insulation fields of the following objects:");
1458 0 : ShowContinueError(state, format(" {} or {}", ObjName_ZoneCoupled_Slab, ObjName_ZoneCoupled_Basement));
1459 0 : ShowContinueError(
1460 : state, "Change any insulation designations in these objects from no mass materials to regular materials that have a thickness, etc.");
1461 0 : }
1462 :
1463 2 : void ReadPipeCircuitInputs(EnergyPlusData &state, bool &ErrorsFound)
1464 : {
1465 :
1466 : // SUBROUTINE INFORMATION:
1467 : // AUTHOR Edwin Lee
1468 : // DATE WRITTEN Summer 2011
1469 : // MODIFIED na
1470 : // RE-ENGINEERED na
1471 :
1472 : // SUBROUTINE PARAMETER DEFINITIONS:
1473 : static constexpr std::string_view routineName = "ReadPipeCircuitInputs";
1474 :
1475 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1476 : int NumAlphas;
1477 : int NumNumbers;
1478 : int IOStatus;
1479 : int CurIndex;
1480 :
1481 2 : auto &s_ipsc = state.dataIPShortCut;
1482 : // get all of the actual generalized pipe circuit objects
1483 :
1484 2 : int NumPipeCircuits = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_Circuit);
1485 4 : for (int PipeCircuitCounter = 1; PipeCircuitCounter <= NumPipeCircuits; ++PipeCircuitCounter) {
1486 :
1487 : // Read all the inputs for this pipe circuit
1488 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1489 : ObjName_Circuit,
1490 : PipeCircuitCounter,
1491 2 : s_ipsc->cAlphaArgs,
1492 : NumAlphas,
1493 2 : s_ipsc->rNumericArgs,
1494 : NumNumbers,
1495 : IOStatus,
1496 2 : s_ipsc->lNumericFieldBlanks,
1497 2 : s_ipsc->lAlphaFieldBlanks,
1498 2 : s_ipsc->cAlphaFieldNames,
1499 2 : s_ipsc->cNumericFieldNames);
1500 :
1501 2 : Circuit thisCircuit = Circuit();
1502 :
1503 : // Get the name, validate
1504 2 : thisCircuit.Name = s_ipsc->cAlphaArgs(1);
1505 2 : Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
1506 :
1507 : // Read pipe thermal properties, validated by IP
1508 2 : thisCircuit.PipeProperties.Conductivity = s_ipsc->rNumericArgs(1);
1509 2 : thisCircuit.PipeProperties.Density = s_ipsc->rNumericArgs(2);
1510 2 : thisCircuit.PipeProperties.SpecificHeat = s_ipsc->rNumericArgs(3);
1511 :
1512 : // Read pipe sizing, validated individually by IP, validated comparison here
1513 2 : thisCircuit.PipeSize.InnerDia = s_ipsc->rNumericArgs(4);
1514 2 : thisCircuit.PipeSize.OuterDia = s_ipsc->rNumericArgs(5);
1515 2 : if (thisCircuit.PipeSize.InnerDia >= thisCircuit.PipeSize.OuterDia) {
1516 1 : CurIndex = 5;
1517 3 : IssueSevereInputFieldError(state,
1518 : routineName,
1519 : ObjName_Circuit,
1520 1 : s_ipsc->cAlphaArgs(1),
1521 1 : s_ipsc->cNumericFieldNames(CurIndex),
1522 1 : s_ipsc->rNumericArgs(CurIndex),
1523 : "Outer diameter must be greater than inner diameter.",
1524 : ErrorsFound);
1525 : }
1526 :
1527 : // Read design flow rate, validated positive by IP
1528 2 : thisCircuit.DesignVolumeFlowRate = s_ipsc->rNumericArgs(6);
1529 :
1530 : // Read inlet and outlet node names and validate them
1531 2 : thisCircuit.InletNodeName = s_ipsc->cAlphaArgs(2);
1532 2 : thisCircuit.InletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1533 2 : s_ipsc->cAlphaArgs(2),
1534 : ErrorsFound,
1535 : DataLoopNode::ConnectionObjectType::PipingSystemUndergroundPipeCircuit,
1536 2 : s_ipsc->cAlphaArgs(1),
1537 : DataLoopNode::NodeFluidType::Water,
1538 : DataLoopNode::ConnectionType::Inlet,
1539 : NodeInputManager::CompFluidStream::Primary,
1540 : DataLoopNode::ObjectIsNotParent);
1541 2 : if (thisCircuit.InletNodeNum == 0) {
1542 0 : CurIndex = 2;
1543 0 : IssueSevereInputFieldError(state,
1544 : routineName,
1545 : ObjName_Circuit,
1546 0 : s_ipsc->cAlphaArgs(1),
1547 0 : s_ipsc->cAlphaFieldNames(CurIndex),
1548 0 : s_ipsc->cAlphaArgs(CurIndex),
1549 : "Bad node name.",
1550 : ErrorsFound);
1551 : }
1552 2 : thisCircuit.OutletNodeName = s_ipsc->cAlphaArgs(3);
1553 2 : thisCircuit.OutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1554 2 : s_ipsc->cAlphaArgs(3),
1555 : ErrorsFound,
1556 : DataLoopNode::ConnectionObjectType::PipingSystemUndergroundPipeCircuit,
1557 2 : s_ipsc->cAlphaArgs(1),
1558 : DataLoopNode::NodeFluidType::Water,
1559 : DataLoopNode::ConnectionType::Outlet,
1560 : NodeInputManager::CompFluidStream::Primary,
1561 : DataLoopNode::ObjectIsNotParent);
1562 2 : if (thisCircuit.OutletNodeNum == 0) {
1563 0 : CurIndex = 3;
1564 0 : IssueSevereInputFieldError(state,
1565 : routineName,
1566 : ObjName_Circuit,
1567 0 : s_ipsc->cAlphaArgs(1),
1568 0 : s_ipsc->cAlphaFieldNames(CurIndex),
1569 0 : s_ipsc->cAlphaArgs(CurIndex),
1570 : "Bad node name.",
1571 : ErrorsFound);
1572 : }
1573 4 : BranchNodeConnections::TestCompSet(
1574 2 : state, ObjName_Circuit, s_ipsc->cAlphaArgs(1), s_ipsc->cAlphaArgs(2), s_ipsc->cAlphaArgs(3), "Piping System Circuit Nodes");
1575 :
1576 : // Convergence tolerance values, validated by IP
1577 2 : thisCircuit.Convergence_CurrentToPrevIteration = s_ipsc->rNumericArgs(7);
1578 2 : thisCircuit.MaxIterationsPerTS = static_cast<int>(s_ipsc->rNumericArgs(8));
1579 :
1580 : // Radial mesh inputs, validated by IP
1581 : // -- mesh thickness should be considered slightly dangerous until mesh dev engine can trap erroneous values
1582 2 : thisCircuit.NumRadialCells = static_cast<int>(s_ipsc->rNumericArgs(9));
1583 2 : thisCircuit.RadialMeshThickness = s_ipsc->rNumericArgs(10);
1584 :
1585 : // Read number of pipe segments for this circuit, allocate arrays
1586 2 : int const NumPipeSegments = static_cast<int>(s_ipsc->rNumericArgs(11));
1587 :
1588 : // Need to loop once to store the names ahead of time because calling the segment factory will override cAlphaArgs
1589 2 : std::vector<std::string> segmentNamesToFind;
1590 2 : int constexpr NumAlphasBeforeSegmentOne = 3;
1591 6 : for (int ThisCircuitPipeSegmentCounter = 1; ThisCircuitPipeSegmentCounter <= NumPipeSegments; ++ThisCircuitPipeSegmentCounter) {
1592 4 : CurIndex = ThisCircuitPipeSegmentCounter + NumAlphasBeforeSegmentOne;
1593 4 : if (s_ipsc->lAlphaFieldBlanks(CurIndex)) {
1594 0 : IssueSevereInputFieldError(state,
1595 : routineName,
1596 : ObjName_Circuit,
1597 0 : s_ipsc->cAlphaArgs(1),
1598 0 : s_ipsc->cAlphaFieldNames(CurIndex),
1599 0 : s_ipsc->cAlphaArgs(CurIndex),
1600 : "Expected a pipe segment name, check pipe segment count input field.",
1601 : ErrorsFound);
1602 : }
1603 4 : segmentNamesToFind.push_back(s_ipsc->cAlphaArgs(CurIndex));
1604 : }
1605 : // then we can loop through and allow the factory to be called and carry on
1606 6 : for (auto &segmentNameToFind : segmentNamesToFind) {
1607 4 : thisCircuit.pipeSegments.push_back(Segment::factory(state, segmentNameToFind));
1608 : }
1609 :
1610 2 : state.dataPlantPipingSysMgr->circuits.push_back(thisCircuit);
1611 :
1612 2 : } // All pipe circuits in input
1613 :
1614 : // now get all the pipe circuits related to horizontal trenches
1615 :
1616 2 : int NumHorizontalTrenches = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_HorizTrench);
1617 :
1618 : // Read in all pipe segments
1619 3 : for (int HorizontalGHXCtr = 1; HorizontalGHXCtr <= NumHorizontalTrenches; ++HorizontalGHXCtr) {
1620 :
1621 : // Read all inputs for this pipe segment
1622 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1623 : ObjName_HorizTrench,
1624 : HorizontalGHXCtr,
1625 1 : s_ipsc->cAlphaArgs,
1626 : NumAlphas,
1627 1 : s_ipsc->rNumericArgs,
1628 : NumNumbers,
1629 : IOStatus,
1630 1 : s_ipsc->lNumericFieldBlanks,
1631 1 : s_ipsc->lAlphaFieldBlanks,
1632 1 : s_ipsc->cAlphaFieldNames,
1633 1 : s_ipsc->cNumericFieldNames);
1634 1 : std::string thisTrenchName = s_ipsc->cAlphaArgs(1);
1635 :
1636 1 : Circuit thisCircuit;
1637 1 : thisCircuit.IsActuallyPartOfAHorizontalTrench = true;
1638 1 : thisCircuit.Name = thisTrenchName;
1639 :
1640 : // Read pipe thermal properties
1641 1 : thisCircuit.PipeProperties.Conductivity = s_ipsc->rNumericArgs(11);
1642 1 : thisCircuit.PipeProperties.Density = s_ipsc->rNumericArgs(12);
1643 1 : thisCircuit.PipeProperties.SpecificHeat = s_ipsc->rNumericArgs(13);
1644 :
1645 : // Pipe sizing
1646 1 : thisCircuit.PipeSize.InnerDia = s_ipsc->rNumericArgs(5);
1647 1 : thisCircuit.PipeSize.OuterDia = s_ipsc->rNumericArgs(6);
1648 :
1649 : // Issue a severe if Inner >= Outer diameter
1650 1 : if (thisCircuit.PipeSize.InnerDia >= thisCircuit.PipeSize.OuterDia) {
1651 1 : ShowSevereError(state, format("{}: {}=\"{}\" has invalid pipe diameters.", routineName, ObjName_HorizTrench, s_ipsc->cAlphaArgs(1)));
1652 2 : ShowContinueError(state,
1653 2 : format("Outer diameter [{:.3T}] must be greater than inner diameter [{:.3T}].",
1654 : thisCircuit.PipeSize.OuterDia,
1655 : thisCircuit.PipeSize.InnerDia));
1656 1 : ErrorsFound = true;
1657 : }
1658 :
1659 : // Read design flow rate, validated positive by IP
1660 1 : thisCircuit.DesignVolumeFlowRate = s_ipsc->rNumericArgs(1);
1661 :
1662 : // Read inlet and outlet node names and validate them
1663 1 : thisCircuit.InletNodeName = s_ipsc->cAlphaArgs(2);
1664 1 : thisCircuit.InletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1665 : thisCircuit.InletNodeName,
1666 : ErrorsFound,
1667 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerHorizontalTrench,
1668 : thisTrenchName,
1669 : DataLoopNode::NodeFluidType::Water,
1670 : DataLoopNode::ConnectionType::Inlet,
1671 : NodeInputManager::CompFluidStream::Primary,
1672 : DataLoopNode::ObjectIsNotParent);
1673 1 : thisCircuit.OutletNodeName = s_ipsc->cAlphaArgs(3);
1674 1 : thisCircuit.OutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1675 : thisCircuit.OutletNodeName,
1676 : ErrorsFound,
1677 : DataLoopNode::ConnectionObjectType::GroundHeatExchangerHorizontalTrench,
1678 : thisTrenchName,
1679 : DataLoopNode::NodeFluidType::Water,
1680 : DataLoopNode::ConnectionType::Outlet,
1681 : NodeInputManager::CompFluidStream::Primary,
1682 : DataLoopNode::ObjectIsNotParent);
1683 1 : BranchNodeConnections::TestCompSet(
1684 : state, ObjName_HorizTrench, thisTrenchName, thisCircuit.InletNodeName, thisCircuit.OutletNodeName, "Piping System Circuit Nodes");
1685 :
1686 : // Convergence tolerance values, validated by IP
1687 1 : thisCircuit.Convergence_CurrentToPrevIteration = 0.001;
1688 1 : thisCircuit.MaxIterationsPerTS = 100;
1689 :
1690 : // Radial mesh inputs, validated by IP
1691 : // -- mesh thickness should be considered slightly dangerous until mesh dev engine can trap erroneous values
1692 1 : thisCircuit.NumRadialCells = 4;
1693 1 : thisCircuit.RadialMeshThickness = thisCircuit.PipeSize.InnerDia / 2.0;
1694 :
1695 : // add it to the main vector, then get a reference to it here
1696 1 : state.dataPlantPipingSysMgr->circuits.push_back(thisCircuit);
1697 1 : }
1698 2 : }
1699 :
1700 4 : Segment *Segment::factory(EnergyPlusData &state, const std::string &segmentName)
1701 : {
1702 4 : if (state.dataPlantPipingSysMgr->GetSegmentInputFlag) {
1703 2 : bool errorsFound = false;
1704 2 : ReadPipeSegmentInputs(state, errorsFound);
1705 2 : state.dataPlantPipingSysMgr->GetSegmentInputFlag = false;
1706 : }
1707 : // Now look for this particular segment in the list
1708 6 : for (auto &segment : state.dataPlantPipingSysMgr->segments) {
1709 6 : if (segment.Name == segmentName) {
1710 4 : return &segment;
1711 : }
1712 : }
1713 : // If we didn't find it, fatal
1714 : ShowFatalError(state, format("PipeSegmentInfoFactory: Error getting inputs for segment named: {}", segmentName)); // LCOV_EXCL_LINE
1715 : // Shut up the compiler
1716 : return nullptr; // LCOV_EXCL_LINE
1717 : }
1718 :
1719 1 : Circuit *Circuit::factory(EnergyPlusData &state, const std::string &circuitName, bool &errorsFound)
1720 : {
1721 1 : if (state.dataPlantPipingSysMgr->GetCircuitInputFlag) {
1722 1 : ReadPipeCircuitInputs(state, errorsFound);
1723 1 : state.dataPlantPipingSysMgr->GetCircuitInputFlag = false;
1724 : }
1725 : // Now look for this particular segment in the list
1726 1 : for (auto &circuit : state.dataPlantPipingSysMgr->circuits) {
1727 1 : if (circuit.Name == circuitName) {
1728 1 : return &circuit;
1729 : }
1730 : }
1731 : // If we didn't find it, fatal
1732 : ShowFatalError(state, format("PipeCircuitInfoFactory: Error getting inputs for circuit named: {}", circuitName)); // LCOV_EXCL_LINE
1733 : // Shut up the compiler
1734 : return nullptr; // LCOV_EXCL_LINE
1735 : }
1736 :
1737 2 : void ReadPipeSegmentInputs(EnergyPlusData &state, bool &ErrorsFound)
1738 : {
1739 :
1740 : // SUBROUTINE INFORMATION:
1741 : // AUTHOR Edwin Lee
1742 : // DATE WRITTEN Summer 2011
1743 : // MODIFIED na
1744 : // RE-ENGINEERED na
1745 :
1746 : // SUBROUTINE PARAMETER DEFINITIONS:
1747 : static constexpr std::string_view RoutineName("ReadPipeSegmentInputs");
1748 :
1749 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1750 : int NumAlphas; // Number of Alphas for each GetObjectItem call
1751 : int NumNumbers; // Number of Numbers for each GetObjectItem call
1752 : int IOStatus; // Used in GetObjectItem
1753 : int CurIndex;
1754 :
1755 2 : auto &s_ipsc = state.dataIPShortCut;
1756 :
1757 : // Read in all pipe segments
1758 2 : int NumPipeSegmentsInInput = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_Segment);
1759 6 : for (int SegmentCtr = 1; SegmentCtr <= NumPipeSegmentsInInput; ++SegmentCtr) {
1760 :
1761 : // Read all inputs for this pipe segment
1762 8 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1763 : ObjName_Segment,
1764 : SegmentCtr,
1765 4 : s_ipsc->cAlphaArgs,
1766 : NumAlphas,
1767 4 : s_ipsc->rNumericArgs,
1768 : NumNumbers,
1769 : IOStatus,
1770 4 : s_ipsc->lNumericFieldBlanks,
1771 4 : s_ipsc->lAlphaFieldBlanks,
1772 4 : s_ipsc->cAlphaFieldNames,
1773 4 : s_ipsc->cNumericFieldNames);
1774 :
1775 4 : Segment thisSegment;
1776 :
1777 : // Get the name, validate
1778 4 : thisSegment.Name = s_ipsc->cAlphaArgs(1);
1779 4 : Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
1780 : // Read in the pipe location, validated as positive by IP
1781 : // -- note that these values will be altered by the main GetInput routine in two ways:
1782 : // 1) shift for basement wall if selected
1783 : // 2) invert y direction to be measured from domain bottom surface for calculations
1784 4 : thisSegment.PipeLocation = PointF(s_ipsc->rNumericArgs(1), s_ipsc->rNumericArgs(2));
1785 :
1786 : // Read in the flow direction
1787 4 : thisSegment.FlowDirection = static_cast<SegmentFlow>(getEnumValue(flowDirectionNamesUC, stripped(s_ipsc->cAlphaArgs(2))));
1788 4 : if (thisSegment.FlowDirection == SegmentFlow::Invalid) {
1789 0 : CurIndex = 2;
1790 0 : IssueSevereInputFieldError(state,
1791 : RoutineName,
1792 : ObjName_Segment,
1793 0 : s_ipsc->cAlphaArgs(1),
1794 0 : s_ipsc->cAlphaFieldNames(CurIndex),
1795 0 : s_ipsc->cAlphaArgs(CurIndex),
1796 : "Invalid flow direction, use one of the available keys.",
1797 : ErrorsFound);
1798 : }
1799 :
1800 4 : state.dataPlantPipingSysMgr->segments.push_back(thisSegment);
1801 4 : }
1802 2 : }
1803 :
1804 1 : void ReadHorizontalTrenchInputs(EnergyPlusData &state,
1805 : int const StartingDomainNumForHorizontal,
1806 : int const StartingCircuitNumForHorizontal,
1807 : bool &ErrorsFound)
1808 : {
1809 :
1810 : // SUBROUTINE INFORMATION:
1811 : // AUTHOR Edwin Lee
1812 : // DATE WRITTEN September 2012
1813 : // MODIFIED na
1814 : // RE-ENGINEERED na
1815 :
1816 1 : constexpr std::string_view routineName = "ReadHorizontalTrenchInputs";
1817 :
1818 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1819 : int NumAlphas; // Number of Alphas for each GetObjectItem call
1820 : int NumNumbers; // Number of Numbers for each GetObjectItem call
1821 : int IOStatus; // Used in GetObjectItem
1822 :
1823 1 : auto &s_ipsc = state.dataIPShortCut;
1824 :
1825 : // initialize these counters properly so they can be incremented within the DO loop
1826 1 : int DomainCtr = StartingDomainNumForHorizontal - 1;
1827 1 : int CircuitCtr = StartingCircuitNumForHorizontal - 1;
1828 :
1829 : // For each horizontal, we need to process the inputs into a local array of derived type,
1830 : // then resolve each one, creating definitions for a pipe domain, pipe circuit, and series of pipe segments
1831 : // This way, the outer get input routines can handle it as though they were generalized routines
1832 :
1833 1 : int NumHorizontalTrenches = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ObjName_HorizTrench);
1834 :
1835 : // Read in all pipe segments
1836 1 : for (int HorizontalGHXCtr = 1; HorizontalGHXCtr <= NumHorizontalTrenches; ++HorizontalGHXCtr) {
1837 :
1838 : // Increment the domain and circuit counters here
1839 0 : ++DomainCtr;
1840 0 : ++CircuitCtr;
1841 :
1842 : // Read all inputs for this pipe segment
1843 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1844 : ObjName_HorizTrench,
1845 : HorizontalGHXCtr,
1846 0 : s_ipsc->cAlphaArgs,
1847 : NumAlphas,
1848 0 : s_ipsc->rNumericArgs,
1849 : NumNumbers,
1850 : IOStatus,
1851 0 : s_ipsc->lNumericFieldBlanks,
1852 0 : s_ipsc->lAlphaFieldBlanks,
1853 0 : s_ipsc->cAlphaFieldNames,
1854 0 : s_ipsc->cNumericFieldNames);
1855 :
1856 0 : auto &thisDomain = state.dataPlantPipingSysMgr->domains[DomainCtr - 1];
1857 :
1858 0 : ErrorObjectHeader eoh{routineName, ObjName_HorizTrench, s_ipsc->cAlphaArgs(1)};
1859 :
1860 : // Get the name, validate
1861 0 : std::string thisTrenchName = s_ipsc->cAlphaArgs(1);
1862 0 : Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
1863 :
1864 0 : int const NumPipeSegments = static_cast<int>(s_ipsc->rNumericArgs(3));
1865 0 : Real64 const thisInterPipeSpacing = s_ipsc->rNumericArgs(4);
1866 0 : Real64 const thisBurialDepth = s_ipsc->rNumericArgs(7);
1867 :
1868 : //******* We'll first set up the domain ********
1869 : // the extents will be: zMax = axial length; yMax = burial depth*2; xMax = ( NumPipes+1 )*HorizontalPipeSpacing
1870 0 : thisDomain.IsActuallyPartOfAHorizontalTrench = true;
1871 0 : thisDomain.Name = format("HorizontalTrenchDomain{:4}", HorizontalGHXCtr);
1872 0 : thisDomain.Extents.xMax = (double(NumPipeSegments) + 1.0) * thisInterPipeSpacing;
1873 0 : thisDomain.Extents.yMax = 2.0 * thisBurialDepth;
1874 0 : thisDomain.Extents.zMax = s_ipsc->rNumericArgs(2);
1875 :
1876 : // set up the mesh with some default parameters
1877 0 : thisDomain.Mesh.X.RegionMeshCount = 4;
1878 0 : thisDomain.Mesh.X.thisMeshDistribution = MeshDistribution::Uniform;
1879 0 : thisDomain.Mesh.Y.RegionMeshCount = 4;
1880 0 : thisDomain.Mesh.Y.thisMeshDistribution = MeshDistribution::Uniform;
1881 0 : thisDomain.Mesh.Z.RegionMeshCount = 4;
1882 0 : thisDomain.Mesh.Z.thisMeshDistribution = MeshDistribution::Uniform;
1883 :
1884 : // Soil properties
1885 0 : thisDomain.GroundProperties.Conductivity = s_ipsc->rNumericArgs(8);
1886 0 : thisDomain.GroundProperties.Density = s_ipsc->rNumericArgs(9);
1887 0 : thisDomain.GroundProperties.SpecificHeat = s_ipsc->rNumericArgs(10);
1888 :
1889 : // Moisture properties
1890 0 : thisDomain.Moisture.Theta_liq = s_ipsc->rNumericArgs(14) / 100.0;
1891 0 : thisDomain.Moisture.Theta_sat = s_ipsc->rNumericArgs(15) / 100.0;
1892 :
1893 : // Other parameters
1894 0 : thisDomain.SimControls.Convergence_CurrentToPrevIteration = 0.001;
1895 0 : thisDomain.SimControls.MaxIterationsPerTS = 250;
1896 :
1897 : // additional evapotranspiration parameter, min/max validated by IP
1898 0 : thisDomain.Moisture.GroundCoverCoefficient = s_ipsc->rNumericArgs(16);
1899 :
1900 : //******* We'll next set up the circuit ********
1901 : // then we can loop through and allow the factory to be called and carry on
1902 0 : thisDomain.circuits.push_back(Circuit::factory(state, thisTrenchName, ErrorsFound));
1903 :
1904 0 : GroundTemp::ModelType gtmType = static_cast<GroundTemp::ModelType>(getEnumValue(GroundTemp::modelTypeNamesUC, s_ipsc->cAlphaArgs(4)));
1905 0 : if (gtmType == GroundTemp::ModelType::Invalid) {
1906 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
1907 0 : ErrorsFound = true;
1908 : }
1909 :
1910 : // Farfield model parameters -- this is pushed down pretty low because it internally calls GetObjectItem
1911 : // using DataIPShortCuts, so it will overwrite the cAlphaArgs and rNumericArgs values
1912 0 : thisDomain.groundTempModel = GroundTemp::GetGroundTempModelAndInit(state, gtmType, s_ipsc->cAlphaArgs(5));
1913 :
1914 : //******* Then we'll do the segments *******!
1915 0 : for (int ThisCircuitPipeSegmentCounter = 1; ThisCircuitPipeSegmentCounter <= NumPipeSegments; ++ThisCircuitPipeSegmentCounter) {
1916 0 : Segment segment;
1917 0 : segment.Name = format("HorizontalTrenchCircuit{}Segment{}", HorizontalGHXCtr, ThisCircuitPipeSegmentCounter);
1918 0 : segment.IsActuallyPartOfAHorizontalTrench = true;
1919 0 : segment.PipeLocation = PointF(ThisCircuitPipeSegmentCounter * thisInterPipeSpacing, thisBurialDepth);
1920 :
1921 0 : if (mod(ThisCircuitPipeSegmentCounter, 2) != 0) {
1922 0 : segment.FlowDirection = SegmentFlow::IncreasingZ;
1923 : } else {
1924 0 : segment.FlowDirection = SegmentFlow::DecreasingZ;
1925 : }
1926 : // add it to the main segment array so it has a place to live
1927 0 : state.dataPlantPipingSysMgr->segments.push_back(segment);
1928 0 : }
1929 :
1930 : // now that they are in the main vector, add them here
1931 0 : int const newSizeSegmentVector = static_cast<int>(state.dataPlantPipingSysMgr->segments.size());
1932 0 : for (int segmentIndexToGrab = newSizeSegmentVector - NumPipeSegments; segmentIndexToGrab < newSizeSegmentVector; ++segmentIndexToGrab) {
1933 0 : thisDomain.circuits[0]->pipeSegments.push_back(&state.dataPlantPipingSysMgr->segments[segmentIndexToGrab]);
1934 : }
1935 0 : }
1936 1 : }
1937 :
1938 1 : void SetupPipingSystemOutputVariables(EnergyPlusData &state)
1939 : {
1940 :
1941 : // SUBROUTINE INFORMATION:
1942 : // AUTHOR Edwin Lee
1943 : // DATE WRITTEN September 2012
1944 : // MODIFIED na
1945 : // RE-ENGINEERED na
1946 :
1947 3 : for (auto &thisSegment : state.dataPlantPipingSysMgr->segments) {
1948 :
1949 2 : if (!thisSegment.IsActuallyPartOfAHorizontalTrench) {
1950 :
1951 4 : SetupOutputVariable(state,
1952 : "Pipe Segment Inlet Temperature",
1953 : Constant::Units::C,
1954 2 : thisSegment.InletTemperature,
1955 : OutputProcessor::TimeStepType::System,
1956 : OutputProcessor::StoreType::Average,
1957 2 : thisSegment.Name);
1958 4 : SetupOutputVariable(state,
1959 : "Pipe Segment Outlet Temperature",
1960 : Constant::Units::C,
1961 2 : thisSegment.OutletTemperature,
1962 : OutputProcessor::TimeStepType::System,
1963 : OutputProcessor::StoreType::Average,
1964 2 : thisSegment.Name);
1965 :
1966 4 : SetupOutputVariable(state,
1967 : "Pipe Segment Fluid Heat Transfer Rate",
1968 : Constant::Units::W,
1969 2 : thisSegment.FluidHeatLoss,
1970 : OutputProcessor::TimeStepType::System,
1971 : OutputProcessor::StoreType::Average,
1972 2 : thisSegment.Name);
1973 : }
1974 : }
1975 :
1976 2 : for (auto &thisCircuit : state.dataPlantPipingSysMgr->circuits) {
1977 :
1978 1 : if (!thisCircuit.IsActuallyPartOfAHorizontalTrench) {
1979 :
1980 2 : SetupOutputVariable(state,
1981 : "Pipe Circuit Mass Flow Rate",
1982 : Constant::Units::kg_s,
1983 1 : thisCircuit.CurCircuitFlowRate,
1984 : OutputProcessor::TimeStepType::System,
1985 : OutputProcessor::StoreType::Average,
1986 1 : thisCircuit.Name);
1987 :
1988 2 : SetupOutputVariable(state,
1989 : "Pipe Circuit Inlet Temperature",
1990 : Constant::Units::C,
1991 1 : thisCircuit.InletTemperature,
1992 : OutputProcessor::TimeStepType::System,
1993 : OutputProcessor::StoreType::Average,
1994 1 : thisCircuit.Name);
1995 2 : SetupOutputVariable(state,
1996 : "Pipe Circuit Outlet Temperature",
1997 : Constant::Units::C,
1998 1 : thisCircuit.OutletTemperature,
1999 : OutputProcessor::TimeStepType::System,
2000 : OutputProcessor::StoreType::Average,
2001 1 : thisCircuit.Name);
2002 :
2003 2 : SetupOutputVariable(state,
2004 : "Pipe Circuit Fluid Heat Transfer Rate",
2005 : Constant::Units::W,
2006 1 : thisCircuit.FluidHeatLoss,
2007 : OutputProcessor::TimeStepType::System,
2008 : OutputProcessor::StoreType::Average,
2009 1 : thisCircuit.Name);
2010 :
2011 : } else { // it is a horizontal trench
2012 :
2013 0 : SetupOutputVariable(state,
2014 : "Ground Heat Exchanger Mass Flow Rate",
2015 : Constant::Units::kg_s,
2016 0 : thisCircuit.CurCircuitFlowRate,
2017 : OutputProcessor::TimeStepType::System,
2018 : OutputProcessor::StoreType::Average,
2019 0 : thisCircuit.Name);
2020 :
2021 0 : SetupOutputVariable(state,
2022 : "Ground Heat Exchanger Inlet Temperature",
2023 : Constant::Units::C,
2024 0 : thisCircuit.InletTemperature,
2025 : OutputProcessor::TimeStepType::System,
2026 : OutputProcessor::StoreType::Average,
2027 0 : thisCircuit.Name);
2028 0 : SetupOutputVariable(state,
2029 : "Ground Heat Exchanger Outlet Temperature",
2030 : Constant::Units::C,
2031 0 : thisCircuit.OutletTemperature,
2032 : OutputProcessor::TimeStepType::System,
2033 : OutputProcessor::StoreType::Average,
2034 0 : thisCircuit.Name);
2035 :
2036 0 : SetupOutputVariable(state,
2037 : "Ground Heat Exchanger Fluid Heat Transfer Rate",
2038 : Constant::Units::W,
2039 0 : thisCircuit.FluidHeatLoss,
2040 : OutputProcessor::TimeStepType::System,
2041 : OutputProcessor::StoreType::Average,
2042 0 : thisCircuit.Name);
2043 : }
2044 : }
2045 1 : }
2046 :
2047 24 : void Domain::SetupZoneCoupledOutputVariables(EnergyPlusData &state)
2048 : {
2049 :
2050 : // SUBROUTINE INFORMATION:
2051 : // AUTHOR Matt Mitchell
2052 : // DATE WRITTEN August 2014
2053 : // MODIFIED na
2054 : // RE-ENGINEERED na
2055 :
2056 24 : if (this->HasZoneCoupledSlab) {
2057 : // Zone-coupled slab outputs
2058 28 : SetupOutputVariable(state,
2059 : "GroundDomain Slab Zone Coupled Surface Heat Flux",
2060 : Constant::Units::W_m2,
2061 14 : this->HeatFlux,
2062 : OutputProcessor::TimeStepType::Zone,
2063 : OutputProcessor::StoreType::Average,
2064 14 : this->Name);
2065 28 : SetupOutputVariable(state,
2066 : "GroundDomain Slab Zone Coupled Surface Temperature",
2067 : Constant::Units::C,
2068 14 : this->ZoneCoupledSurfaceTemp,
2069 : OutputProcessor::TimeStepType::Zone,
2070 : OutputProcessor::StoreType::Average,
2071 14 : this->Name);
2072 10 : } else if (this->HasZoneCoupledBasement) {
2073 : // Zone-coupled basement wall outputs
2074 20 : SetupOutputVariable(state,
2075 : "GroundDomain Basement Wall Interface Heat Flux",
2076 : Constant::Units::W_m2,
2077 10 : this->WallHeatFlux,
2078 : OutputProcessor::TimeStepType::Zone,
2079 : OutputProcessor::StoreType::Average,
2080 10 : this->Name);
2081 20 : SetupOutputVariable(state,
2082 : "GroundDomain Basement Wall Interface Temperature",
2083 : Constant::Units::C,
2084 10 : this->BasementWallTemp,
2085 : OutputProcessor::TimeStepType::Zone,
2086 : OutputProcessor::StoreType::Average,
2087 10 : this->Name);
2088 : // Zone-coupled basement floor outputs
2089 20 : SetupOutputVariable(state,
2090 : "GroundDomain Basement Floor Interface Heat Flux",
2091 : Constant::Units::W_m2,
2092 10 : this->FloorHeatFlux,
2093 : OutputProcessor::TimeStepType::Zone,
2094 : OutputProcessor::StoreType::Average,
2095 10 : this->Name);
2096 20 : SetupOutputVariable(state,
2097 : "GroundDomain Basement Floor Interface Temperature",
2098 : Constant::Units::C,
2099 10 : this->BasementFloorTemp,
2100 : OutputProcessor::TimeStepType::Zone,
2101 : OutputProcessor::StoreType::Average,
2102 10 : this->Name);
2103 : }
2104 24 : }
2105 :
2106 1 : void Domain::InitPipingSystems(EnergyPlusData &state, Circuit *thisCircuit)
2107 : {
2108 :
2109 : // SUBROUTINE INFORMATION:
2110 : // AUTHOR Edwin Lee
2111 : // DATE WRITTEN Summer 2011
2112 : // MODIFIED na
2113 : // RE-ENGINEERED na
2114 :
2115 : // SUBROUTINE PARAMETER DEFINITIONS:
2116 : static constexpr std::string_view RoutineName("InitPipingSystems");
2117 :
2118 : // Do any one-time initializations
2119 1 : if (thisCircuit->NeedToFindOnPlantLoop) {
2120 :
2121 : DataPlant::PlantEquipmentType TypeToLookFor;
2122 1 : if (thisCircuit->IsActuallyPartOfAHorizontalTrench) {
2123 0 : TypeToLookFor = DataPlant::PlantEquipmentType::GrndHtExchgHorizTrench;
2124 : } else {
2125 1 : TypeToLookFor = DataPlant::PlantEquipmentType::PipingSystemPipeCircuit;
2126 : }
2127 :
2128 1 : bool errFlag = false;
2129 1 : PlantUtilities::ScanPlantLoopsForObject(state, thisCircuit->Name, TypeToLookFor, thisCircuit->plantLoc, errFlag, _, _, _, _, _);
2130 1 : if (errFlag) {
2131 0 : ShowFatalError(state, format("PipingSystems:{}: Program terminated due to previous condition(s).", RoutineName));
2132 : }
2133 :
2134 : // Once we find ourselves on the plant loop, we can do other things
2135 1 : Real64 rho = thisCircuit->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
2136 1 : thisCircuit->DesignMassFlowRate = thisCircuit->DesignVolumeFlowRate * rho;
2137 1 : thisCircuit->NeedToFindOnPlantLoop = false;
2138 : }
2139 :
2140 1 : if (this->DomainNeedsToBeMeshed) {
2141 :
2142 1 : this->developMesh(state);
2143 :
2144 : // would be OK to do some post-mesh error handling here I think
2145 2 : for (auto const &thisDomainCircuit : this->circuits) {
2146 3 : for (auto const &segment : thisDomainCircuit->pipeSegments) {
2147 2 : if (!segment->PipeCellCoordinatesSet) {
2148 0 : ShowSevereError(state, format("PipingSystems:{}:Pipe segment index not set.", RoutineName));
2149 0 : ShowContinueError(state, "...Possibly because pipe segment was placed outside of the domain.");
2150 0 : ShowContinueError(state, "...Verify piping system domain inputs, circuits, and segments.");
2151 0 : ShowFatalError(state, "Preceding error causes program termination");
2152 : }
2153 : }
2154 : }
2155 :
2156 1 : this->DomainNeedsToBeMeshed = false;
2157 : }
2158 :
2159 : // The time init should be done here before we DoOneTimeInits because the DoOneTimeInits
2160 : // includes a ground temperature initialization, which is based on the Cur%CurSimTimeSeconds variable
2161 : // which would be carried over from the previous environment
2162 1 : this->Cur.CurSimTimeStepSize = state.dataHVACGlobal->TimeStepSysSec;
2163 1 : this->Cur.CurSimTimeSeconds = (state.dataGlobal->DayOfSim - 1) * 24 + (state.dataGlobal->HourOfDay - 1) +
2164 1 : (state.dataGlobal->TimeStep - 1) * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
2165 :
2166 : // There are also some inits that are "close to one time" inits...(one-time in standalone, each envrn in E+)
2167 1 : if ((state.dataGlobal->BeginSimFlag && this->BeginSimInit) || (state.dataGlobal->BeginEnvrnFlag && this->BeginSimEnvironment)) {
2168 :
2169 : // this seemed to clean up a lot of reverse DD stuff because fluid thermal properties were
2170 : // being based on the inlet temperature, which wasn't updated until later
2171 1 : thisCircuit->CurCircuitInletTemp = state.dataLoopNodes->Node(thisCircuit->InletNodeNum).Temp;
2172 1 : thisCircuit->InletTemperature = thisCircuit->CurCircuitInletTemp;
2173 :
2174 1 : this->DoOneTimeInitializations(state, thisCircuit);
2175 :
2176 1 : this->BeginSimInit = false;
2177 1 : this->BeginSimEnvironment = false;
2178 : }
2179 1 : if (!state.dataGlobal->BeginSimFlag) this->BeginSimInit = true;
2180 1 : if (!state.dataGlobal->BeginEnvrnFlag) this->BeginSimEnvironment = true;
2181 :
2182 : // Shift history arrays only if necessary
2183 1 : if (std::abs(this->Cur.CurSimTimeSeconds - this->Cur.PrevSimTimeSeconds) > 1.0e-6) {
2184 1 : this->Cur.PrevSimTimeSeconds = this->Cur.CurSimTimeSeconds;
2185 1 : this->ShiftTemperaturesForNewTimeStep();
2186 1 : this->DomainNeedsSimulation = true;
2187 : }
2188 :
2189 : // Get the mass flow and inlet temperature to use for this time step
2190 1 : int InletNodeNum = thisCircuit->InletNodeNum;
2191 1 : int OutletNodeNum = thisCircuit->OutletNodeNum;
2192 1 : thisCircuit->CurCircuitInletTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
2193 :
2194 : // request design, set component flow will decide what to give us based on restrictions and flow lock status
2195 1 : thisCircuit->CurCircuitFlowRate = thisCircuit->DesignMassFlowRate;
2196 1 : PlantUtilities::SetComponentFlowRate(state, thisCircuit->CurCircuitFlowRate, InletNodeNum, OutletNodeNum, thisCircuit->plantLoc);
2197 1 : }
2198 :
2199 1 : void Domain::UpdatePipingSystems(EnergyPlusData &state, Circuit *thisCircuit)
2200 : {
2201 :
2202 : // SUBROUTINE INFORMATION:
2203 : // AUTHOR Edwin Lee
2204 : // DATE WRITTEN Summer 2011
2205 : // MODIFIED na
2206 : // RE-ENGINEERED na
2207 :
2208 1 : int OutletNodeNum = thisCircuit->OutletNodeNum;
2209 1 : auto const &out_cell = thisCircuit->CircuitOutletCell;
2210 1 : state.dataLoopNodes->Node(OutletNodeNum).Temp = this->Cells(out_cell.X, out_cell.Y, out_cell.Z).PipeCellData.Fluid.Temperature;
2211 1 : }
2212 :
2213 3 : void IssueSevereInputFieldError(EnergyPlusData &state,
2214 : std::string_view const RoutineName,
2215 : std::string const &ObjectName,
2216 : std::string const &InstanceName,
2217 : std::string_view FieldName,
2218 : std::string const &FieldEntry,
2219 : std::string const &Condition,
2220 : bool &ErrorsFound)
2221 : {
2222 :
2223 : // SUBROUTINE INFORMATION:
2224 : // AUTHOR Edwin Lee
2225 : // DATE WRITTEN Summer 2011
2226 : // MODIFIED na
2227 : // RE-ENGINEERED na
2228 :
2229 6 : ShowSevereError(
2230 6 : state, format("{}:{}=\"{}\", invalid {}=\"{}\", Condition: {}", RoutineName, ObjectName, InstanceName, FieldName, FieldEntry, Condition));
2231 3 : ErrorsFound = true;
2232 3 : }
2233 :
2234 1 : void IssueSevereInputFieldError(EnergyPlusData &state,
2235 : std::string_view const RoutineName,
2236 : std::string const &ObjectName,
2237 : std::string const &InstanceName,
2238 : std::string_view FieldName,
2239 : Real64 const FieldEntry,
2240 : std::string const &Condition,
2241 : bool &ErrorsFound)
2242 : {
2243 :
2244 : // SUBROUTINE INFORMATION:
2245 : // AUTHOR Edwin Lee
2246 : // DATE WRITTEN Summer 2011
2247 : // MODIFIED na
2248 : // RE-ENGINEERED na
2249 :
2250 2 : ShowSevereError(
2251 : state,
2252 2 : format(R"({}:{}="{}", invalid {}="{:.3T}", Condition: {})", RoutineName, ObjectName, InstanceName, FieldName, FieldEntry, Condition));
2253 1 : ErrorsFound = true;
2254 1 : }
2255 :
2256 13 : int GetSurfaceCountForOSCM(EnergyPlusData &state, int const OSCMIndex)
2257 : {
2258 :
2259 : // FUNCTION INFORMATION:
2260 : // AUTHOR Edwin Lee
2261 : // DATE WRITTEN Summer 2011
2262 : // MODIFIED na
2263 : // RE-ENGINEERED na
2264 :
2265 13 : int RetVal = 0;
2266 26 : for (int SurfCtr = 1; SurfCtr <= isize(state.dataSurface->Surface); ++SurfCtr) {
2267 13 : if (state.dataSurface->Surface(SurfCtr).OSCMPtr == OSCMIndex) ++RetVal;
2268 : }
2269 13 : return RetVal;
2270 : }
2271 :
2272 18 : std::vector<int> GetSurfaceIndecesForOSCM(EnergyPlusData &state, int const OSCMIndex)
2273 : {
2274 :
2275 : // FUNCTION INFORMATION:
2276 : // AUTHOR Edwin Lee
2277 : // DATE WRITTEN Summer 2011
2278 : // MODIFIED na
2279 : // RE-ENGINEERED na
2280 :
2281 18 : std::vector<int> retVal;
2282 36 : for (int SurfCtr = 1; SurfCtr <= isize(state.dataSurface->Surface); ++SurfCtr) {
2283 18 : if (state.dataSurface->Surface(SurfCtr).OSCMPtr == OSCMIndex) {
2284 18 : retVal.push_back(SurfCtr);
2285 : }
2286 : }
2287 18 : return retVal;
2288 0 : }
2289 :
2290 22 : std::vector<ZoneCoupledSurfaceData> GetSurfaceDataForOSCM(EnergyPlusData &state, int const OSCMIndex)
2291 : {
2292 :
2293 : // FUNCTION INFORMATION:
2294 : // AUTHOR Edwin Lee
2295 : // DATE WRITTEN Summer 2011
2296 : // MODIFIED na
2297 : // RE-ENGINEERED na
2298 :
2299 22 : std::vector<ZoneCoupledSurfaceData> RetVal;
2300 44 : for (int SurfCtr = 1; SurfCtr <= isize(state.dataSurface->Surface); ++SurfCtr) {
2301 22 : if (state.dataSurface->Surface(SurfCtr).OSCMPtr == OSCMIndex) {
2302 22 : ZoneCoupledSurfaceData z;
2303 22 : z.IndexInSurfaceArray = SurfCtr;
2304 22 : z.SurfaceArea = state.dataSurface->Surface(SurfCtr).Area;
2305 22 : z.Zone = state.dataSurface->Surface(SurfCtr).Zone;
2306 22 : RetVal.push_back(z);
2307 22 : }
2308 : }
2309 22 : return RetVal;
2310 0 : }
2311 :
2312 12 : void Segment::initPipeCells(int const x, int const y)
2313 : {
2314 :
2315 : // SUBROUTINE INFORMATION:
2316 : // AUTHOR Edwin Lee
2317 : // DATE WRITTEN Summer 2011
2318 : // MODIFIED na
2319 : // RE-ENGINEERED na
2320 :
2321 12 : this->PipeCellCoordinates.X = x;
2322 12 : this->PipeCellCoordinates.Y = y;
2323 12 : this->PipeCellCoordinatesSet = true;
2324 12 : }
2325 :
2326 1 : void Circuit::initInOutCells(CartesianCell const &in, CartesianCell const &out)
2327 : {
2328 :
2329 : // SUBROUTINE INFORMATION:
2330 : // AUTHOR Edwin Lee
2331 : // DATE WRITTEN Summer 2011
2332 : // MODIFIED na
2333 : // RE-ENGINEERED na
2334 :
2335 1 : this->CircuitInletCell = Point3DInteger(in.X_index, in.Y_index, in.Z_index);
2336 1 : this->CircuitOutletCell = Point3DInteger(out.X_index, out.Y_index, out.Z_index);
2337 1 : }
2338 0 : void Circuit::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
2339 : {
2340 0 : }
2341 0 : void Circuit::oneTimeInit_new([[maybe_unused]] EnergyPlusData &state)
2342 : {
2343 0 : }
2344 :
2345 2 : bool Domain::IsConverged_CurrentToPrevIteration()
2346 : {
2347 :
2348 : // FUNCTION INFORMATION:
2349 : // AUTHOR Edwin Lee
2350 : // DATE WRITTEN Summer 2011
2351 : // MODIFIED na
2352 : // RE-ENGINEERED na
2353 :
2354 2 : Real64 LocalMax = 0.0;
2355 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
2356 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
2357 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
2358 3615 : auto const &cell = this->Cells(X, Y, Z);
2359 3615 : LocalMax = max(LocalMax, std::abs(cell.Temperature - cell.Temperature_PrevIteration));
2360 : }
2361 : }
2362 : }
2363 2 : return (LocalMax < this->SimControls.Convergence_CurrentToPrevIteration);
2364 : }
2365 :
2366 12 : bool IsConverged_PipeCurrentToPrevIteration(Circuit const *thisCircuit, CartesianCell const &CellToCheck)
2367 : {
2368 :
2369 : // FUNCTION INFORMATION:
2370 : // AUTHOR Edwin Lee
2371 : // DATE WRITTEN Summer 2011
2372 : // MODIFIED na
2373 : // RE-ENGINEERED na
2374 :
2375 : Real64 ThisCellMax;
2376 :
2377 12 : Real64 MaxDivAmount = 0.0;
2378 36 : for (auto const &radCell : CellToCheck.PipeCellData.Soil) {
2379 24 : ThisCellMax = std::abs(radCell.Temperature - radCell.Temperature_PrevIteration);
2380 24 : if (ThisCellMax > MaxDivAmount) {
2381 0 : MaxDivAmount = ThisCellMax;
2382 : }
2383 : }
2384 : //'also do the pipe cell
2385 12 : ThisCellMax = std::abs(CellToCheck.PipeCellData.Pipe.Temperature - CellToCheck.PipeCellData.Pipe.Temperature_PrevIteration);
2386 12 : if (ThisCellMax > MaxDivAmount) {
2387 0 : MaxDivAmount = ThisCellMax;
2388 : }
2389 : //'also do the water cell
2390 12 : ThisCellMax = std::abs(CellToCheck.PipeCellData.Fluid.Temperature - CellToCheck.PipeCellData.Fluid.Temperature_PrevIteration);
2391 12 : if (ThisCellMax > MaxDivAmount) {
2392 0 : MaxDivAmount = ThisCellMax;
2393 : }
2394 : //'also do insulation if it exists
2395 12 : if (thisCircuit->HasInsulation) {
2396 0 : ThisCellMax = std::abs(CellToCheck.PipeCellData.Insulation.Temperature - CellToCheck.PipeCellData.Insulation.Temperature_PrevIteration);
2397 0 : if (ThisCellMax > MaxDivAmount) {
2398 0 : MaxDivAmount = ThisCellMax;
2399 : }
2400 : }
2401 :
2402 12 : return (MaxDivAmount < thisCircuit->Convergence_CurrentToPrevIteration);
2403 : }
2404 :
2405 2 : void Domain::ShiftTemperaturesForNewTimeStep()
2406 : {
2407 :
2408 : // SUBROUTINE INFORMATION:
2409 : // AUTHOR Edwin Lee
2410 : // DATE WRITTEN Summer 2011
2411 : // MODIFIED na
2412 : // RE-ENGINEERED na
2413 :
2414 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
2415 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
2416 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
2417 3615 : auto &cell = this->Cells(X, Y, Z);
2418 :
2419 3615 : cell.Temperature_PrevTimeStep = cell.Temperature;
2420 :
2421 3615 : if (cell.cellType == CellType::Pipe) {
2422 :
2423 36 : for (auto &radCell : cell.PipeCellData.Soil) {
2424 24 : radCell.Temperature_PrevTimeStep = radCell.Temperature;
2425 : }
2426 :
2427 12 : cell.PipeCellData.Fluid.Temperature_PrevTimeStep = cell.PipeCellData.Fluid.Temperature;
2428 :
2429 12 : cell.PipeCellData.Pipe.Temperature_PrevTimeStep = cell.PipeCellData.Pipe.Temperature;
2430 :
2431 12 : cell.PipeCellData.Insulation.Temperature_PrevTimeStep = cell.PipeCellData.Insulation.Temperature;
2432 : }
2433 : }
2434 : }
2435 : }
2436 2 : }
2437 :
2438 2 : void Domain::ShiftTemperaturesForNewIteration()
2439 : {
2440 :
2441 : // SUBROUTINE INFORMATION:
2442 : // AUTHOR Edwin Lee
2443 : // DATE WRITTEN Summer 2011
2444 : // MODIFIED na
2445 : // RE-ENGINEERED na
2446 :
2447 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
2448 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
2449 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
2450 3615 : auto &cell = this->Cells(X, Y, Z);
2451 :
2452 3615 : cell.Temperature_PrevIteration = cell.Temperature;
2453 :
2454 3615 : if (cell.cellType == CellType::Pipe) {
2455 :
2456 36 : for (auto &radCell : cell.PipeCellData.Soil) {
2457 24 : radCell.Temperature_PrevIteration = radCell.Temperature;
2458 : }
2459 :
2460 12 : cell.PipeCellData.Fluid.Temperature_PrevIteration = cell.PipeCellData.Fluid.Temperature;
2461 :
2462 12 : cell.PipeCellData.Pipe.Temperature_PrevIteration = cell.PipeCellData.Pipe.Temperature;
2463 :
2464 12 : cell.PipeCellData.Insulation.Temperature_PrevIteration = cell.PipeCellData.Insulation.Temperature;
2465 : }
2466 : }
2467 : }
2468 : }
2469 2 : }
2470 :
2471 12 : void ShiftPipeTemperaturesForNewIteration(CartesianCell &ThisPipeCell)
2472 : {
2473 :
2474 : // SUBROUTINE INFORMATION:
2475 : // AUTHOR Edwin Lee
2476 : // DATE WRITTEN Summer 2011
2477 : // MODIFIED na
2478 : // RE-ENGINEERED na
2479 :
2480 12 : if (ThisPipeCell.cellType == CellType::Pipe) { // It better be!
2481 :
2482 36 : for (auto &radCell : ThisPipeCell.PipeCellData.Soil) {
2483 24 : radCell.Temperature_PrevIteration = radCell.Temperature;
2484 : }
2485 :
2486 12 : ThisPipeCell.PipeCellData.Fluid.Temperature_PrevIteration = ThisPipeCell.PipeCellData.Fluid.Temperature;
2487 :
2488 12 : ThisPipeCell.PipeCellData.Pipe.Temperature_PrevIteration = ThisPipeCell.PipeCellData.Pipe.Temperature;
2489 :
2490 12 : ThisPipeCell.PipeCellData.Insulation.Temperature_PrevIteration = ThisPipeCell.PipeCellData.Insulation.Temperature;
2491 : }
2492 12 : }
2493 :
2494 2 : bool Domain::CheckForOutOfRangeTemps() const
2495 : {
2496 :
2497 : // FUNCTION INFORMATION:
2498 : // AUTHOR Edwin Lee
2499 : // DATE WRITTEN Summer 2011
2500 : // MODIFIED na
2501 : // RE-ENGINEERED na
2502 :
2503 2 : Real64 const MaxLimit = this->SimControls.MaximumTemperatureLimit;
2504 2 : Real64 const MinLimit = this->SimControls.MinimumTemperatureLimit;
2505 :
2506 3617 : for (std::size_t i = 0, e = this->Cells.size(); i < e; ++i) {
2507 3615 : double const Temperature(this->Cells[i].Temperature);
2508 3615 : if ((Temperature > MaxLimit) || (Temperature < MinLimit)) return true;
2509 : }
2510 2 : return false;
2511 : }
2512 :
2513 36869 : Real64 CartesianCell::normalArea(Direction const direction) const
2514 : {
2515 :
2516 : // FUNCTION INFORMATION:
2517 : // AUTHOR Edwin Lee
2518 : // DATE WRITTEN Summer 2011
2519 : // MODIFIED na
2520 : // RE-ENGINEERED na
2521 :
2522 36869 : switch (direction) {
2523 12339 : case Direction::PositiveY:
2524 : case Direction::NegativeY:
2525 12339 : return this->YNormalArea();
2526 12295 : case Direction::PositiveX:
2527 : case Direction::NegativeX:
2528 12295 : return this->XNormalArea();
2529 12235 : case Direction::PositiveZ:
2530 : case Direction::NegativeZ:
2531 12235 : return this->ZNormalArea();
2532 0 : default:
2533 0 : assert(false);
2534 : }
2535 :
2536 : return 0;
2537 : }
2538 :
2539 12 : CartesianPipeCellInformation::CartesianPipeCellInformation(Real64 const GridCellWidth,
2540 : PlantPipingSystemsManager::RadialSizing const PipeSizes,
2541 : int const NumRadialNodes,
2542 : Real64 const CellDepth,
2543 : Real64 const InsulationThickness,
2544 : Real64 const RadialGridExtent,
2545 12 : bool const SimHasInsulation)
2546 : {
2547 :
2548 : // SUBROUTINE INFORMATION:
2549 : // AUTHOR Edwin Lee
2550 : // DATE WRITTEN Summer 2011
2551 : // MODIFIED na
2552 : // RE-ENGINEERED na
2553 :
2554 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2555 : Real64 InsulationInnerRadius;
2556 : Real64 InsulationOuterRadius;
2557 : Real64 InsulationCentroid;
2558 : Real64 MinimumSoilRadius;
2559 :
2560 : //'calculate pipe radius
2561 12 : Real64 const PipeOuterRadius = PipeSizes.OuterDia / 2.0;
2562 12 : Real64 const PipeInnerRadius = PipeSizes.InnerDia / 2.0;
2563 :
2564 : //'--we will work from inside out, calculating dimensions and instantiating variables--
2565 : //'first instantiate the water cell
2566 12 : this->Fluid = FluidCellInformation(PipeInnerRadius, CellDepth);
2567 :
2568 : //'then the pipe cell
2569 12 : this->Pipe = RadialCellInformation((PipeOuterRadius + PipeInnerRadius) / 2.0, PipeInnerRadius, PipeOuterRadius);
2570 :
2571 : //'then the insulation if we have it
2572 12 : if (InsulationThickness > 0.0) {
2573 0 : InsulationInnerRadius = PipeOuterRadius;
2574 0 : InsulationOuterRadius = InsulationInnerRadius + InsulationThickness;
2575 0 : InsulationCentroid = (InsulationInnerRadius + InsulationOuterRadius) / 2.0;
2576 0 : this->Insulation = RadialCellInformation(InsulationCentroid, InsulationInnerRadius, InsulationOuterRadius);
2577 : }
2578 :
2579 : //'determine where to start applying the radial soil cells based on whether we have insulation or not
2580 12 : if (!SimHasInsulation) {
2581 12 : MinimumSoilRadius = PipeOuterRadius;
2582 : } else {
2583 0 : MinimumSoilRadius = this->Insulation.OuterRadius;
2584 : }
2585 :
2586 : //'the radial cells are distributed evenly throughout this region
2587 12 : this->RadialSliceWidth = RadialGridExtent / NumRadialNodes;
2588 :
2589 : // first set Rval to the minimum soil radius plus half a slice thickness for the innermost radial node
2590 12 : Real64 Rval = MinimumSoilRadius + (this->RadialSliceWidth / 2.0);
2591 12 : Real64 ThisSliceInnerRadius = MinimumSoilRadius;
2592 12 : this->Soil.emplace_back(Rval, ThisSliceInnerRadius, ThisSliceInnerRadius + this->RadialSliceWidth);
2593 :
2594 : //'then loop through the rest and assign them, each radius is simply one more slice thickness
2595 24 : for (int RadialCellCtr = 1; RadialCellCtr < NumRadialNodes; ++RadialCellCtr) {
2596 12 : Rval += this->RadialSliceWidth;
2597 12 : ThisSliceInnerRadius += this->RadialSliceWidth;
2598 12 : this->Soil.emplace_back(Rval, ThisSliceInnerRadius, ThisSliceInnerRadius + this->RadialSliceWidth);
2599 : }
2600 :
2601 : //'also assign the interface cell surrounding the radial system
2602 12 : this->InterfaceVolume = (1.0 - (Constant::Pi / 4.0)) * pow_2(GridCellWidth) * CellDepth;
2603 12 : }
2604 :
2605 2 : void Domain::developMesh(EnergyPlusData &state)
2606 : {
2607 :
2608 : // SUBROUTINE INFORMATION:
2609 : // AUTHOR Edwin Lee
2610 : // DATE WRITTEN Summer 2011
2611 : // MODIFIED na
2612 : // RE-ENGINEERED na
2613 :
2614 : //'****** LAYOUT PARTITIONS ******'
2615 2 : this->createPartitionCenterList(state);
2616 :
2617 2 : bool XPartitionsExist = !this->Partitions.X.empty();
2618 2 : std::vector<GridRegion> XPartitionRegions = this->createPartitionRegionList(state, this->Partitions.X, XPartitionsExist, this->Extents.xMax);
2619 :
2620 2 : bool YPartitionsExist = !this->Partitions.Y.empty();
2621 2 : std::vector<GridRegion> YPartitionRegions = this->createPartitionRegionList(state, this->Partitions.Y, YPartitionsExist, this->Extents.yMax);
2622 :
2623 2 : bool ZPartitionsExist = !this->Partitions.Z.empty();
2624 2 : std::vector<GridRegion> ZPartitionRegions = this->createPartitionRegionList(state, this->Partitions.Z, ZPartitionsExist, this->Extents.zMax);
2625 :
2626 : //'***** LAYOUT MESH REGIONS *****'
2627 : // Zone-coupled slab models
2628 2 : if (this->HasZoneCoupledBasement) {
2629 2 : this->createRegionList(XRegions,
2630 : XPartitionRegions,
2631 : this->Extents.xMax,
2632 : RegionType::XDirection,
2633 : XPartitionsExist,
2634 : _,
2635 : _,
2636 1 : this->XIndex,
2637 1 : this->XWallIndex,
2638 1 : this->InsulationXIndex);
2639 :
2640 2 : this->createRegionList(YRegions,
2641 : YPartitionRegions,
2642 : this->Extents.yMax,
2643 : RegionType::YDirection,
2644 : YPartitionsExist,
2645 : _,
2646 : _,
2647 : _,
2648 : _,
2649 : _,
2650 1 : this->YIndex,
2651 1 : this->YFloorIndex,
2652 1 : this->InsulationYIndex);
2653 :
2654 1 : this->createRegionList(ZRegions,
2655 : ZPartitionRegions,
2656 : this->Extents.zMax,
2657 : RegionType::ZDirection,
2658 : ZPartitionsExist,
2659 : _,
2660 : _,
2661 : _,
2662 : _,
2663 : _,
2664 : _,
2665 : _,
2666 : _,
2667 1 : this->ZIndex,
2668 1 : this->ZWallIndex,
2669 1 : this->InsulationZIndex);
2670 1 : } else if (this->HasZoneCoupledSlab) {
2671 0 : this->createRegionList(XRegions,
2672 : XPartitionRegions,
2673 : this->Extents.xMax,
2674 : RegionType::XDirection,
2675 : XPartitionsExist,
2676 : _,
2677 : _,
2678 0 : this->XIndex,
2679 : _,
2680 0 : this->InsulationXIndex);
2681 :
2682 0 : this->createRegionList(YRegions,
2683 : YPartitionRegions,
2684 : this->Extents.yMax,
2685 : RegionType::YDirection,
2686 : YPartitionsExist,
2687 : _,
2688 : _,
2689 : _,
2690 : _,
2691 : _,
2692 0 : this->YIndex,
2693 : _,
2694 0 : this->InsulationYIndex);
2695 :
2696 0 : this->createRegionList(ZRegions,
2697 : ZPartitionRegions,
2698 : this->Extents.zMax,
2699 : RegionType::ZDirection,
2700 : ZPartitionsExist,
2701 : _,
2702 : _,
2703 : _,
2704 : _,
2705 : _,
2706 : _,
2707 : _,
2708 : _,
2709 0 : this->ZIndex,
2710 : _,
2711 0 : this->InsulationZIndex);
2712 : } else {
2713 2 : this->createRegionList(
2714 1 : XRegions, XPartitionRegions, this->Extents.xMax, RegionType::XDirection, XPartitionsExist, this->BasementZone.BasementWallXIndex);
2715 :
2716 3 : this->createRegionList(
2717 2 : YRegions, YPartitionRegions, this->Extents.yMax, RegionType::YDirection, YPartitionsExist, _, this->BasementZone.BasementFloorYIndex);
2718 :
2719 1 : this->createRegionList(ZRegions, ZPartitionRegions, this->Extents.zMax, RegionType::ZDirection, ZPartitionsExist);
2720 : }
2721 :
2722 : //'** MAKE REGIONS > BOUNDARIES **'
2723 2 : std::vector<Real64> XBoundaryPoints = CreateBoundaryList(XRegions, this->Extents.xMax, RegionType::XDirection);
2724 2 : std::vector<Real64> YBoundaryPoints = CreateBoundaryList(YRegions, this->Extents.yMax, RegionType::YDirection);
2725 2 : std::vector<Real64> ZBoundaryPoints = CreateBoundaryList(ZRegions, this->Extents.zMax, RegionType::ZDirection);
2726 :
2727 : //'****** DEVELOP CELL ARRAY *****'
2728 2 : this->createCellArray(XBoundaryPoints, YBoundaryPoints, ZBoundaryPoints);
2729 :
2730 : //'***** SETUP CELL NEIGHBORS ****'
2731 2 : this->setupCellNeighbors();
2732 :
2733 : //'** SET UP PIPE CIRCUIT CELLS **'
2734 2 : this->setupPipeCircuitInOutCells();
2735 2 : }
2736 :
2737 4 : void Domain::createPartitionCenterList([[maybe_unused]] EnergyPlusData &state)
2738 : {
2739 :
2740 : // SUBROUTINE INFORMATION:
2741 : // AUTHOR Edwin Lee
2742 : // DATE WRITTEN Summer 2011
2743 : // MODIFIED na
2744 : // RE-ENGINEERED na
2745 :
2746 : // SUBROUTINE PARAMETER DEFINITIONS:
2747 4 : Real64 constexpr BasementCellFraction(0.001); // the fraction of domain extent to use for the basement cells
2748 : // actual dimension shouldn't matter for calculation purposes
2749 :
2750 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2751 : Real64 BasementDistFromBottom;
2752 : Real64 FloorLocation;
2753 : Real64 UnderFloorLocation;
2754 : Real64 PipeCellWidth;
2755 : Real64 SurfCellWidth; // Basement surface...
2756 : Real64 SideXLocation;
2757 : Real64 SideXWallLocation;
2758 : Real64 SideXInsulationLocation;
2759 : Real64 SideZLocation;
2760 : Real64 SideZWallLocation;
2761 : Real64 SideZInsulationLocation;
2762 : Real64 SlabDistFromBottom;
2763 : Real64 YInsulationLocation;
2764 4 : Real64 CellWidth(0.0);
2765 4 : Real64 InterfaceCellWidth(0.008);
2766 :
2767 : // Object Data
2768 4 : std::vector<MeshPartition> PreviousEntries;
2769 4 : Segment ThisSegment;
2770 :
2771 : //'NOTE: pipe location y values have already been corrected to be measured from the bottom surface
2772 : //'in input they are measured by depth, but internally they are referred to by distance from y = 0, or the bottom boundary
2773 5 : for (auto const &thisCircuit : this->circuits) {
2774 :
2775 : // set up a convenience variable here
2776 : //'account for the pipe and insulation if necessary
2777 1 : if (!thisCircuit->HasInsulation) {
2778 1 : PipeCellWidth = thisCircuit->PipeSize.OuterDia;
2779 : } else {
2780 0 : PipeCellWidth = thisCircuit->InsulationSize.OuterDia;
2781 : }
2782 :
2783 : //'then add the radial mesh thickness on both sides of the pipe/insulation construct
2784 1 : PipeCellWidth += 2 * thisCircuit->RadialMeshThickness;
2785 :
2786 3 : for (auto &segment : thisCircuit->pipeSegments) {
2787 2 : if (std::find(this->Partitions.X.begin(), this->Partitions.X.end(), segment->PipeLocation.X) == this->Partitions.X.end()) {
2788 2 : this->Partitions.X.emplace_back(segment->PipeLocation.X, PartitionType::Pipe, PipeCellWidth);
2789 : }
2790 2 : if (std::find(this->Partitions.Y.begin(), this->Partitions.Y.end(), segment->PipeLocation.Y) == this->Partitions.Y.end()) {
2791 1 : this->Partitions.Y.emplace_back(segment->PipeLocation.Y, PartitionType::Pipe, PipeCellWidth);
2792 : }
2793 : }
2794 : }
2795 :
2796 : // Underground Piping Systems Ground domain with basement interaction
2797 4 : if (!this->HasZoneCoupledBasement) {
2798 3 : if (this->HasBasement) { // FHX model
2799 : //'NOTE: the basement depth is still a depth from the ground surface, need to correct for this here
2800 0 : if (this->BasementZone.Width > 0) {
2801 0 : SurfCellWidth = this->Extents.xMax * BasementCellFraction;
2802 0 : if (std::find(this->Partitions.X.begin(), this->Partitions.X.end(), this->BasementZone.Width) == this->Partitions.X.end()) {
2803 0 : this->Partitions.X.emplace_back(this->BasementZone.Width, PartitionType::BasementWall, SurfCellWidth);
2804 : }
2805 : }
2806 0 : if (this->BasementZone.Depth > 0) {
2807 0 : SurfCellWidth = this->Extents.yMax * BasementCellFraction;
2808 0 : BasementDistFromBottom = this->Extents.yMax - this->BasementZone.Depth;
2809 0 : if (std::find(this->Partitions.Y.begin(), this->Partitions.Y.end(), BasementDistFromBottom) == this->Partitions.Y.end()) {
2810 0 : this->Partitions.Y.emplace_back(BasementDistFromBottom, PartitionType::BasementFloor, SurfCellWidth);
2811 : }
2812 : }
2813 : }
2814 : } else { // Zone-coupled basement model
2815 : //'NOTE: the basement depth is still a depth from the ground surface, need to correct for this here
2816 1 : if (this->BasementZone.Width > 0) {
2817 : // Create partitions at basement walls and horizontal insulation edges
2818 1 : CellWidth = this->VertInsThickness;
2819 : // Side X direction - Insulation layer
2820 1 : SideXLocation = this->PerimeterOffset - InterfaceCellWidth - CellWidth / 2.0;
2821 : // Side X direction - Basement Wall Interface
2822 1 : SideXWallLocation = this->PerimeterOffset - InterfaceCellWidth / 2.0;
2823 1 : if (this->HorizIns == HorizInsulation::Perimeter) {
2824 : // Insulation Edge in X direction
2825 1 : SideXInsulationLocation = this->PerimeterOffset + this->HorizInsWidth + InterfaceCellWidth / 2.0;
2826 : } else {
2827 0 : SideXInsulationLocation = -1;
2828 : }
2829 1 : if (std::find(this->Partitions.X.begin(), this->Partitions.X.end(), this->BasementZone.Width) == this->Partitions.X.end()) {
2830 : // Partition at insulation edges in the X direction, if horizontal insulation present
2831 1 : if (this->HorizIns == HorizInsulation::Perimeter) {
2832 : // Side X direction - Insulation layer
2833 1 : this->Partitions.X.emplace_back(SideXLocation, PartitionType::XSide, CellWidth);
2834 : // Side X direction - Basement Wall interface
2835 1 : this->Partitions.X.emplace_back(SideXWallLocation, PartitionType::XSideWall, InterfaceCellWidth);
2836 : // Insulation Edge X direction
2837 1 : this->Partitions.X.emplace_back(SideXInsulationLocation, PartitionType::HorizInsXSide, InterfaceCellWidth);
2838 0 : } else if (this->HorizIns == HorizInsulation::Full) {
2839 : // Side X direction - Insulation layer
2840 0 : this->Partitions.X.emplace_back(SideXLocation, PartitionType::XSide, CellWidth);
2841 : // Side X direction -Basement Wall interface
2842 0 : this->Partitions.X.emplace_back(SideXWallLocation, PartitionType::XSideWall, InterfaceCellWidth);
2843 : } else {
2844 : // Side X direction - Insulation layer
2845 0 : this->Partitions.X.emplace_back(SideXLocation, PartitionType::XSide, CellWidth);
2846 : // Side X direction - Basement Wall interface
2847 0 : this->Partitions.X.emplace_back(SideXWallLocation, PartitionType::XSideWall, InterfaceCellWidth);
2848 : }
2849 : }
2850 : }
2851 : // Zone coupled basement model
2852 1 : if (this->BasementZone.Depth > 0) {
2853 1 : CellWidth = this->HorizInsThickness;
2854 : // Distance of basement floor interface from domain bottom
2855 1 : FloorLocation = this->Extents.yMax - this->BasementZone.Depth - InterfaceCellWidth / 2.0;
2856 : // Distance of basement floor insulation layer from domain bottom
2857 1 : UnderFloorLocation = this->Extents.yMax - this->BasementZone.Depth - InterfaceCellWidth - CellWidth / 2.0;
2858 1 : if (this->VertInsPresentFlag) {
2859 1 : YInsulationLocation = this->Extents.yMax - this->VertInsDepth - InterfaceCellWidth / 2.0;
2860 : } else {
2861 0 : YInsulationLocation = -1;
2862 : }
2863 1 : if (std::find(this->Partitions.Y.begin(), this->Partitions.Y.end(), FloorLocation) == this->Partitions.Y.end()) {
2864 : // Partition at bottom edge of vertical insulation, if vertical insulation is present
2865 1 : if (this->VertInsPresentFlag && YInsulationLocation > FloorLocation + CellWidth) {
2866 : // Partition at basement floor interface
2867 1 : this->Partitions.Y.emplace_back(FloorLocation, PartitionType::FloorInside, InterfaceCellWidth);
2868 : // Partition under the basement floor for insulation layer
2869 1 : this->Partitions.Y.emplace_back(UnderFloorLocation, PartitionType::UnderFloor, CellWidth);
2870 : // Vertical-Insulation edge partition
2871 1 : this->Partitions.Y.emplace_back(YInsulationLocation, PartitionType::VertInsLowerEdge, InterfaceCellWidth);
2872 : } else {
2873 0 : this->Partitions.Y.emplace_back(FloorLocation, PartitionType::FloorInside, InterfaceCellWidth);
2874 0 : this->Partitions.Y.emplace_back(UnderFloorLocation, PartitionType::UnderFloor, CellWidth);
2875 : }
2876 : }
2877 : }
2878 1 : if (this->BasementZone.Width > 0) {
2879 : // Create partitions at basement walls and horizontal insulation edges
2880 1 : CellWidth = this->VertInsThickness;
2881 : // Side Z direction - Insulation layer
2882 1 : SideZLocation = this->PerimeterOffset - InterfaceCellWidth - CellWidth / 2.0;
2883 : // Side Z direction - Basement Wall Interface
2884 1 : SideZWallLocation = this->PerimeterOffset - InterfaceCellWidth / 2.0;
2885 1 : if (this->HorizIns == HorizInsulation::Perimeter) {
2886 : // Insulation Edge Z direction
2887 1 : SideZInsulationLocation = this->PerimeterOffset + this->HorizInsWidth + InterfaceCellWidth / 2.0;
2888 : } else {
2889 0 : SideZInsulationLocation = -1;
2890 : }
2891 1 : if (std::find(this->Partitions.Z.begin(), this->Partitions.Z.end(), this->BasementZone.Width) == this->Partitions.Z.end()) {
2892 : // Partition at insulation edges in the Z direction, if horizontal insulation present
2893 1 : if (this->HorizIns == HorizInsulation::Perimeter) {
2894 : // Side Z direction - Insulation layer
2895 1 : this->Partitions.Z.emplace_back(SideZLocation, PartitionType::ZSide, CellWidth);
2896 : // Side Z direction - Basement Wall interface
2897 1 : this->Partitions.Z.emplace_back(SideZWallLocation, PartitionType::ZSideWall, InterfaceCellWidth);
2898 : // Insulation Edge Z direction
2899 1 : this->Partitions.Z.emplace_back(SideZInsulationLocation, PartitionType::HorizInsZSide, InterfaceCellWidth);
2900 0 : } else if (this->HorizIns == HorizInsulation::Full) {
2901 : // Side Z direction - Insulation layer
2902 0 : this->Partitions.Z.emplace_back(SideZLocation, PartitionType::ZSide, CellWidth);
2903 : // Side Z direction -Basement Wall interface
2904 0 : this->Partitions.Z.emplace_back(SideZWallLocation, PartitionType::ZSideWall, InterfaceCellWidth);
2905 : } else {
2906 : // Side Z direction - Insulation layer
2907 0 : this->Partitions.Z.emplace_back(SideZLocation, PartitionType::ZSide, CellWidth);
2908 : // Side Z direction -Basement Wall interface
2909 0 : this->Partitions.Z.emplace_back(SideZWallLocation, PartitionType::ZSideWall, InterfaceCellWidth);
2910 : }
2911 : }
2912 : }
2913 : }
2914 :
2915 : // Zone-coupled slab
2916 4 : if (this->HasZoneCoupledSlab) {
2917 : // NOTE: the slab depth is still a depth from the ground surface, need to correct for this here.
2918 :
2919 : // Create X-direction partitions
2920 :
2921 : // Create partition at slab edges in the X direction
2922 2 : CellWidth = this->VertInsThickness;
2923 : // Side X direction
2924 2 : SideXLocation = this->PerimeterOffset - CellWidth / 2.0;
2925 : // Insulation Edge X direction
2926 2 : if (this->HorizIns == HorizInsulation::Perimeter) {
2927 0 : SideXInsulationLocation = SideXLocation + this->HorizInsWidth;
2928 : } else {
2929 2 : SideXInsulationLocation = -1;
2930 : }
2931 2 : if (std::find(this->Partitions.X.begin(), this->Partitions.X.end(), this->SlabWidth) == this->Partitions.X.end()) {
2932 : // Partition at insulation edges in the X direction, if horizontal insulation present
2933 2 : if (this->HorizIns == HorizInsulation::Perimeter) {
2934 : // Side X direction
2935 0 : this->Partitions.X.emplace_back(SideXLocation, PartitionType::XSide, CellWidth);
2936 : // Insulation Edge X direction
2937 0 : this->Partitions.X.emplace_back(SideXInsulationLocation, PartitionType::HorizInsXSide, CellWidth);
2938 2 : } else if (this->HorizIns == HorizInsulation::Full) {
2939 : // Side X direction
2940 2 : this->Partitions.X.emplace_back(SideXLocation, PartitionType::XSide, CellWidth);
2941 : } else {
2942 : // Side X direction
2943 0 : this->Partitions.X.emplace_back(SideXLocation, PartitionType::XSide, CellWidth);
2944 : }
2945 : }
2946 :
2947 : // Create Y-direction partitions
2948 :
2949 2 : CellWidth = this->HorizInsThickness;
2950 :
2951 : // Partition at bottom edge of vertical insulation, if vertical insulation present
2952 2 : if (this->VertInsPresentFlag) {
2953 2 : YInsulationLocation = this->Extents.yMax - this->VertInsDepth + CellWidth / 2.0;
2954 : } else {
2955 0 : YInsulationLocation = -1;
2956 : }
2957 :
2958 2 : if (this->slabPosition == SlabPosition::InGrade) { // Slab in-grade case
2959 :
2960 2 : SlabDistFromBottom = this->Extents.yMax - this->SlabThickness - CellWidth / 2.0;
2961 :
2962 2 : if (std::find(this->Partitions.Y.begin(), this->Partitions.Y.end(), SlabDistFromBottom) == this->Partitions.Y.end()) {
2963 :
2964 : // Partition at bottom edge of vertical insulation, if vertical insulation present
2965 2 : if (this->VertInsPresentFlag) {
2966 : // Under-slab partition
2967 2 : this->Partitions.Y.emplace_back(SlabDistFromBottom, PartitionType::UnderFloor, CellWidth);
2968 : // Vertical-Insulation edge partition
2969 2 : this->Partitions.Y.emplace_back(YInsulationLocation, PartitionType::VertInsLowerEdge, CellWidth);
2970 : } else {
2971 : // Under-slab partition
2972 0 : this->Partitions.Y.emplace_back(SlabDistFromBottom, PartitionType::UnderFloor, CellWidth);
2973 : }
2974 : }
2975 : } else { // Slab on-grade case
2976 :
2977 0 : if (std::find(this->Partitions.Y.begin(), this->Partitions.Y.end(), YInsulationLocation) == this->Partitions.Y.end()) {
2978 : // Partition at bottom edge of vertical insulation, if vertical insulation present
2979 0 : if (this->VertInsPresentFlag) {
2980 : // Vertical-Insulation edge partition
2981 0 : this->Partitions.Y.emplace_back(YInsulationLocation, PartitionType::VertInsLowerEdge, CellWidth);
2982 : }
2983 : }
2984 : }
2985 :
2986 : // Create Z-direction partitions
2987 :
2988 2 : CellWidth = this->VertInsThickness;
2989 : // Side Z direction
2990 2 : SideZLocation = this->PerimeterOffset - CellWidth / 2.0;
2991 : // Insulation Edge Z direction
2992 2 : if (this->HorizIns == HorizInsulation::Perimeter) {
2993 0 : SideZInsulationLocation = SideZLocation + this->HorizInsWidth;
2994 : } else {
2995 2 : SideZInsulationLocation = -1;
2996 : }
2997 2 : if (std::find(this->Partitions.Z.begin(), this->Partitions.Z.end(), this->SlabWidth) == this->Partitions.Z.end()) {
2998 : // Partition at insulation edges in the Z direction, if horizontal insulation present
2999 2 : if (this->HorizIns == HorizInsulation::Perimeter) {
3000 : // Side Z direction
3001 0 : this->Partitions.Z.emplace_back(SideZLocation, PartitionType::ZSide, CellWidth);
3002 : // Insulation Edge Z direction
3003 0 : this->Partitions.Z.emplace_back(SideZInsulationLocation, PartitionType::HorizInsZSide, CellWidth);
3004 2 : } else if (this->HorizIns == HorizInsulation::Full) {
3005 : // Side Z direction
3006 2 : this->Partitions.Z.emplace_back(SideZLocation, PartitionType::ZSide, CellWidth);
3007 : } else {
3008 : // Side Z direction
3009 0 : this->Partitions.Z.emplace_back(SideZLocation, PartitionType::ZSide, CellWidth);
3010 : }
3011 : }
3012 : }
3013 15 : auto lambda = [](MeshPartition a, MeshPartition b) { return a.rDimension < b.rDimension; };
3014 4 : std::sort(this->Partitions.X.begin(), this->Partitions.X.end(), lambda);
3015 4 : std::sort(this->Partitions.Y.begin(), this->Partitions.Y.end(), lambda);
3016 4 : std::sort(this->Partitions.Z.begin(), this->Partitions.Z.end(), lambda);
3017 4 : }
3018 :
3019 6 : std::vector<GridRegion> Domain::createPartitionRegionList(EnergyPlusData &state,
3020 : std::vector<MeshPartition> const &ThesePartitionCenters,
3021 : bool const PartitionsExist,
3022 : Real64 const DirExtentMax)
3023 : {
3024 :
3025 : // FUNCTION INFORMATION:
3026 : // AUTHOR Edwin Lee
3027 : // DATE WRITTEN Summer 2011
3028 : // MODIFIED na
3029 : // RE-ENGINEERED na
3030 :
3031 : // Return value
3032 6 : std::vector<GridRegion> ThesePartitionRegions;
3033 :
3034 : // FUNCTION PARAMETER DEFINITIONS:
3035 : static constexpr std::string_view RoutineName("CreatePartitionRegionList");
3036 :
3037 6 : if (!PartitionsExist) {
3038 1 : return ThesePartitionRegions;
3039 : }
3040 :
3041 : //'loop across all partitions
3042 17 : for (int Index = 0; Index < (int)ThesePartitionCenters.size(); ++Index) {
3043 12 : auto const &thisPartitionCenter = ThesePartitionCenters[Index];
3044 :
3045 12 : Real64 const ThisCellWidthBy2 = thisPartitionCenter.TotalWidth / 2.0;
3046 12 : PartitionType ThisPartitionType = thisPartitionCenter.partitionType;
3047 :
3048 : //'use this half width to validate the region and add it to the collection
3049 12 : Real64 CellLeft = thisPartitionCenter.rDimension - ThisCellWidthBy2;
3050 12 : Real64 CellRight = thisPartitionCenter.rDimension + ThisCellWidthBy2;
3051 :
3052 : // check to make sure this location is valid
3053 12 : if (CellLeft < 0.0 || CellRight > DirExtentMax) {
3054 0 : ShowSevereError(state, format("PlantPipingSystems::{}: Invalid partition location in domain.", RoutineName));
3055 0 : ShowContinueError(state, format("Occurs during mesh development for domain={}", this->Name));
3056 0 : ShowContinueError(state, "A pipe or basement is located outside of the domain extents.");
3057 0 : ShowFatalError(state, "Preceding error causes program termination.");
3058 : }
3059 :
3060 : // Scan all grid regions to make sure this range doesn't fall within an already entered range
3061 22 : for (int SubIndex = 0; SubIndex <= Index - 1; ++SubIndex) {
3062 10 : auto &thisPartitionRegionSubIndex = ThesePartitionRegions[SubIndex];
3063 : // Coupled-basement model has adjacent partitions: ThesePartitionRegions( 0 ) and ThesePartitionRegions( 1 ) - SA
3064 10 : if (this->HasZoneCoupledBasement && Index == 1) {
3065 6 : if (IsInRange_BasementModel(CellLeft, thisPartitionRegionSubIndex.Min, thisPartitionRegionSubIndex.Max) ||
3066 3 : IsInRangeReal(CellRight, thisPartitionRegionSubIndex.Min, thisPartitionRegionSubIndex.Max)) {
3067 :
3068 0 : ShowSevereError(state, format("PlantPipingSystems::{}: Invalid partition location in domain.", RoutineName));
3069 0 : ShowContinueError(state, format("Occurs during mesh development for domain={}", this->Name));
3070 0 : ShowContinueError(state, "A mesh conflict was encountered where partitions were overlapping.");
3071 0 : ShowContinueError(state, "Ensure that all pipes exactly line up or are separated to allow meshing in between them");
3072 0 : ShowContinueError(state, "Also verify the pipe and basement dimensions to avoid conflicts there.");
3073 0 : ShowFatalError(state, "Preceding error causes program termination");
3074 : }
3075 :
3076 : } else {
3077 :
3078 14 : if (IsInRangeReal(CellLeft, thisPartitionRegionSubIndex.Min, thisPartitionRegionSubIndex.Max) ||
3079 7 : IsInRangeReal(CellRight, thisPartitionRegionSubIndex.Min, thisPartitionRegionSubIndex.Max)) {
3080 :
3081 0 : ShowSevereError(state, format("PlantPipingSystems::{}: Invalid partition location in domain.", RoutineName));
3082 0 : ShowContinueError(state, format("Occurs during mesh development for domain={}", this->Name));
3083 0 : ShowContinueError(state, "A mesh conflict was encountered where partitions were overlapping.");
3084 0 : ShowContinueError(state, "Ensure that all pipes exactly line up or are separated to allow meshing in between them");
3085 0 : ShowContinueError(state, "Also verify the pipe and basement dimensions to avoid conflicts there.");
3086 0 : ShowFatalError(state, "Preceding error causes program termination");
3087 : }
3088 : }
3089 : }
3090 :
3091 12 : ThesePartitionRegions.emplace_back();
3092 12 : auto &thisNewPartitionRegion = ThesePartitionRegions[Index];
3093 12 : thisNewPartitionRegion.Min = CellLeft;
3094 12 : thisNewPartitionRegion.Max = CellRight;
3095 :
3096 : // Need to map partition type into region type parameters, since they are different enumerations
3097 12 : if (ThisPartitionType == PartitionType::BasementWall) {
3098 0 : thisNewPartitionRegion.thisRegionType = RegionType::BasementWall;
3099 12 : } else if (ThisPartitionType == PartitionType::BasementFloor) {
3100 0 : thisNewPartitionRegion.thisRegionType = RegionType::BasementFloor;
3101 12 : } else if (ThisPartitionType == PartitionType::Pipe) {
3102 3 : thisNewPartitionRegion.thisRegionType = RegionType::Pipe;
3103 9 : } else if (ThisPartitionType == PartitionType::XSide) {
3104 1 : thisNewPartitionRegion.thisRegionType = RegionType::XSide;
3105 8 : } else if (ThisPartitionType == PartitionType::XSideWall) {
3106 1 : thisNewPartitionRegion.thisRegionType = RegionType::XSideWall;
3107 7 : } else if (ThisPartitionType == PartitionType::HorizInsXSide) {
3108 1 : thisNewPartitionRegion.thisRegionType = RegionType::HorizInsXSide;
3109 6 : } else if (ThisPartitionType == PartitionType::ZSide) {
3110 1 : thisNewPartitionRegion.thisRegionType = RegionType::ZSide;
3111 5 : } else if (ThisPartitionType == PartitionType::ZSideWall) {
3112 1 : thisNewPartitionRegion.thisRegionType = RegionType::ZSideWall;
3113 4 : } else if (ThisPartitionType == PartitionType::HorizInsZSide) {
3114 1 : thisNewPartitionRegion.thisRegionType = RegionType::HorizInsZSide;
3115 3 : } else if (ThisPartitionType == PartitionType::FloorInside) {
3116 1 : thisNewPartitionRegion.thisRegionType = RegionType::FloorInside;
3117 2 : } else if (ThisPartitionType == PartitionType::UnderFloor) {
3118 1 : thisNewPartitionRegion.thisRegionType = RegionType::UnderFloor;
3119 1 : } else if (ThisPartitionType == PartitionType::VertInsLowerEdge) {
3120 1 : thisNewPartitionRegion.thisRegionType = RegionType::VertInsLowerEdge;
3121 : } else {
3122 : // diagnostic error
3123 : }
3124 : }
3125 :
3126 5 : return ThesePartitionRegions;
3127 0 : }
3128 :
3129 : #pragma clang diagnostic push
3130 : #pragma ide diagnostic ignored "ArgumentSelectionDefectsInspection"
3131 :
3132 6 : void Domain::createRegionList(std::vector<GridRegion> &Regions,
3133 : std::vector<GridRegion> const &ThesePartitionRegions,
3134 : Real64 const DirExtentMax,
3135 : RegionType const DirDirection,
3136 : bool const PartitionsExist,
3137 : ObjexxFCL::Optional_int BasementWallXIndex,
3138 : ObjexxFCL::Optional_int BasementFloorYIndex,
3139 : ObjexxFCL::Optional_int XIndex,
3140 : ObjexxFCL::Optional_int XWallIndex,
3141 : ObjexxFCL::Optional_int InsulationXIndex,
3142 : ObjexxFCL::Optional_int YIndex,
3143 : ObjexxFCL::Optional_int YFloorIndex,
3144 : ObjexxFCL::Optional_int InsulationYIndex,
3145 : ObjexxFCL::Optional_int ZIndex,
3146 : ObjexxFCL::Optional_int ZWallIndex,
3147 : ObjexxFCL::Optional_int InsulationZIndex)
3148 : {
3149 :
3150 : // FUNCTION INFORMATION:
3151 : // AUTHOR Edwin Lee
3152 : // DATE WRITTEN Summer 2011
3153 : // MODIFIED na
3154 : // RE-ENGINEERED na
3155 :
3156 6 : std::vector<Real64> tempCellWidths;
3157 :
3158 6 : if (PartitionsExist) {
3159 5 : int cellCountUpToNow = 0;
3160 :
3161 17 : for (int i = 0; i < (int)ThesePartitionRegions.size(); ++i) {
3162 12 : auto &thisPartition = ThesePartitionRegions[i];
3163 :
3164 12 : if (i == 0) { // First partition
3165 : // Create region to left of partition
3166 5 : GridRegion tempRegion(0.0, thisPartition.Min, DirDirection, tempCellWidths);
3167 5 : int potentialCellWidthsCount = this->getCellWidthsCount(DirDirection);
3168 5 : if ((thisPartition.Min - 0.0) < 0.00001) {
3169 0 : cellCountUpToNow += 1; // just one cell for extremely tight regions
3170 : } else {
3171 5 : cellCountUpToNow += potentialCellWidthsCount;
3172 : }
3173 5 : this->getCellWidths(tempRegion, tempRegion.thisRegionType);
3174 5 : Regions.push_back(tempRegion);
3175 12 : } else if (i == 1 && this->HasZoneCoupledBasement) {
3176 3 : cellCountUpToNow += 1; // don't add a left partition for partition index 1 of coupled basements
3177 : } else { // All other partitions
3178 : // Because of the way the index block below is structured, we need to update cellCount
3179 : // **after** we pass that block. We could include logic below to do this, but this block
3180 : // already fits within the structure properly, so increment it here to account for the
3181 : // single cell partition layer that was applied at the **end** of the previous partition index
3182 4 : ++cellCountUpToNow;
3183 : // Create region to left of partition
3184 4 : auto &leftPartition = ThesePartitionRegions[i - 1];
3185 4 : auto tempRegion = GridRegion(leftPartition.Max, thisPartition.Min, DirDirection, tempCellWidths); // (AUTO_OK_OBJ)
3186 4 : int potentialCellWidthsCount = this->getCellWidthsCount(DirDirection);
3187 4 : if ((thisPartition.Min - leftPartition.Max) < 0.00001) {
3188 0 : cellCountUpToNow += 1; // just one cell for extremely tight regions
3189 : } else {
3190 4 : cellCountUpToNow += potentialCellWidthsCount;
3191 : }
3192 4 : this->getCellWidths(tempRegion, tempRegion.thisRegionType);
3193 4 : Regions.push_back(tempRegion);
3194 4 : }
3195 :
3196 12 : if (thisPartition.thisRegionType == RegionType::BasementWall) {
3197 0 : if (present(BasementWallXIndex)) BasementWallXIndex = cellCountUpToNow;
3198 12 : } else if (thisPartition.thisRegionType == RegionType::BasementFloor) {
3199 0 : if (present(BasementFloorYIndex)) BasementFloorYIndex = cellCountUpToNow;
3200 12 : } else if (thisPartition.thisRegionType == RegionType::XSide) {
3201 1 : if (present(XIndex)) XIndex = cellCountUpToNow;
3202 1 : this->XIndex = XIndex;
3203 11 : } else if (thisPartition.thisRegionType == RegionType::XSideWall) {
3204 1 : if (present(XWallIndex)) XWallIndex = cellCountUpToNow;
3205 1 : this->XWallIndex = XWallIndex;
3206 10 : } else if (thisPartition.thisRegionType == RegionType::ZSide) {
3207 1 : if (present(ZIndex)) ZIndex = cellCountUpToNow;
3208 1 : this->ZIndex = ZIndex;
3209 9 : } else if (thisPartition.thisRegionType == RegionType::ZSideWall) {
3210 1 : if (present(ZWallIndex)) ZWallIndex = cellCountUpToNow;
3211 1 : this->ZWallIndex = ZWallIndex;
3212 8 : } else if (thisPartition.thisRegionType == RegionType::HorizInsXSide) {
3213 1 : if (present(InsulationXIndex)) InsulationXIndex = cellCountUpToNow;
3214 1 : this->InsulationXIndex = InsulationXIndex;
3215 7 : } else if (thisPartition.thisRegionType == RegionType::HorizInsZSide) {
3216 1 : if (present(InsulationZIndex)) InsulationZIndex = cellCountUpToNow;
3217 1 : this->InsulationZIndex = InsulationZIndex;
3218 6 : } else if (thisPartition.thisRegionType == RegionType::FloorInside) {
3219 1 : if (present(YFloorIndex)) YFloorIndex = cellCountUpToNow;
3220 1 : this->YFloorIndex = YFloorIndex;
3221 5 : } else if (thisPartition.thisRegionType == RegionType::UnderFloor) {
3222 1 : if (present(YIndex)) YIndex = cellCountUpToNow;
3223 1 : this->YIndex = YIndex;
3224 4 : } else if (thisPartition.thisRegionType == RegionType::VertInsLowerEdge) {
3225 1 : if (present(InsulationYIndex)) InsulationYIndex = cellCountUpToNow;
3226 1 : this->InsulationYIndex = InsulationYIndex;
3227 : }
3228 :
3229 : // Create region for this partition
3230 12 : auto tempRegion = GridRegion(thisPartition.Min, thisPartition.Max, thisPartition.thisRegionType, tempCellWidths); // (AUTO_OK_OBJ)
3231 12 : this->getCellWidths(tempRegion, tempRegion.thisRegionType);
3232 12 : Regions.push_back(tempRegion);
3233 12 : }
3234 :
3235 : // Create final region
3236 5 : auto &thisPartition = ThesePartitionRegions[ThesePartitionRegions.size() - 1];
3237 5 : auto tempRegion = GridRegion(thisPartition.Max, DirExtentMax, DirDirection, tempCellWidths); // (AUTO_OK_OBJ)
3238 5 : this->getCellWidths(tempRegion, tempRegion.thisRegionType);
3239 5 : Regions.push_back(tempRegion);
3240 :
3241 5 : } else {
3242 : // Need to create a region anyway if no partitions exist
3243 1 : auto tempRegion = GridRegion(0.0, DirExtentMax, DirDirection, tempCellWidths); // (AUTO_OK_OBJ)
3244 1 : this->getCellWidths(tempRegion, tempRegion.thisRegionType);
3245 1 : Regions.push_back(tempRegion);
3246 1 : }
3247 6 : }
3248 :
3249 : #pragma clang diagnostic pop
3250 :
3251 6 : std::vector<Real64> CreateBoundaryList(std::vector<GridRegion> const &RegionList, Real64 const DirExtentMax, RegionType const DirDirection)
3252 : {
3253 :
3254 : // FUNCTION INFORMATION:
3255 : // AUTHOR Edwin Lee
3256 : // DATE WRITTEN Summer 2011
3257 : // MODIFIED na
3258 : // RE-ENGINEERED na
3259 :
3260 6 : std::vector<Real64> RetVal;
3261 33 : for (auto const &thisRegion : RegionList) {
3262 27 : switch (thisRegion.thisRegionType) {
3263 12 : case RegionType::Pipe:
3264 : case RegionType::BasementFloor:
3265 : case RegionType::BasementWall:
3266 : case RegionType::XSide:
3267 : case RegionType::XSideWall:
3268 : case RegionType::ZSide:
3269 : case RegionType::ZSideWall:
3270 : case RegionType::HorizInsXSide:
3271 : case RegionType::HorizInsZSide:
3272 : case RegionType::FloorInside:
3273 : case RegionType::UnderFloor:
3274 : case RegionType::VertInsLowerEdge:
3275 12 : RetVal.push_back(thisRegion.Min);
3276 12 : break;
3277 15 : default:
3278 15 : if (thisRegion.thisRegionType == DirDirection) {
3279 15 : Real64 StartingPointCounter = thisRegion.Min;
3280 67 : for (auto const &cellWidth : thisRegion.CellWidths) {
3281 52 : RetVal.push_back(StartingPointCounter);
3282 52 : StartingPointCounter += cellWidth;
3283 : }
3284 : }
3285 : }
3286 : }
3287 6 : RetVal.push_back(DirExtentMax);
3288 6 : return RetVal;
3289 0 : }
3290 :
3291 2 : void Domain::createCellArray(std::vector<Real64> const &XBoundaryPoints,
3292 : std::vector<Real64> const &YBoundaryPoints,
3293 : std::vector<Real64> const &ZBoundaryPoints)
3294 : {
3295 :
3296 : // SUBROUTINE INFORMATION:
3297 : // AUTHOR Edwin Lee
3298 : // DATE WRITTEN Summer 2011
3299 : // MODIFIED na
3300 : // RE-ENGINEERED na
3301 :
3302 2 : int TotNumCells = 0;
3303 2 : int NumCutawayBasementCells = 0;
3304 2 : int NumInsulationCells = 0;
3305 2 : int NumGroundSurfaceCells = 0;
3306 :
3307 : //'subtract 2 in each dimension:
3308 : //' one for zero based array
3309 : //' one because the boundary points contain one entry more than the number of cells WITHIN the domain
3310 2 : this->x_max_index = XBoundaryPoints.size() - 2;
3311 2 : this->y_max_index = YBoundaryPoints.size() - 2;
3312 2 : this->z_max_index = ZBoundaryPoints.size() - 2;
3313 2 : this->Cells.allocate({0, this->x_max_index}, {0, this->y_max_index}, {0, this->z_max_index});
3314 :
3315 2 : int MaxBasementXNodeIndex = this->BasementZone.BasementWallXIndex;
3316 2 : int MinBasementYNodeIndex = this->BasementZone.BasementFloorYIndex;
3317 2 : int MinXIndex = this->XIndex;
3318 2 : int YIndex = this->YIndex;
3319 2 : int MinZIndex = this->ZIndex;
3320 2 : int XWallIndex = this->XWallIndex;
3321 2 : int YFloorIndex = this->YFloorIndex;
3322 2 : int ZWallIndex = this->ZWallIndex;
3323 2 : int InsulationXIndex = this->InsulationXIndex;
3324 2 : int InsulationYIndex = this->InsulationYIndex;
3325 2 : int InsulationZIndex = this->InsulationZIndex;
3326 :
3327 2 : auto &cells = this->Cells;
3328 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
3329 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
3330 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
3331 3615 : auto &cell = cells(X, Y, Z);
3332 :
3333 : //'set up x-direction variables
3334 3615 : int CellXIndex = X; //'zero based index
3335 3615 : Real64 CellXMinValue = XBoundaryPoints[X]; //'left wall x-value
3336 3615 : Real64 CellXMaxValue = XBoundaryPoints[X + 1]; //'right wall x-value
3337 3615 : Real64 CellXCenter = (CellXMinValue + CellXMaxValue) / 2;
3338 3615 : Real64 CellWidth = CellXMaxValue - CellXMinValue;
3339 :
3340 : //'set up y-direction variables
3341 3615 : int CellYIndex = Y; //'zero based index
3342 3615 : Real64 CellYMinValue = YBoundaryPoints[Y]; //'bottom wall y-value
3343 3615 : Real64 CellYMaxValue = YBoundaryPoints[Y + 1]; //'top wall y-value
3344 3615 : Real64 CellYCenter = (CellYMinValue + CellYMaxValue) / 2;
3345 3615 : Real64 CellHeight = CellYMaxValue - CellYMinValue;
3346 :
3347 : //'set up z-direction variables
3348 3615 : int CellZIndex = Z; //'zero based index
3349 3615 : Real64 CellZMinValue = ZBoundaryPoints[Z]; //'lower z value
3350 3615 : Real64 CellZMaxValue = ZBoundaryPoints[Z + 1]; //'higher z value
3351 3615 : Real64 CellZCenter = (CellZMinValue + CellZMaxValue) / 2;
3352 :
3353 : //'set up an extent class for this cell
3354 : CellExtents theseCellExtents =
3355 3615 : CellExtents(CellXMaxValue, CellYMaxValue, CellZMaxValue, CellXMinValue, CellYMinValue, CellZMinValue);
3356 :
3357 : //'set up centroid, index, and overall size
3358 3615 : Point3DReal Centroid = Point3DReal(CellXCenter, CellYCenter, CellZCenter);
3359 3615 : Point3DInteger CellIndeces = Point3DInteger(CellXIndex, CellYIndex, CellZIndex);
3360 3615 : RectangleF XYRectangle = RectangleF(CellXMinValue, CellYMinValue, CellWidth, CellHeight);
3361 :
3362 : //'determine cell type
3363 3615 : CellType cellType = CellType::Invalid;
3364 :
3365 : //'if this is a pipe node, some flags are needed
3366 3615 : bool pipeCell = false;
3367 3615 : int NumRadialCells = -1;
3368 :
3369 : // Adiabatic behavior is now achieved in the SetupCellNeighbors routine, these are simply farfield for now.
3370 3615 : CellType const ZWallCellType = CellType::FarfieldBoundary;
3371 3615 : CellType const UnderBasementBoundary = CellType::FarfieldBoundary;
3372 :
3373 : //'apply boundary conditions
3374 :
3375 : // For zone-coupled ground domain
3376 3615 : if (this->HasZoneCoupledSlab) {
3377 :
3378 : // Assign all cells common between on-grade and in-grade cases first
3379 : // This should be all X/Z-side interface, vertical insulation, far-field,
3380 : // ground surface, and ground/zone interface cells
3381 0 : if (CellXIndex == MinXIndex && CellZIndex >= MinZIndex) { // Z side interface
3382 : // Check if vertical insulation present
3383 0 : if (this->VertInsPresentFlag) {
3384 0 : if (CellYIndex <= this->y_max_index && CellYIndex >= InsulationYIndex) { // Check depth of vertical insulation
3385 0 : cellType = CellType::VertInsulation;
3386 0 : ++NumInsulationCells;
3387 : }
3388 0 : } else if (CellYIndex == this->y_max_index) {
3389 0 : cellType = CellType::GroundSurface;
3390 0 : ++NumGroundSurfaceCells;
3391 : }
3392 0 : } else if (CellZIndex == MinZIndex && CellXIndex >= MinXIndex) { // X side interface
3393 0 : if (this->VertInsPresentFlag) { // Check if vertical insulation present
3394 0 : if (CellYIndex <= this->y_max_index && CellYIndex >= InsulationYIndex) { // Check depth of vertical insulation
3395 0 : cellType = CellType::VertInsulation;
3396 0 : ++NumInsulationCells;
3397 : }
3398 0 : } else if (CellYIndex == this->y_max_index) {
3399 0 : cellType = CellType::GroundSurface;
3400 0 : ++NumGroundSurfaceCells;
3401 : }
3402 0 : } else if (CellYIndex == y_max_index) {
3403 0 : if (CellXIndex <= MinXIndex || CellZIndex <= MinZIndex) { // Ground surface
3404 0 : cellType = CellType::GroundSurface;
3405 0 : ++NumGroundSurfaceCells;
3406 0 : } else if (CellXIndex >= MinXIndex || CellZIndex >= MinZIndex) { // Zone-ground interface
3407 0 : cellType = CellType::ZoneGroundInterface;
3408 : }
3409 : }
3410 :
3411 0 : if (CellYIndex == 0 || CellXIndex == 0 || CellZIndex == 0) { // Farfield boundary
3412 0 : cellType = CellType::FarfieldBoundary;
3413 : }
3414 :
3415 : // Assign different cells between in-grade and on-grade cases
3416 0 : if (this->slabPosition == SlabPosition::InGrade) { // In-grade case
3417 : // This will assign the slab cells and horizontal insulation
3418 :
3419 0 : if (CellZIndex > MinZIndex && CellXIndex > MinXIndex) { // Cells inside bounds of slab
3420 0 : if (CellYIndex >= YIndex && CellYIndex < y_max_index) { // Slab cells
3421 0 : cellType = CellType::Slab;
3422 0 : } else if (CellYIndex == (YIndex - 1)) {
3423 0 : if (this->HorizIns == HorizInsulation::Full) { // Full under-slab insulation
3424 0 : cellType = CellType::HorizInsulation;
3425 0 : } else if (this->HorizIns == HorizInsulation::Perimeter) { // Perimeter only under-slab insulation
3426 0 : if (CellZIndex < InsulationZIndex || CellXIndex < InsulationXIndex) {
3427 0 : cellType = CellType::HorizInsulation;
3428 : }
3429 : }
3430 : }
3431 : }
3432 :
3433 : } else { // Slab-on grade
3434 : // Nothing should happen. Interface cells should already be set.
3435 : // Under that are 'General' field cells that should be caught later.
3436 : }
3437 :
3438 3615 : } else if (this->HasZoneCoupledBasement) { // basement model, zone-coupled
3439 : // Set the appropriate cell type
3440 3375 : if (CellYIndex == 0) { // Farfield cells
3441 225 : cellType = CellType::FarfieldBoundary;
3442 3150 : } else if (CellXIndex > XWallIndex && CellZIndex > ZWallIndex) { // Basement cutaway
3443 1134 : if (CellYIndex <= this->y_max_index && CellYIndex > YFloorIndex) { // General basement cells
3444 729 : cellType = CellType::BasementCutaway;
3445 : // Not counting basement cutaway cells.
3446 405 : } else if (CellYIndex == YFloorIndex) { // Basement Floor cells
3447 81 : cellType = CellType::BasementFloor;
3448 324 : } else if (CellYIndex == YIndex) {
3449 : // Check if horizontal insulation present
3450 81 : if (this->HorizIns == HorizInsulation::Full) { // Entire underfloor insulated
3451 0 : cellType = CellType::HorizInsulation;
3452 0 : ++NumInsulationCells;
3453 81 : } else if (this->HorizIns == HorizInsulation::Perimeter) { // Perimeter insulation
3454 81 : if (CellXIndex < InsulationXIndex || CellZIndex < InsulationZIndex) {
3455 56 : cellType = CellType::HorizInsulation;
3456 56 : ++NumInsulationCells;
3457 : }
3458 : }
3459 : }
3460 2016 : } else if ((CellXIndex == XWallIndex && CellZIndex > ZWallIndex) ||
3461 210 : (CellZIndex == ZWallIndex && CellXIndex > XWallIndex)) { // Basement Walls
3462 252 : if (CellYIndex <= this->y_max_index && CellYIndex > YFloorIndex) {
3463 162 : cellType = CellType::BasementWall;
3464 : }
3465 1764 : } else if ((CellXIndex == MinXIndex && CellZIndex > ZWallIndex) ||
3466 210 : (CellZIndex == MinZIndex && CellXIndex > XWallIndex)) { // Insulation cells
3467 252 : if (CellYIndex <= this->y_max_index && CellYIndex > YFloorIndex) {
3468 : // Check if vertical insulation present
3469 162 : if (this->VertInsPresentFlag) {
3470 162 : if (InsulationYIndex != 0) { // Partial vertical insulation
3471 162 : if (CellYIndex > InsulationYIndex) {
3472 72 : cellType = CellType::VertInsulation;
3473 72 : ++NumInsulationCells;
3474 : }
3475 : } else { // Vertical insulation extends to depth of basement floor
3476 0 : cellType = CellType::VertInsulation;
3477 0 : ++NumInsulationCells;
3478 : }
3479 : }
3480 : }
3481 1512 : } else if (CellYIndex == this->y_max_index) { // Surface cells
3482 108 : cellType = CellType::GroundSurface;
3483 108 : ++NumGroundSurfaceCells;
3484 1404 : } else if (CellXIndex == 0 || CellZIndex == 0) { // Farfield boundary
3485 377 : cellType = CellType::FarfieldBoundary;
3486 : }
3487 240 : } else if (CellXIndex == MaxBasementXNodeIndex && CellYIndex == MinBasementYNodeIndex) {
3488 0 : cellType = CellType::BasementCorner;
3489 240 : } else if (CellXIndex == MaxBasementXNodeIndex && CellYIndex > MinBasementYNodeIndex) {
3490 0 : cellType = CellType::BasementWall;
3491 240 : } else if (CellXIndex < MaxBasementXNodeIndex && CellYIndex == MinBasementYNodeIndex) {
3492 0 : cellType = CellType::BasementFloor;
3493 240 : } else if (CellXIndex < MaxBasementXNodeIndex && CellYIndex > MinBasementYNodeIndex) {
3494 0 : cellType = CellType::BasementCutaway;
3495 : // Not counting basement cutaway cells
3496 240 : } else if (CellYIndex == Y_end) {
3497 48 : cellType = CellType::GroundSurface;
3498 48 : ++NumGroundSurfaceCells;
3499 192 : } else if (CellXIndex == 0) {
3500 24 : if (this->HasBasement && Y > 0) {
3501 0 : cellType = UnderBasementBoundary; //'this must come after the basement cutaway ELSEIF branch
3502 : } else {
3503 24 : cellType = CellType::FarfieldBoundary;
3504 : }
3505 168 : } else if (CellXIndex == X_end || CellYIndex == 0) {
3506 60 : cellType = CellType::FarfieldBoundary;
3507 108 : } else if (CellZIndex == 0 || CellZIndex == Z_end) {
3508 36 : cellType = ZWallCellType;
3509 : }
3510 :
3511 : //'check to see if this is a pipe node...
3512 3615 : Real64 InsulationThickness(0.0);
3513 3615 : Real64 RadialMeshThickness(0.0);
3514 3615 : bool HasInsulation(false);
3515 3615 : RadialSizing PipeSizing;
3516 3615 : Circuit *circuitReference = nullptr;
3517 3843 : for (auto const &thisCircuit : this->circuits) {
3518 702 : for (auto &segment : thisCircuit->pipeSegments) {
3519 474 : if (XYRectangle.contains(segment->PipeLocation)) {
3520 : //'inform the cell that it is a pipe node
3521 12 : cellType = CellType::Pipe;
3522 : //'inform the cell of which pipe it contains
3523 12 : pipeCell = true;
3524 : //'inform the cell of which pipe circuit contains it
3525 12 : circuitReference = thisCircuit;
3526 : //'inform the pipe of what cell it is inside
3527 12 : segment->initPipeCells(CellXIndex, CellYIndex);
3528 : //'set the number of cells to be generated in this near-pipe region
3529 12 : NumRadialCells = thisCircuit->NumRadialCells;
3530 : //'exit the pipe counter loop
3531 12 : goto CircuitLoop_exit;
3532 : }
3533 : }
3534 : }
3535 3615 : CircuitLoop_exit:;
3536 :
3537 : //'if it still isn't anything, then it is just an interior node
3538 3615 : switch (cellType) {
3539 729 : case CellType::BasementCutaway:
3540 729 : ++NumCutawayBasementCells;
3541 729 : break;
3542 1629 : case CellType::Invalid:
3543 1629 : cellType = CellType::GeneralField;
3544 : // fallthrough
3545 2886 : default:
3546 2886 : ++TotNumCells;
3547 : }
3548 :
3549 : // if we were found on a pipe circuit, get some things for convenience
3550 3615 : if (circuitReference) {
3551 12 : if (circuitReference->HasInsulation) {
3552 0 : InsulationThickness = circuitReference->InsulationSize.thickness();
3553 : }
3554 12 : PipeSizing = circuitReference->PipeSize;
3555 12 : RadialMeshThickness = circuitReference->RadialMeshThickness;
3556 12 : HasInsulation = circuitReference->HasInsulation;
3557 : }
3558 :
3559 : //'instantiate the cell class
3560 3615 : cell.X_min = theseCellExtents.Xmin;
3561 3615 : cell.X_max = theseCellExtents.xMax;
3562 3615 : cell.Y_min = theseCellExtents.Ymin;
3563 3615 : cell.Y_max = theseCellExtents.yMax;
3564 3615 : cell.Z_min = theseCellExtents.Zmin;
3565 3615 : cell.Z_max = theseCellExtents.zMax;
3566 3615 : cell.X_index = CellIndeces.X;
3567 3615 : cell.Y_index = CellIndeces.Y;
3568 3615 : cell.Z_index = CellIndeces.Z;
3569 3615 : cell.Centroid = Centroid;
3570 3615 : cell.cellType = cellType;
3571 :
3572 3615 : if (pipeCell) {
3573 24 : cell.PipeCellData = CartesianPipeCellInformation(cell.X_max - cell.X_min,
3574 : PipeSizing,
3575 : NumRadialCells,
3576 : cell.depth(),
3577 : InsulationThickness,
3578 : RadialMeshThickness,
3579 12 : HasInsulation);
3580 : }
3581 :
3582 : } //'z
3583 : } //'y
3584 : } //'x
3585 :
3586 2 : this->NumDomainCells = TotNumCells;
3587 2 : this->NumGroundSurfCells = NumGroundSurfaceCells;
3588 2 : this->NumInsulationCells = NumInsulationCells;
3589 2 : }
3590 :
3591 2 : void Domain::setupCellNeighbors()
3592 : {
3593 :
3594 : // SUBROUTINE INFORMATION:
3595 : // AUTHOR Edwin Lee
3596 : // DATE WRITTEN Summer 2011
3597 : // MODIFIED na
3598 : // RE-ENGINEERED na
3599 :
3600 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3601 : Real64 CellRightCentroidX;
3602 : Real64 CellRightLeftWallX;
3603 : Real64 CellLeftCentroidX;
3604 : Real64 CellLeftRightWallX;
3605 : Real64 LeftCellCentroidX;
3606 : Real64 LeftCellRightWallX;
3607 : Real64 RightCellCentroidX;
3608 : Real64 RightCellLeftWallX;
3609 : Real64 UpperCellCentroidY;
3610 : Real64 UpperCellLowerWallY;
3611 : Real64 LowerCellCentroidY;
3612 : Real64 LowerCellUpperWallY;
3613 : Real64 UpperZCellCentroidZ;
3614 : Real64 UpperZCellLowerWallZ;
3615 : Real64 LowerZCellCentroidZ;
3616 : Real64 LowerZCellUpperWallZ;
3617 :
3618 2 : auto const &cells = this->Cells;
3619 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
3620 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
3621 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
3622 3615 : auto const &cell = cells(X, Y, Z);
3623 :
3624 : //'for convenience
3625 3615 : Real64 const &ThisCellCentroidX = cell.Centroid.X;
3626 3615 : Real64 const &ThisCellCentroidY = cell.Centroid.Y;
3627 3615 : Real64 const &ThisCellCentroidZ = cell.Centroid.Z;
3628 3615 : Real64 ThisAdiabaticMultiplier = 1.0;
3629 3615 : Real64 ThisAdiabaticMultiplierMirror = 1.0;
3630 :
3631 : //'setup east/west cell neighbors
3632 3615 : if (X == 0) {
3633 255 : CellRightCentroidX = cells(X + 1, Y, Z).Centroid.X;
3634 255 : CellRightLeftWallX = cells(X + 1, Y, Z).X_min;
3635 : // on the X=0 face, the only adiabatic cases are:
3636 : // 1) For a non-zone-coupled basement simulation, where the under basement X=0 cells are adiabatic -- cutaways will also get
3637 : // adiabatic, but who cares?
3638 255 : if (((!this->HasZoneCoupledSlab) && (!this->HasZoneCoupledBasement) && (this->HasBasement))) {
3639 0 : ThisAdiabaticMultiplier = 2.0;
3640 0 : ThisAdiabaticMultiplierMirror = 0.0;
3641 : }
3642 255 : this->addNeighborInformation(X,
3643 : Y,
3644 : Z,
3645 : Direction::PositiveX,
3646 255 : CellRightLeftWallX - ThisCellCentroidX,
3647 : CellRightCentroidX - CellRightLeftWallX,
3648 : ThisAdiabaticMultiplier);
3649 255 : this->addNeighborInformation(X, Y, Z, Direction::NegativeX, 0.0, 0.0, ThisAdiabaticMultiplierMirror);
3650 3360 : } else if (X == this->x_max_index) {
3651 : // on the X=XMAX face, the adiabatic cases are:
3652 : // 1) if we are doing a zone coupled slab/basement simulation where we quartered the domain
3653 255 : if (this->HasZoneCoupledSlab || this->HasZoneCoupledBasement) {
3654 225 : ThisAdiabaticMultiplier = 2.0;
3655 225 : ThisAdiabaticMultiplierMirror = 0.0;
3656 : }
3657 255 : CellLeftCentroidX = cells(X - 1, Y, Z).Centroid.X;
3658 255 : CellLeftRightWallX = cells(X - 1, Y, Z).X_max;
3659 255 : this->addNeighborInformation(X,
3660 : Y,
3661 : Z,
3662 : Direction::NegativeX,
3663 255 : ThisCellCentroidX - CellLeftRightWallX,
3664 : CellLeftRightWallX - CellLeftCentroidX,
3665 : ThisAdiabaticMultiplier);
3666 255 : this->addNeighborInformation(X, Y, Z, Direction::PositiveX, 0.0, 0.0, ThisAdiabaticMultiplierMirror);
3667 : } else {
3668 3105 : LeftCellCentroidX = cells(X - 1, Y, Z).Centroid.X;
3669 3105 : LeftCellRightWallX = cells(X - 1, Y, Z).X_max;
3670 3105 : RightCellCentroidX = cells(X + 1, Y, Z).Centroid.X;
3671 3105 : RightCellLeftWallX = cells(X + 1, Y, Z).X_min;
3672 3105 : this->addNeighborInformation(X,
3673 : Y,
3674 : Z,
3675 : Direction::NegativeX,
3676 3105 : ThisCellCentroidX - LeftCellRightWallX,
3677 : LeftCellRightWallX - LeftCellCentroidX,
3678 : ThisAdiabaticMultiplier);
3679 3105 : this->addNeighborInformation(X,
3680 : Y,
3681 : Z,
3682 : Direction::PositiveX,
3683 3105 : RightCellLeftWallX - ThisCellCentroidX,
3684 : RightCellCentroidX - RightCellLeftWallX,
3685 : ThisAdiabaticMultiplier);
3686 : }
3687 :
3688 : // Reset for the Y direction assignments
3689 3615 : ThisAdiabaticMultiplier = 1.0;
3690 3615 : ThisAdiabaticMultiplierMirror = 1.0;
3691 :
3692 : //'setup north/south cell neighbors
3693 3615 : if (Y == 0) {
3694 273 : UpperCellCentroidY = cells(X, Y + 1, Z).Centroid.Y;
3695 273 : UpperCellLowerWallY = cells(X, Y + 1, Z).Y_min;
3696 : // on the Y=0 face, the only adiabatic cases are:
3697 : // 1) NONE
3698 273 : this->addNeighborInformation(X,
3699 : Y,
3700 : Z,
3701 : Direction::PositiveY,
3702 273 : UpperCellLowerWallY - ThisCellCentroidY,
3703 : UpperCellCentroidY - UpperCellLowerWallY,
3704 : ThisAdiabaticMultiplier);
3705 273 : this->addNeighborInformation(X, Y, Z, Direction::NegativeY, 0.0, 0.0, ThisAdiabaticMultiplierMirror);
3706 3342 : } else if (Y == this->y_max_index) {
3707 273 : LowerCellCentroidY = cells(X, Y - 1, Z).Centroid.Y;
3708 273 : LowerCellUpperWallY = cells(X, Y - 1, Z).Y_max;
3709 : // on the Y=YMAX face, the only adiabatic cases are:
3710 : // 1) NONE
3711 273 : this->addNeighborInformation(X,
3712 : Y,
3713 : Z,
3714 : Direction::NegativeY,
3715 273 : ThisCellCentroidY - LowerCellUpperWallY,
3716 : LowerCellUpperWallY - LowerCellCentroidY,
3717 : ThisAdiabaticMultiplier);
3718 273 : this->addNeighborInformation(X, Y, Z, Direction::PositiveY, 0.0, 0.0, ThisAdiabaticMultiplierMirror);
3719 : } else {
3720 3069 : UpperCellCentroidY = cells(X, Y + 1, Z).Centroid.Y;
3721 3069 : LowerCellCentroidY = cells(X, Y - 1, Z).Centroid.Y;
3722 3069 : UpperCellLowerWallY = cells(X, Y + 1, Z).Y_min;
3723 3069 : LowerCellUpperWallY = cells(X, Y - 1, Z).Y_max;
3724 3069 : this->addNeighborInformation(X,
3725 : Y,
3726 : Z,
3727 : Direction::NegativeY,
3728 3069 : ThisCellCentroidY - LowerCellUpperWallY,
3729 : LowerCellUpperWallY - LowerCellCentroidY,
3730 : ThisAdiabaticMultiplier);
3731 3069 : this->addNeighborInformation(X,
3732 : Y,
3733 : Z,
3734 : Direction::PositiveY,
3735 3069 : UpperCellLowerWallY - ThisCellCentroidY,
3736 : UpperCellCentroidY - UpperCellLowerWallY,
3737 : ThisAdiabaticMultiplier);
3738 : }
3739 :
3740 : // Reset for the Z direction assignments
3741 3615 : ThisAdiabaticMultiplier = 1.0;
3742 3615 : ThisAdiabaticMultiplierMirror = 1.0;
3743 :
3744 : //'setup forward/backward cell neighbors
3745 3615 : if (Z == 0) {
3746 265 : UpperZCellCentroidZ = cells(X, Y, Z + 1).Centroid.Z;
3747 265 : UpperZCellLowerWallZ = cells(X, Y, Z + 1).Z_min;
3748 : // on the Z=0 face, the only adiabatic cases are:
3749 : // 1) for a non-zone-related simulation, such as for a standalone ground HX, or if we have the regular HasBasement
3750 : // simulation
3751 265 : if (((!this->HasZoneCoupledSlab) && (!this->HasZoneCoupledBasement))) {
3752 40 : ThisAdiabaticMultiplier = 2.0;
3753 40 : ThisAdiabaticMultiplierMirror = 0.0;
3754 : }
3755 265 : this->addNeighborInformation(X,
3756 : Y,
3757 : Z,
3758 : Direction::PositiveZ,
3759 265 : UpperZCellLowerWallZ - ThisCellCentroidZ,
3760 : UpperZCellCentroidZ - UpperZCellLowerWallZ,
3761 : ThisAdiabaticMultiplier);
3762 265 : this->addNeighborInformation(X, Y, Z, Direction::NegativeZ, 0.0, 0.0, ThisAdiabaticMultiplierMirror);
3763 3350 : } else if (Z == this->z_max_index) {
3764 265 : LowerZCellCentroidZ = cells(X, Y, Z - 1).Centroid.Z;
3765 265 : LowerZCellUpperWallZ = cells(X, Y, Z - 1).Z_max;
3766 : // on the Z=ZMAX face, the only adiabatic cases are:
3767 : // 1) this face is always adiabatic?
3768 : // if ( ) {
3769 265 : ThisAdiabaticMultiplier = 2.0;
3770 265 : ThisAdiabaticMultiplierMirror = 0.0;
3771 : //}
3772 265 : this->addNeighborInformation(X,
3773 : Y,
3774 : Z,
3775 : Direction::NegativeZ,
3776 265 : ThisCellCentroidZ - LowerZCellUpperWallZ,
3777 : LowerZCellUpperWallZ - LowerZCellCentroidZ,
3778 : ThisAdiabaticMultiplier);
3779 265 : this->addNeighborInformation(X, Y, Z, Direction::PositiveZ, 0.0, 0.0, ThisAdiabaticMultiplierMirror);
3780 : } else {
3781 3085 : LowerZCellCentroidZ = cells(X, Y, Z - 1).Centroid.Z;
3782 3085 : UpperZCellCentroidZ = cells(X, Y, Z + 1).Centroid.Z;
3783 3085 : UpperZCellLowerWallZ = cells(X, Y, Z + 1).Z_min;
3784 3085 : LowerZCellUpperWallZ = cells(X, Y, Z - 1).Z_max;
3785 3085 : this->addNeighborInformation(X,
3786 : Y,
3787 : Z,
3788 : Direction::NegativeZ,
3789 3085 : ThisCellCentroidZ - LowerZCellUpperWallZ,
3790 : LowerZCellUpperWallZ - LowerZCellCentroidZ,
3791 : ThisAdiabaticMultiplier);
3792 3085 : this->addNeighborInformation(X,
3793 : Y,
3794 : Z,
3795 : Direction::PositiveZ,
3796 3085 : UpperZCellLowerWallZ - ThisCellCentroidZ,
3797 : UpperZCellCentroidZ - UpperZCellLowerWallZ,
3798 : ThisAdiabaticMultiplier);
3799 : }
3800 : }
3801 : }
3802 : }
3803 2 : }
3804 :
3805 21690 : void Domain::addNeighborInformation(int const X,
3806 : int const Y,
3807 : int const Z,
3808 : Direction const direction,
3809 : Real64 const ThisCentroidToNeighborWall,
3810 : Real64 const ThisWallToNeighborCentroid,
3811 : Real64 const ThisAdiabaticMultiplier)
3812 : {
3813 : // SUBROUTINE INFORMATION:
3814 : // AUTHOR Edwin Lee
3815 : // DATE WRITTEN Summer 2011
3816 : // MODIFIED na
3817 : // RE-ENGINEERED na
3818 21690 : auto &thisNeighborInfo = this->Cells(X, Y, Z).NeighborInfo[direction];
3819 21690 : thisNeighborInfo.direction = direction;
3820 21690 : thisNeighborInfo.ThisCentroidToNeighborWall = ThisCentroidToNeighborWall;
3821 21690 : thisNeighborInfo.ThisWallToNeighborCentroid = ThisWallToNeighborCentroid;
3822 21690 : thisNeighborInfo.adiabaticMultiplier = ThisAdiabaticMultiplier;
3823 21690 : }
3824 :
3825 2 : void Domain::setupPipeCircuitInOutCells()
3826 : {
3827 :
3828 : // SUBROUTINE INFORMATION:
3829 : // AUTHOR Edwin Lee
3830 : // DATE WRITTEN Summer 2011
3831 : // MODIFIED na
3832 : // RE-ENGINEERED na
3833 :
3834 2 : auto const &cells = this->Cells;
3835 3 : for (auto &thisCircuit : this->circuits) {
3836 :
3837 1 : int SegmentInletCellX = 0;
3838 1 : int SegmentInletCellY = 0;
3839 1 : int SegmentInletCellZ = 0;
3840 1 : int SegmentOutletCellX = 0;
3841 1 : int SegmentOutletCellY = 0;
3842 1 : int SegmentOutletCellZ = 0;
3843 1 : int CircuitInletCellX = 0;
3844 1 : int CircuitInletCellY = 0;
3845 1 : int CircuitInletCellZ = 0;
3846 1 : int CircuitOutletCellX = 0;
3847 1 : int CircuitOutletCellY = 0;
3848 1 : int CircuitOutletCellZ = 0;
3849 :
3850 1 : bool CircuitInletCellSet = false;
3851 :
3852 3 : for (auto const &segment : thisCircuit->pipeSegments) {
3853 2 : switch (segment->FlowDirection) {
3854 1 : case SegmentFlow::IncreasingZ:
3855 1 : SegmentInletCellX = segment->PipeCellCoordinates.X;
3856 1 : SegmentInletCellY = segment->PipeCellCoordinates.Y;
3857 1 : SegmentInletCellZ = 0;
3858 1 : SegmentOutletCellX = segment->PipeCellCoordinates.X;
3859 1 : SegmentOutletCellY = segment->PipeCellCoordinates.Y;
3860 1 : SegmentOutletCellZ = this->z_max_index;
3861 1 : break;
3862 1 : case SegmentFlow::DecreasingZ:
3863 1 : SegmentInletCellX = segment->PipeCellCoordinates.X;
3864 1 : SegmentInletCellY = segment->PipeCellCoordinates.Y;
3865 1 : SegmentInletCellZ = this->z_max_index;
3866 1 : SegmentOutletCellX = segment->PipeCellCoordinates.X;
3867 1 : SegmentOutletCellY = segment->PipeCellCoordinates.Y;
3868 1 : SegmentOutletCellZ = 0;
3869 1 : break;
3870 0 : default:
3871 0 : assert(false);
3872 : }
3873 2 : if (!CircuitInletCellSet) {
3874 1 : CircuitInletCellX = SegmentInletCellX;
3875 1 : CircuitInletCellY = SegmentInletCellY;
3876 1 : CircuitInletCellZ = SegmentInletCellZ;
3877 1 : CircuitInletCellSet = true;
3878 : }
3879 2 : CircuitOutletCellX = SegmentOutletCellX;
3880 2 : CircuitOutletCellY = SegmentOutletCellY;
3881 2 : CircuitOutletCellZ = SegmentOutletCellZ;
3882 : }
3883 :
3884 1 : thisCircuit->initInOutCells(cells(CircuitInletCellX, CircuitInletCellY, CircuitInletCellZ),
3885 : cells(CircuitOutletCellX, CircuitOutletCellY, CircuitOutletCellZ));
3886 : }
3887 2 : }
3888 :
3889 9 : int Domain::getCellWidthsCount(RegionType const dir) const
3890 : {
3891 :
3892 : // FUNCTION INFORMATION:
3893 : // AUTHOR Edwin Lee
3894 : // DATE WRITTEN Summer 2011
3895 : // MODIFIED na
3896 : // RE-ENGINEERED na
3897 :
3898 9 : if (dir == RegionType::XDirection) {
3899 4 : return this->Mesh.X.RegionMeshCount;
3900 5 : } else if (dir == RegionType::YDirection) {
3901 3 : return this->Mesh.Y.RegionMeshCount;
3902 2 : } else if (dir == RegionType::ZDirection) {
3903 2 : return this->Mesh.Z.RegionMeshCount;
3904 : } else {
3905 0 : return 1; // it's either a mesh region (X,Y,ZDirection), or it is some form of partition -- so 1
3906 : }
3907 : return 0;
3908 : }
3909 :
3910 27 : void Domain::getCellWidths(GridRegion &g, RegionType const direction) const
3911 : {
3912 :
3913 : // FUNCTION INFORMATION:
3914 : // AUTHOR Edwin Lee
3915 : // DATE WRITTEN Summer 2011
3916 : // MODIFIED na
3917 : // RE-ENGINEERED na
3918 :
3919 : // Object Data
3920 27 : DistributionStructure ThisMesh;
3921 27 : ThisMesh.RegionMeshCount = 0;
3922 27 : ThisMesh.GeometricSeriesCoefficient = 0.0;
3923 :
3924 27 : switch (direction) {
3925 6 : case RegionType::XDirection:
3926 6 : ThisMesh = this->Mesh.X;
3927 6 : break;
3928 5 : case RegionType::YDirection:
3929 5 : ThisMesh = this->Mesh.Y;
3930 5 : break;
3931 4 : case RegionType::ZDirection:
3932 4 : ThisMesh = this->Mesh.Z;
3933 4 : break;
3934 12 : default:
3935 12 : ThisMesh.RegionMeshCount = 1; // it must be a partition type or something
3936 12 : ThisMesh.thisMeshDistribution = MeshDistribution::Uniform;
3937 : // ShowSevereError(state, "Invalid RegionType passed to PlantPipingSystems::Domain::getCellWidths; should be x, y, or z
3938 : // direction only." ); ShowContinueError(state, "This is a developer problem, as the code should never reach this point." );
3939 : // ShowFatalError(state, "EnergyPlus aborts due to the previous severe error" );
3940 : }
3941 :
3942 : // just one cell for extremely tight regions
3943 27 : if ((g.Max - g.Min) < 0.00001) {
3944 0 : ThisMesh.RegionMeshCount = 1;
3945 0 : ThisMesh.thisMeshDistribution = MeshDistribution::Uniform;
3946 : }
3947 27 : assert(g.Max > g.Min);
3948 :
3949 27 : Real64 GridWidth = g.Max - g.Min;
3950 :
3951 27 : if (ThisMesh.thisMeshDistribution == MeshDistribution::Uniform) {
3952 27 : if (this->HasZoneCoupledSlab && g.thisRegionType == RegionType::YDirection && g.Max == this->Extents.yMax) { // Slab region
3953 0 : Real64 const CellWidth = GridWidth / this->NumSlabCells;
3954 0 : for (int I = 0; I <= this->NumSlabCells - 1; ++I) {
3955 0 : g.CellWidths.push_back(CellWidth);
3956 : }
3957 0 : } else {
3958 : // we have it quite simple
3959 27 : assert(ThisMesh.RegionMeshCount > 0);
3960 27 : Real64 const CellWidth = GridWidth / ThisMesh.RegionMeshCount;
3961 91 : for (int I = 0; I <= ThisMesh.RegionMeshCount - 1; ++I) {
3962 64 : g.CellWidths.push_back(CellWidth);
3963 : }
3964 : }
3965 0 : } else if (ThisMesh.thisMeshDistribution == MeshDistribution::SymmetricGeometric) {
3966 :
3967 : //'then apply this "direction"'s conditions to generate a cell width array
3968 : //'first get the total number of cells on this half of the region
3969 0 : int NumCellsOnEachSide = ThisMesh.RegionMeshCount / 2; // Already validated to be an even #
3970 : //'calculate geometric series
3971 0 : Real64 SummationTerm = 0.0;
3972 0 : for (int I = 1; I <= NumCellsOnEachSide; ++I) {
3973 0 : SummationTerm += std::pow(ThisMesh.GeometricSeriesCoefficient, I - 1);
3974 : }
3975 : //'set up a list of cell widths for this region
3976 0 : Real64 CellWidth = (GridWidth / 2) / SummationTerm;
3977 0 : g.CellWidths.push_back(CellWidth);
3978 0 : for (int I = 1; I <= NumCellsOnEachSide - 1; ++I) {
3979 0 : CellWidth *= ThisMesh.GeometricSeriesCoefficient;
3980 0 : g.CellWidths.push_back(CellWidth);
3981 : }
3982 0 : for (int I = NumCellsOnEachSide - 1; I >= 0; --I) {
3983 0 : g.CellWidths.push_back(g.CellWidths[I]);
3984 : }
3985 :
3986 0 : } else if (ThisMesh.thisMeshDistribution == MeshDistribution::Geometric) {
3987 :
3988 0 : int NumCells = ThisMesh.RegionMeshCount;
3989 0 : if (g.thisRegionType == RegionType::XDirection || g.thisRegionType == RegionType::ZDirection) {
3990 : //'calculate geometric series
3991 0 : Real64 SummationTerm = 0.0;
3992 0 : for (int I = 1; I <= NumCells; ++I) {
3993 0 : SummationTerm += std::pow(ThisMesh.GeometricSeriesCoefficient, I - 1);
3994 : }
3995 0 : Real64 CellWidth = GridWidth / SummationTerm;
3996 0 : if (g.Min == 0) {
3997 : // Ground region to the left of the slab will have cells expanding to the left
3998 : // adding them here moving forward, then reversing it to get this effect
3999 : // Possible spot for diffs
4000 0 : g.CellWidths.push_back(CellWidth);
4001 0 : for (int I = 0; I < NumCells; ++I) {
4002 0 : CellWidth *= ThisMesh.GeometricSeriesCoefficient;
4003 0 : g.CellWidths.push_back(CellWidth);
4004 : }
4005 0 : std::reverse(g.CellWidths.begin(), g.CellWidths.end());
4006 : } else {
4007 : // Slab region will have cells expanding to the right
4008 0 : g.CellWidths.push_back(CellWidth);
4009 0 : for (int I = 1; I <= NumCells - 1; ++I) {
4010 0 : CellWidth *= ThisMesh.GeometricSeriesCoefficient;
4011 0 : g.CellWidths.push_back(CellWidth);
4012 : }
4013 : }
4014 0 : } else if (g.thisRegionType == RegionType::YDirection) {
4015 : // Assign uniform cell thickness to the slab cells.
4016 0 : if (g.Max == this->Extents.yMax) {
4017 0 : NumCells = this->NumSlabCells;
4018 0 : Real64 CellWidth = GridWidth / NumCells;
4019 0 : for (int I = 0; I <= NumCells - 1; ++I) {
4020 0 : g.CellWidths.push_back(CellWidth);
4021 : }
4022 : } else {
4023 : //'calculate geometric series
4024 0 : Real64 SummationTerm = 0.0;
4025 0 : for (int I = 1; I <= NumCells; ++I) {
4026 0 : SummationTerm += std::pow(ThisMesh.GeometricSeriesCoefficient, I - 1);
4027 : }
4028 0 : Real64 CellWidth = GridWidth / SummationTerm;
4029 : // Ground region under the slab will have cells expanding as we go down
4030 : // adding them here moving forward, then reversing it to get this effect
4031 : // Possible spot for diffs
4032 0 : g.CellWidths.push_back(CellWidth);
4033 0 : for (int I = 1; I < NumCells; ++I) {
4034 0 : CellWidth *= ThisMesh.GeometricSeriesCoefficient;
4035 0 : g.CellWidths.push_back(CellWidth);
4036 : }
4037 0 : std::reverse(g.CellWidths.begin(), g.CellWidths.end());
4038 : }
4039 : }
4040 : }
4041 27 : }
4042 :
4043 1 : void Domain::PerformIterationLoop(EnergyPlusData &state)
4044 : {
4045 :
4046 : // SUBROUTINE INFORMATION:
4047 : // AUTHOR Edwin Lee
4048 : // DATE WRITTEN Summer 2011
4049 : // MODIFIED na
4050 : // RE-ENGINEERED na
4051 :
4052 : // Always do start of time step inits
4053 1 : this->DoStartOfTimeStepInitializations(state);
4054 :
4055 : // Begin iterating for this time step
4056 1 : for (int IterationIndex = 1; IterationIndex <= this->SimControls.MaxIterationsPerTS; ++IterationIndex) {
4057 1 : this->ShiftTemperaturesForNewIteration();
4058 1 : if (this->DomainNeedsSimulation) this->PerformTemperatureFieldUpdate(state);
4059 1 : bool FinishedIterationLoop = false;
4060 1 : this->DoEndOfIterationOperations(state, FinishedIterationLoop);
4061 1 : if (FinishedIterationLoop) break;
4062 : }
4063 :
4064 : // Update the basement surface temperatures, if any
4065 1 : if (this->HasBasement || this->HasZoneCoupledBasement) {
4066 1 : this->UpdateBasementSurfaceTemperatures(state);
4067 : }
4068 :
4069 : // Update the slab surface temperatures, if any
4070 1 : if (this->HasZoneCoupledSlab) {
4071 0 : this->UpdateZoneSurfaceTemperatures(state);
4072 : }
4073 1 : }
4074 :
4075 1 : void Domain::PerformIterationLoop(EnergyPlusData &state, Circuit *thisCircuit)
4076 : {
4077 :
4078 : // SUBROUTINE INFORMATION:
4079 : // AUTHOR Edwin Lee
4080 : // DATE WRITTEN Summer 2011
4081 : // MODIFIED na
4082 : // RE-ENGINEERED na
4083 :
4084 : // Always do start of time step inits
4085 1 : this->DoStartOfTimeStepInitializations(state, thisCircuit);
4086 :
4087 : // Prepare the pipe circuit for calculations, but we'll actually do calcs at the iteration level
4088 1 : if (this->HasAPipeCircuit) {
4089 1 : this->PreparePipeCircuitSimulation(thisCircuit);
4090 : }
4091 :
4092 : // Begin iterating for this time step
4093 1 : for (int IterationIndex = 1; IterationIndex <= this->SimControls.MaxIterationsPerTS; ++IterationIndex) {
4094 :
4095 1 : this->ShiftTemperaturesForNewIteration();
4096 :
4097 1 : if (this->HasAPipeCircuit) {
4098 1 : this->PerformPipeCircuitSimulation(state, thisCircuit);
4099 : }
4100 :
4101 1 : if (this->DomainNeedsSimulation) this->PerformTemperatureFieldUpdate(state);
4102 1 : bool FinishedIterationLoop = false;
4103 1 : this->DoEndOfIterationOperations(state, FinishedIterationLoop);
4104 :
4105 1 : if (FinishedIterationLoop) break;
4106 : }
4107 :
4108 : // Update the basement surface temperatures, if any
4109 1 : if (this->HasBasement || this->HasZoneCoupledBasement) {
4110 0 : this->UpdateBasementSurfaceTemperatures(state);
4111 : }
4112 :
4113 : // Update the slab surface temperatures, if any
4114 1 : if (this->HasZoneCoupledSlab) {
4115 0 : this->UpdateZoneSurfaceTemperatures(state);
4116 : }
4117 1 : }
4118 :
4119 2 : void Domain::PerformTemperatureFieldUpdate(EnergyPlusData &state)
4120 : {
4121 :
4122 : // SUBROUTINE INFORMATION:
4123 : // AUTHOR Edwin Lee
4124 : // DATE WRITTEN Summer 2011
4125 : // MODIFIED na
4126 : // RE-ENGINEERED na
4127 :
4128 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
4129 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
4130 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
4131 3615 : auto &cell = this->Cells(X, Y, Z);
4132 :
4133 3615 : switch (cell.cellType) {
4134 12 : case CellType::Pipe:
4135 : //'pipes are simulated separately
4136 12 : break;
4137 1757 : case CellType::GeneralField:
4138 : case CellType::Slab:
4139 : case CellType::HorizInsulation:
4140 : case CellType::VertInsulation:
4141 1757 : cell.Temperature = this->EvaluateFieldCellTemperature(cell);
4142 1757 : break;
4143 156 : case CellType::GroundSurface:
4144 156 : cell.Temperature = this->EvaluateGroundSurfaceTemperature(state, cell);
4145 156 : break;
4146 718 : case CellType::FarfieldBoundary:
4147 718 : cell.Temperature = this->EvaluateFarfieldBoundaryTemperature(state, cell);
4148 718 : break;
4149 243 : case CellType::BasementWall:
4150 : case CellType::BasementCorner:
4151 : case CellType::BasementFloor:
4152 : // basement model, zone-coupled. Call EvaluateZoneInterfaceTemperature routine to handle timestep/hourly simulation.
4153 243 : if (this->HasZoneCoupledBasement) {
4154 243 : cell.Temperature = this->EvaluateZoneInterfaceTemperature(cell);
4155 : } else { // FHX model
4156 0 : cell.Temperature = this->EvaluateBasementCellTemperature(state, cell);
4157 : }
4158 243 : break;
4159 0 : case CellType::ZoneGroundInterface:
4160 0 : cell.Temperature = this->EvaluateZoneInterfaceTemperature(cell);
4161 0 : break;
4162 729 : case CellType::BasementCutaway:
4163 : // it's ok to not simulate this one
4164 729 : break;
4165 0 : default:
4166 0 : assert(false);
4167 : }
4168 : }
4169 : }
4170 : }
4171 2 : }
4172 :
4173 1757 : Real64 Domain::EvaluateFieldCellTemperature(CartesianCell &cell)
4174 : {
4175 :
4176 : // FUNCTION INFORMATION:
4177 : // AUTHOR Edwin Lee
4178 : // DATE WRITTEN Summer 2011
4179 : // MODIFIED na
4180 : // RE-ENGINEERED na
4181 :
4182 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4183 1757 : Real64 Numerator = 0.0;
4184 1757 : Real64 Denominator = 0.0;
4185 1757 : Real64 AdiabaticMultiplier = 1.0;
4186 :
4187 : // add effect from cell history
4188 1757 : Numerator += cell.Temperature_PrevTimeStep;
4189 1757 : ++Denominator;
4190 :
4191 : // determine the neighbor types based on cell location
4192 1757 : int NumFieldCells = 0, NumBoundaryCells = 0;
4193 1757 : this->EvaluateCellNeighborDirections(cell, NumFieldCells, NumBoundaryCells);
4194 :
4195 : // loop across each direction in the simulation
4196 12093 : for (int DirectionCounter = 0; DirectionCounter <= NumFieldCells; ++DirectionCounter) {
4197 :
4198 10336 : Real64 NeighborTemp = 0.0;
4199 10336 : Real64 Resistance = 0.0;
4200 10336 : Direction CurDirection = this->NeighborFieldCells[DirectionCounter];
4201 :
4202 : //'evaluate the transient expression terms
4203 10336 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4204 :
4205 10336 : Numerator += AdiabaticMultiplier * (cell.Beta / Resistance) * NeighborTemp;
4206 10336 : Denominator += AdiabaticMultiplier * (cell.Beta / Resistance);
4207 : }
4208 :
4209 : //'now that we have passed all directions, update the temperature
4210 1757 : return Numerator / Denominator;
4211 : }
4212 :
4213 156 : Real64 Domain::EvaluateGroundSurfaceTemperature(EnergyPlusData &state, CartesianCell &cell)
4214 : {
4215 :
4216 : // FUNCTION INFORMATION:
4217 : // AUTHOR Edwin Lee
4218 : // DATE WRITTEN Summer 2011
4219 : // MODIFIED na
4220 : // RE-ENGINEERED na
4221 :
4222 : // FUNCTION PARAMETER DEFINITIONS:
4223 156 : Real64 constexpr AirDensity(1.22521); // '[kg/m3]
4224 156 : Real64 constexpr AirSpecificHeat(1003); // '[J/kg-K]
4225 : // evapotranspiration parameters
4226 156 : Real64 constexpr MeanSolarConstant(0.08196); // 1367 [W/m2], entered in [MJ/m2-minute]
4227 156 : Real64 constexpr A_s(0.25); // ?
4228 156 : Real64 constexpr B_s(0.5); // ?
4229 156 : Real64 constexpr Absor_Corrected(0.77);
4230 156 : Real64 const Convert_Wm2_To_MJhrmin(3600.0 / 1000000.0);
4231 156 : Real64 const Convert_MJhrmin_To_Wm2(1.0 / Convert_Wm2_To_MJhrmin);
4232 156 : Real64 constexpr Rho_water(998.0); // [kg/m3]
4233 :
4234 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4235 156 : Real64 NeighborTemp = 0.0;
4236 : Real64 IncidentHeatGain;
4237 : Real64 Latitude_Degrees; // Latitude, degrees N
4238 : Real64 StMeridian_Degrees; // Standard meridian, degrees W -- note it is degrees E in DataEnvironment
4239 : Real64 Longitude_Degrees; // Longitude, degrees W -- note it is degrees E in DataEnvironment
4240 : // evapotranspiration calculated values
4241 : Real64 Latitude_Radians;
4242 : Real64 DayOfYear;
4243 : Real64 HourOfDay;
4244 : Real64 CurSecondsIntoToday;
4245 : Real64 dr;
4246 : Real64 Declination;
4247 : Real64 b_SC;
4248 : Real64 Sc;
4249 : Real64 Hour_Angle;
4250 : Real64 X_sunset;
4251 : Real64 Sunset_Angle;
4252 : Real64 Solar_Angle_1;
4253 : Real64 Solar_Angle_2;
4254 : Real64 QRAD_A;
4255 : Real64 QRAD_SO;
4256 : Real64 Ratio_SO;
4257 : Real64 IncidentSolar_MJhrmin;
4258 : Real64 AbsorbedIncidentSolar_MJhrmin;
4259 : Real64 VaporPressureSaturated_kPa;
4260 : Real64 VaporPressureActual_kPa;
4261 : Real64 QRAD_NL;
4262 : Real64 NetIncidentRadiation_MJhr; // [MJ/hr]
4263 : Real64 NetIncidentRadiation_Wm2; // [W/m2]
4264 : Real64 CN;
4265 : Real64 G_hr;
4266 : Real64 Cd;
4267 : Real64 Slope_S;
4268 : Real64 Pressure;
4269 : Real64 PsychrometricConstant;
4270 : Real64 EvapotransFluidLoss_mmhr;
4271 : Real64 EvapotransFluidLoss_mhr;
4272 : Real64 LatentHeatVaporization;
4273 : Real64 EvapotransHeatLoss_MJhrmin; // [MJ/m2-hr]
4274 : Real64 EvapotransHeatLoss_Wm2; // [W/m2]
4275 : Real64 CurAirTempK;
4276 : Real64 GroundCoverCoefficient;
4277 :
4278 : // retrieve information from E+ globals
4279 156 : Latitude_Degrees = state.dataEnvrn->Latitude;
4280 156 : StMeridian_Degrees = -state.dataEnvrn->TimeZoneMeridian; // Standard meridian, degrees W
4281 156 : Longitude_Degrees = -state.dataEnvrn->Longitude; // Longitude, degrees W
4282 :
4283 : // retrieve any information from input data structure
4284 156 : GroundCoverCoefficient = this->Moisture.GroundCoverCoefficient;
4285 :
4286 : // initialize values
4287 156 : Real64 AdiabaticMultiplier = 1.0;
4288 156 : Real64 Numerator = 0.0;
4289 156 : Real64 Denominator = 0.0;
4290 156 : Real64 Resistance = 0.0;
4291 156 : Real64 Beta = cell.Beta;
4292 156 : Real64 ThisNormalArea = cell.normalArea(Direction::PositiveY);
4293 :
4294 : //'add effect from previous time step
4295 156 : Numerator += cell.Temperature_PrevTimeStep;
4296 156 : ++Denominator;
4297 :
4298 : // now that we aren't infinitesimal, we need to determine the neighbor types based on cell location
4299 156 : int NumFieldCells = 0, NumBoundaryCells = 0;
4300 156 : this->EvaluateCellNeighborDirections(cell, NumFieldCells, NumBoundaryCells);
4301 :
4302 : // loop over all regular neighbor cells, check if we have adiabatic on opposite surface
4303 870 : for (int DirectionCounter = 0; DirectionCounter <= NumFieldCells; ++DirectionCounter) {
4304 714 : Direction CurDirection = this->NeighborFieldCells[DirectionCounter];
4305 :
4306 : // Use the multiplier ( either 1 or 2 ) to calculate the neighbor cell effects
4307 714 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4308 714 : Numerator = AdiabaticMultiplier * Numerator + (Beta / Resistance) * NeighborTemp;
4309 714 : Denominator = AdiabaticMultiplier * Denominator + (Beta / Resistance);
4310 : }
4311 :
4312 : // do all non-adiabatic boundary types here
4313 378 : for (int DirectionCounter = 0; DirectionCounter <= NumBoundaryCells; ++DirectionCounter) {
4314 222 : Direction CurDirection = this->NeighborBoundaryCells[DirectionCounter];
4315 :
4316 : // For Zone-coupled slab or basement configuration
4317 222 : if (this->HasZoneCoupledSlab || this->HasZoneCoupledBasement) {
4318 : //-x-direction will always be a farfield boundary
4319 : //-z will also be a farfield boundary
4320 : //+x and +z will be handled above
4321 : //-y will always be a neighbor cell, so it is handled above
4322 : //+y will always be the outdoor air
4323 146 : if (CurDirection == Direction::NegativeX || CurDirection == Direction::NegativeZ) {
4324 : // always farfield
4325 30 : this->EvaluateFarfieldCharacteristics(state, cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4326 30 : Numerator += (Beta / Resistance) * NeighborTemp;
4327 30 : Denominator += (Beta / Resistance);
4328 116 : } else if (CurDirection == Direction::PositiveY) {
4329 : // convection at the surface
4330 108 : if (state.dataEnvrn->WindSpeed > 0.1) {
4331 0 : Resistance = 208.0 / (AirDensity * AirSpecificHeat * state.dataEnvrn->WindSpeed * ThisNormalArea);
4332 0 : Numerator += (Beta / Resistance) * this->Cur.CurAirTemp;
4333 0 : Denominator += (Beta / Resistance);
4334 : }
4335 8 : } else if (CurDirection == Direction::NegativeY) {
4336 0 : assert(false); // debug error, can't get here!
4337 : }
4338 : } else { // FHX model
4339 : // x-direction will always be a farfield boundary
4340 : // z-direction will be handled above -- adiabatic
4341 : //-y we don't handle here because -y will always be a neighbor cell, so handled above
4342 : //+y will always be the outdoor air
4343 76 : if ((CurDirection == Direction::PositiveX) || (CurDirection == Direction::NegativeX)) {
4344 : // always farfield
4345 12 : this->EvaluateFarfieldCharacteristics(state, cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4346 12 : Numerator += (Beta / Resistance) * NeighborTemp;
4347 12 : Denominator += (Beta / Resistance);
4348 64 : } else if ((CurDirection == Direction::PositiveZ) || (CurDirection == Direction::NegativeZ)) {
4349 : // debug error, can't get here
4350 48 : } else if (CurDirection == Direction::PositiveY) {
4351 : // convection at the surface
4352 48 : if (state.dataEnvrn->WindSpeed > 0.1) {
4353 0 : Resistance = 208.0 / (AirDensity * AirSpecificHeat * state.dataEnvrn->WindSpeed * ThisNormalArea);
4354 0 : Numerator += (Beta / Resistance) * this->Cur.CurAirTemp;
4355 0 : Denominator += (Beta / Resistance);
4356 : } else {
4357 : // Future development should include additional natural convection effects here
4358 : }
4359 0 : } else if (CurDirection == Direction::NegativeY) {
4360 0 : assert(false); // debug error, can't get here!
4361 : }
4362 : }
4363 : }
4364 :
4365 : // Latitude, converted to radians...positive for northern hemisphere, [radians]
4366 156 : Latitude_Radians = Constant::Pi / 180.0 * Latitude_Degrees;
4367 :
4368 : // The day of year at this point in the simulation
4369 156 : DayOfYear = int(this->Cur.CurSimTimeSeconds / Constant::rSecsInDay);
4370 :
4371 : // The number of seconds into the current day
4372 156 : CurSecondsIntoToday = int(mod(this->Cur.CurSimTimeSeconds, Constant::rSecsInDay));
4373 :
4374 : // The number of hours into today
4375 156 : HourOfDay = int(CurSecondsIntoToday / Constant::rSecsInHour);
4376 :
4377 : // For convenience convert to Kelvin once
4378 156 : CurAirTempK = this->Cur.CurAirTemp + 273.15;
4379 :
4380 : // Calculate some angles
4381 156 : dr = 1.0 + 0.033 * std::cos(2.0 * Constant::Pi * DayOfYear / 365.0);
4382 156 : Declination = 0.409 * std::sin(2.0 * Constant::Pi / 365.0 * DayOfYear - 1.39);
4383 156 : b_SC = 2.0 * Constant::Pi * (DayOfYear - 81.0) / 364.0;
4384 156 : Sc = 0.1645 * std::sin(2.0 * b_SC) - 0.1255 * std::cos(b_SC) - 0.025 * std::sin(b_SC);
4385 156 : Hour_Angle = Constant::Pi / 12.0 * (((HourOfDay - 0.5) + 0.06667 * (StMeridian_Degrees - Longitude_Degrees) + Sc) - 12.0);
4386 :
4387 : // Calculate sunset something, and constrain to a minimum of 0.000001
4388 156 : X_sunset = 1.0 - pow_2(std::tan(Latitude_Radians)) * pow_2(std::tan(Declination));
4389 156 : X_sunset = max(X_sunset, 0.000001);
4390 :
4391 : // Find sunset angle
4392 156 : Sunset_Angle = Constant::Pi / 2.0 - std::atan(-std::tan(Latitude_Radians) * std::tan(Declination) / std::sqrt(X_sunset));
4393 :
4394 : // Find solar angles
4395 156 : Solar_Angle_1 = Hour_Angle - Constant::Pi / 24.0;
4396 156 : Solar_Angle_2 = Hour_Angle + Constant::Pi / 24.0;
4397 :
4398 : // Constrain solar angles
4399 156 : if (Solar_Angle_1 < -Sunset_Angle) Solar_Angle_1 = -Sunset_Angle;
4400 156 : if (Solar_Angle_2 < -Sunset_Angle) Solar_Angle_2 = -Sunset_Angle;
4401 156 : if (Solar_Angle_1 > Sunset_Angle) Solar_Angle_1 = Sunset_Angle;
4402 156 : if (Solar_Angle_2 > Sunset_Angle) Solar_Angle_2 = Sunset_Angle;
4403 156 : if (Solar_Angle_1 > Solar_Angle_2) Solar_Angle_1 = Solar_Angle_2;
4404 :
4405 : // Convert input solar radiation [w/m2] into units for ET model, [MJ/hr-min]
4406 156 : IncidentSolar_MJhrmin = this->Cur.CurIncidentSolar * Convert_Wm2_To_MJhrmin;
4407 :
4408 : // Calculate another Q term...
4409 156 : QRAD_A = 12.0 * 60.0 / Constant::Pi * MeanSolarConstant * dr *
4410 156 : ((Solar_Angle_2 - Solar_Angle_1) * std::sin(Latitude_Radians) * std::sin(Declination) +
4411 156 : std::cos(Latitude_Radians) * std::cos(Declination) * (std::sin(Solar_Angle_2) - std::sin(Solar_Angle_1)));
4412 :
4413 : // Calculate another Q term...
4414 156 : QRAD_SO = (A_s + B_s + 0.00002 * state.dataEnvrn->Elevation) * QRAD_A;
4415 :
4416 : // Correct the Qrad term ... better way??
4417 156 : if (IncidentSolar_MJhrmin < 0.01) {
4418 156 : Ratio_SO = 0.0;
4419 : } else {
4420 0 : if (QRAD_SO != 0.0) {
4421 0 : Ratio_SO = IncidentSolar_MJhrmin / QRAD_SO;
4422 : } else {
4423 : // I used logic below to choose value, divide by 0 = infinity, so value = 1, not sure if correct...
4424 0 : Ratio_SO = 1.0;
4425 : }
4426 : }
4427 :
4428 : // Constrain Ratio_SO
4429 156 : Ratio_SO = min(Ratio_SO, 1.0);
4430 156 : Ratio_SO = max(Ratio_SO, 0.3);
4431 :
4432 : // Calculate another Q term, [MJ/hr-min]
4433 156 : AbsorbedIncidentSolar_MJhrmin = Absor_Corrected * IncidentSolar_MJhrmin;
4434 :
4435 : // Calculate saturated vapor pressure, [kPa]
4436 156 : VaporPressureSaturated_kPa = 0.6108 * std::exp(17.27 * this->Cur.CurAirTemp / (this->Cur.CurAirTemp + 237.3));
4437 :
4438 : // Calculate actual vapor pressure, [kPa]
4439 156 : VaporPressureActual_kPa = VaporPressureSaturated_kPa * this->Cur.CurRelativeHumidity / 100.0;
4440 :
4441 : // Calculate another Q term, [MJ/m2-hr]
4442 156 : QRAD_NL = 2.042E-10 * pow_4(CurAirTempK) * (0.34 - 0.14 * std::sqrt(VaporPressureActual_kPa)) * (1.35 * Ratio_SO - 0.35);
4443 :
4444 : // Calculate another Q term, [MJ/hr]
4445 156 : NetIncidentRadiation_MJhr = AbsorbedIncidentSolar_MJhrmin - QRAD_NL;
4446 :
4447 : // ?
4448 156 : CN = 37.0;
4449 :
4450 : // Check whether there was sun
4451 156 : if (NetIncidentRadiation_MJhr < 0.0) {
4452 156 : G_hr = 0.5 * NetIncidentRadiation_MJhr;
4453 156 : Cd = 0.96;
4454 : } else {
4455 0 : G_hr = 0.1 * NetIncidentRadiation_MJhr;
4456 0 : Cd = 0.24;
4457 : }
4458 :
4459 : // Just For Check
4460 : // Lu Xing Sep 22 2009
4461 :
4462 156 : Slope_S = 2503.0 * std::exp(17.27 * this->Cur.CurAirTemp / (this->Cur.CurAirTemp + 237.3)) / pow_2(this->Cur.CurAirTemp + 237.3);
4463 156 : Pressure = 98.0;
4464 156 : PsychrometricConstant = 0.665e-3 * Pressure;
4465 :
4466 : // Evapotranspiration constant, [mm/hr]
4467 156 : EvapotransFluidLoss_mmhr =
4468 156 : (GroundCoverCoefficient * Slope_S * (NetIncidentRadiation_MJhr - G_hr) +
4469 156 : PsychrometricConstant * (CN / CurAirTempK) * this->Cur.CurWindSpeed * (VaporPressureSaturated_kPa - VaporPressureActual_kPa)) /
4470 156 : (Slope_S + PsychrometricConstant * (1 + Cd * this->Cur.CurWindSpeed));
4471 :
4472 : // Convert units, [m/hr]
4473 156 : EvapotransFluidLoss_mhr = EvapotransFluidLoss_mmhr / 1000.0;
4474 :
4475 : // Calculate latent heat, [MJ/kg]
4476 : // Full formulation is cubic: L(T) = -0.0000614342 * T**3 + 0.00158927 * T**2 - 2.36418 * T + 2500.79[5]
4477 : // In: Cubic fit to Table 2.1,p.16, Textbook: R.R.Rogers & M.K. Yau, A Short Course in Cloud Physics, 3e,(1989), Pergamon press
4478 : // But a linear relation should suffice;
4479 : // note-for now using the previous time step temperature as an approximation to help ensure stability
4480 156 : LatentHeatVaporization = 2.501 - 2.361e-3 * cell.Temperature_PrevTimeStep;
4481 :
4482 : // Calculate evapotranspiration heat loss, [MJ/m2-hr]
4483 156 : EvapotransHeatLoss_MJhrmin = Rho_water * EvapotransFluidLoss_mhr * LatentHeatVaporization;
4484 :
4485 : // Convert net incident solar units, [W/m2]
4486 156 : NetIncidentRadiation_Wm2 = NetIncidentRadiation_MJhr * Convert_MJhrmin_To_Wm2;
4487 :
4488 : // Convert evapotranspiration units, [W/m2]
4489 156 : EvapotransHeatLoss_Wm2 = EvapotransHeatLoss_MJhrmin * Convert_MJhrmin_To_Wm2;
4490 :
4491 : // Calculate overall net heat ?gain? into the cell, [W]
4492 156 : IncidentHeatGain = (NetIncidentRadiation_Wm2 - EvapotransHeatLoss_Wm2) * ThisNormalArea;
4493 :
4494 : // Add any solar/evapotranspiration heat gain here
4495 156 : Numerator += Beta * IncidentHeatGain;
4496 :
4497 : // Calculate the return temperature and leave
4498 156 : return Numerator / Denominator;
4499 : }
4500 :
4501 0 : Real64 Domain::EvaluateBasementCellTemperature(EnergyPlusData &state, CartesianCell &cell)
4502 : {
4503 :
4504 : // FUNCTION INFORMATION:
4505 : // AUTHOR Edwin Lee
4506 : // DATE WRITTEN Summer 2011
4507 : // MODIFIED na
4508 : // RE-ENGINEERED na
4509 :
4510 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4511 : Real64 Beta;
4512 0 : Real64 NeighborTemp = 0.0;
4513 : Real64 HeatFlux;
4514 0 : Real64 Numerator = 0.0;
4515 0 : Real64 Denominator = 0.0;
4516 0 : Real64 Resistance = 0.0;
4517 0 : Real64 AdiabaticMultiplier = 1.0;
4518 :
4519 : // add effect from previous time step
4520 0 : Numerator += cell.Temperature_PrevTimeStep;
4521 0 : ++Denominator;
4522 :
4523 0 : switch (cell.cellType) {
4524 0 : case CellType::BasementWall:
4525 :
4526 : // we will only have heat flux from the basement wall and heat conduction to the +x cell
4527 :
4528 : // This is actually only a half-cell since the basement wall slices right through the middle in one direction
4529 0 : Beta = cell.Beta / 2.0;
4530 :
4531 : // get the average basement wall heat flux and add it to the tally
4532 0 : HeatFlux = this->GetBasementWallHeatFlux(state);
4533 0 : Numerator += Beta * HeatFlux * cell.height();
4534 :
4535 : // then get the +x conduction to continue the heat balance
4536 0 : this->EvaluateNeighborCharacteristics(cell, Direction::PositiveX, NeighborTemp, Resistance, AdiabaticMultiplier);
4537 0 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4538 0 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4539 :
4540 0 : break;
4541 :
4542 0 : case CellType::BasementFloor:
4543 :
4544 : // we will only have heat flux from the basement floor and heat conduction to the lower cell
4545 :
4546 : // This is actually only a half-cell since the basement wall slices right through the middle in one direction
4547 0 : Beta = cell.Beta / 2.0;
4548 :
4549 : // get the average basement floor heat flux and add it to the tally
4550 0 : HeatFlux = this->GetBasementFloorHeatFlux(state);
4551 0 : Numerator += Beta * HeatFlux * cell.width();
4552 :
4553 : // then get the -y conduction to continue the heat balance
4554 0 : this->EvaluateNeighborCharacteristics(cell, Direction::NegativeY, NeighborTemp, Resistance, AdiabaticMultiplier);
4555 0 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4556 0 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4557 :
4558 0 : break;
4559 :
4560 0 : case CellType::BasementCorner:
4561 :
4562 : // This is actually only a three-quarter-cell since the basement wall slices right through the middle in both directions
4563 0 : Beta = cell.Beta * 3.0 / 4.0;
4564 :
4565 : // we will only have heat conduction to the +x and -y cells
4566 0 : this->EvaluateNeighborCharacteristics(cell, Direction::PositiveX, NeighborTemp, Resistance, AdiabaticMultiplier);
4567 0 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4568 0 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4569 :
4570 0 : this->EvaluateNeighborCharacteristics(cell, Direction::NegativeY, NeighborTemp, Resistance, AdiabaticMultiplier);
4571 0 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4572 0 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4573 :
4574 0 : break;
4575 :
4576 0 : default:
4577 : // other cell types are not calculated here
4578 0 : break;
4579 : }
4580 :
4581 0 : return Numerator / Denominator;
4582 : }
4583 :
4584 718 : Real64 Domain::EvaluateFarfieldBoundaryTemperature(EnergyPlusData &state, CartesianCell &cell)
4585 : {
4586 :
4587 : // FUNCTION INFORMATION:
4588 : // AUTHOR Edwin Lee
4589 : // DATE WRITTEN Summer 2011
4590 : // MODIFIED na
4591 : // RE-ENGINEERED na
4592 :
4593 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4594 718 : Real64 Numerator = 0.0;
4595 718 : Real64 Denominator = 0.0;
4596 718 : Real64 Resistance = 0.0;
4597 718 : Real64 AdiabaticMultiplier = 1.0;
4598 718 : Real64 Beta = cell.Beta;
4599 :
4600 : // add effect from previous time step
4601 718 : Numerator += cell.Temperature_PrevTimeStep;
4602 718 : ++Denominator;
4603 :
4604 : // now that we aren't infinitesimal, we need to determine the neighbor types based on cell location
4605 718 : int NumFieldCells = 0, NumBoundaryCells = 0;
4606 718 : this->EvaluateCellNeighborDirections(cell, NumFieldCells, NumBoundaryCells);
4607 :
4608 : // Do all neighbor cells
4609 4169 : for (int DirectionCounter = 0; DirectionCounter <= NumFieldCells; ++DirectionCounter) {
4610 3451 : Direction CurDirection = this->NeighborFieldCells[DirectionCounter];
4611 3451 : Real64 NeighborTemp = 0.0;
4612 3451 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4613 3451 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4614 3451 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4615 : }
4616 :
4617 : // Then all farfield boundaries
4618 1575 : for (int DirectionCounter = 0; DirectionCounter <= NumBoundaryCells; ++DirectionCounter) {
4619 857 : Direction CurDirection = this->NeighborBoundaryCells[DirectionCounter];
4620 857 : Real64 NeighborTemp = 0.0;
4621 857 : this->EvaluateFarfieldCharacteristics(state, cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4622 857 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4623 857 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4624 : }
4625 :
4626 718 : return Numerator / Denominator;
4627 : }
4628 :
4629 243 : Real64 Domain::EvaluateZoneInterfaceTemperature(CartesianCell &cell)
4630 : {
4631 :
4632 : // FUNCTION INFORMATION:
4633 : // AUTHOR Edwin Lee
4634 : // DATE WRITTEN Summer 2011
4635 : // MODIFIED na
4636 : // RE-ENGINEERED na
4637 :
4638 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4639 : Real64 HeatFlux;
4640 : Real64 ConductionArea;
4641 243 : Real64 Numerator = 0.0;
4642 243 : Real64 Denominator = 0.0;
4643 243 : Real64 Resistance = 0.0;
4644 243 : Real64 AdiabaticMultiplier = 1.0;
4645 243 : Real64 Beta = cell.Beta;
4646 :
4647 : // add effect from previous time step
4648 243 : Numerator += cell.Temperature_PrevTimeStep;
4649 243 : ++Denominator;
4650 :
4651 243 : if (cell.cellType == CellType::BasementWall) {
4652 : // Get the average basement wall heat flux and add it to the tally
4653 162 : HeatFlux = this->WallHeatFlux;
4654 162 : if (cell.X_index == this->XWallIndex) {
4655 81 : ConductionArea = cell.depth() * cell.height();
4656 81 : Numerator += Beta * HeatFlux * ConductionArea;
4657 81 : } else if (cell.Z_index == this->ZWallIndex) {
4658 81 : ConductionArea = cell.width() * cell.height();
4659 81 : Numerator += Beta * HeatFlux * ConductionArea;
4660 : }
4661 81 : } else if (cell.cellType == CellType::BasementFloor) {
4662 : // Get the average basement floor heat flux and add it to the tally
4663 81 : HeatFlux = this->FloorHeatFlux;
4664 81 : ConductionArea = cell.width() * cell.depth();
4665 81 : Numerator += Beta * HeatFlux * ConductionArea;
4666 0 : } else if (cell.cellType == CellType::ZoneGroundInterface) {
4667 : // Get the average slab heat flux and add it to the tally
4668 0 : HeatFlux = this->WeightedHeatFlux(cell.X_index, cell.Z_index);
4669 0 : ConductionArea = cell.width() * cell.depth();
4670 0 : Numerator += Beta * HeatFlux * ConductionArea;
4671 : }
4672 :
4673 : // determine the neighbor types based on cell location
4674 243 : int NumFieldCells = 0, NumBoundaryCells = 0;
4675 243 : this->EvaluateCellNeighborDirections(cell, NumFieldCells, NumBoundaryCells);
4676 :
4677 : // loop across each direction in the simulation
4678 1647 : for (int DirectionCounter = 0; DirectionCounter <= NumFieldCells; ++DirectionCounter) {
4679 :
4680 1404 : Real64 NeighborTemp = 0.0;
4681 1404 : Direction CurDirection = this->NeighborFieldCells[DirectionCounter];
4682 :
4683 : // Have to be careful here to make sure heat conduction happens only in the appropriate directions
4684 1404 : if (cell.cellType == CellType::BasementWall) {
4685 : // No heat conduction from the X-side basement wall cell to the +x cell ( basement cutaway )
4686 936 : if (cell.X_index == this->XWallIndex && CurDirection != Direction::PositiveX) {
4687 : // Evaluate the transient expression terms
4688 387 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4689 387 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4690 387 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4691 : }
4692 : // No heat conduction from the Z-side basement wall cell to the +z cell ( basement cutaway )
4693 936 : if (cell.Z_index == this->ZWallIndex && CurDirection != Direction::PositiveZ) {
4694 : // Evaluate the transient expression terms
4695 387 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4696 387 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4697 387 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4698 : }
4699 468 : } else if (cell.cellType == CellType::BasementFloor) {
4700 : // No heat conduction from the basement floor cell to the +y cell ( basement cutaway )
4701 468 : if (CurDirection != Direction::PositiveY) {
4702 : // Evaluate the transient expression terms
4703 387 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4704 387 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4705 387 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4706 : }
4707 0 : } else if (cell.cellType == CellType::ZoneGroundInterface || cell.cellType == CellType::BasementCorner) {
4708 : // Heat conduction in all directions
4709 : // Evaluate the transient expression terms
4710 0 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
4711 0 : Numerator += AdiabaticMultiplier * (Beta / Resistance) * NeighborTemp;
4712 0 : Denominator += AdiabaticMultiplier * (Beta / Resistance);
4713 : }
4714 : }
4715 :
4716 243 : return Numerator / Denominator;
4717 : }
4718 :
4719 2 : Real64 Domain::GetBasementWallHeatFlux(EnergyPlusData &state)
4720 : {
4721 :
4722 : // FUNCTION INFORMATION:
4723 : // AUTHOR Edwin Lee
4724 : // DATE WRITTEN Summer 2011
4725 : // MODIFIED na
4726 : // RE-ENGINEERED na
4727 :
4728 2 : Real64 RunningSummation = 0.0;
4729 2 : unsigned int const numSurfaces = static_cast<unsigned int>(this->BasementZone.WallSurfacePointers.size());
4730 4 : for (auto &surfaceIndex : this->BasementZone.WallSurfacePointers) {
4731 2 : RunningSummation += state.dataHeatBalSurf->SurfQdotConvOutPerArea(surfaceIndex);
4732 : }
4733 2 : return -RunningSummation / numSurfaces; // heat flux is negative here
4734 : }
4735 :
4736 2 : Real64 Domain::GetBasementFloorHeatFlux(EnergyPlusData &state)
4737 : {
4738 :
4739 : // FUNCTION INFORMATION:
4740 : // AUTHOR Edwin Lee
4741 : // DATE WRITTEN Summer 2011
4742 : // MODIFIED na
4743 : // RE-ENGINEERED na
4744 :
4745 2 : Real64 RunningSummation = 0.0;
4746 2 : unsigned int const numSurfaces = static_cast<unsigned int>(this->BasementZone.FloorSurfacePointers.size());
4747 4 : for (auto &surfaceIndex : this->BasementZone.FloorSurfacePointers) {
4748 2 : RunningSummation += state.dataHeatBalSurf->SurfQdotConvOutPerArea(surfaceIndex);
4749 : }
4750 2 : return -RunningSummation / numSurfaces; // heat flux is negative here
4751 : }
4752 :
4753 1 : void Domain::UpdateBasementSurfaceTemperatures(EnergyPlusData &state)
4754 : {
4755 :
4756 : // SUBROUTINE INFORMATION:
4757 : // AUTHOR Edwin Lee
4758 : // DATE WRITTEN Summer 2011
4759 : // MODIFIED na
4760 : // RE-ENGINEERED na
4761 :
4762 : // SUBROUTINE PARAMETER DEFINITIONS:
4763 1 : Real64 constexpr BigNumber(10000.0);
4764 :
4765 : // First the wall
4766 1 : this->BasementWallTemp = this->GetAverageTempByType(state, CellType::BasementWall);
4767 1 : int OSCMIndex = this->BasementZone.WallBoundaryOSCMIndex;
4768 1 : state.dataSurface->OSCM(OSCMIndex).TConv = this->BasementWallTemp;
4769 1 : state.dataSurface->OSCM(OSCMIndex).HConv = BigNumber;
4770 1 : state.dataSurface->OSCM(OSCMIndex).TRad = this->BasementWallTemp;
4771 1 : state.dataSurface->OSCM(OSCMIndex).HRad = 0.0;
4772 :
4773 : // Then the floor
4774 1 : this->BasementFloorTemp = this->GetAverageTempByType(state, CellType::BasementFloor);
4775 1 : OSCMIndex = this->BasementZone.FloorBoundaryOSCMIndex;
4776 1 : state.dataSurface->OSCM(OSCMIndex).TConv = this->BasementFloorTemp;
4777 1 : state.dataSurface->OSCM(OSCMIndex).HConv = BigNumber;
4778 1 : state.dataSurface->OSCM(OSCMIndex).TRad = this->BasementFloorTemp;
4779 1 : state.dataSurface->OSCM(OSCMIndex).HRad = 0.0;
4780 1 : }
4781 :
4782 0 : Real64 Domain::GetZoneInterfaceHeatFlux(EnergyPlusData &state)
4783 : {
4784 :
4785 : // FUNCTION INFORMATION:
4786 : // AUTHOR Edwin Lee
4787 : // DATE WRITTEN Summer 2011
4788 : // MODIFIED na
4789 : // RE-ENGINEERED na
4790 :
4791 0 : Real64 RunningSummation = 0.0;
4792 0 : int const NumSurfaces = this->ZoneCoupledSurfaces.size();
4793 0 : for (auto &z : this->ZoneCoupledSurfaces) {
4794 0 : RunningSummation += state.dataHeatBalSurf->SurfQdotConvOutPerArea(z.IndexInSurfaceArray);
4795 : }
4796 0 : return -RunningSummation / NumSurfaces; // heat flux is negative here
4797 : }
4798 :
4799 0 : void Domain::UpdateZoneSurfaceTemperatures(EnergyPlusData &state)
4800 : {
4801 :
4802 : // SUBROUTINE INFORMATION:
4803 : // AUTHOR
4804 : // DATE WRITTEN
4805 : // MODIFIED na
4806 : // RE-ENGINEERED na
4807 :
4808 : // SUBROUTINE PARAMETER DEFINITIONS:
4809 0 : Real64 constexpr BigNumber(10000.0);
4810 :
4811 0 : this->ZoneCoupledSurfaceTemp = this->GetAverageTempByType(state, CellType::ZoneGroundInterface);
4812 0 : int OSCMIndex = this->ZoneCoupledOSCMIndex;
4813 0 : state.dataSurface->OSCM(OSCMIndex).TConv = this->ZoneCoupledSurfaceTemp;
4814 0 : state.dataSurface->OSCM(OSCMIndex).HConv = BigNumber;
4815 0 : state.dataSurface->OSCM(OSCMIndex).TRad = this->ZoneCoupledSurfaceTemp;
4816 0 : state.dataSurface->OSCM(OSCMIndex).HRad = 0.0;
4817 :
4818 : // Reset the interface heat flux after iteration
4819 0 : this->ResetHeatFluxFlag = true;
4820 0 : }
4821 :
4822 2 : Real64 Domain::GetAverageTempByType(EnergyPlusData &state, CellType const cellType) const
4823 : {
4824 :
4825 : // FUNCTION INFORMATION:
4826 : // AUTHOR Edwin Lee
4827 : // DATE WRITTEN Summer 2011
4828 : // MODIFIED na
4829 : // RE-ENGINEERED na
4830 :
4831 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4832 2 : Real64 RunningSummation = 0.0;
4833 2 : Real64 RunningVolume = 0.0;
4834 :
4835 2 : auto const &cells = Cells;
4836 32 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
4837 480 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
4838 7200 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
4839 6750 : auto const &cell = cells(X, Y, Z);
4840 6750 : if (cell.cellType == cellType) {
4841 243 : Real64 CellVolume = cell.volume();
4842 243 : RunningVolume += CellVolume;
4843 243 : RunningSummation += CellVolume * cell.Temperature;
4844 : }
4845 : }
4846 : }
4847 : }
4848 :
4849 2 : if (RunningVolume <= 0.0) {
4850 0 : ShowFatalError(state, "Domain::GetAverageTempByType calculated zero volume, program aborts");
4851 : }
4852 :
4853 2 : return RunningSummation / RunningVolume;
4854 : }
4855 :
4856 899 : void Domain::EvaluateFarfieldCharacteristics(
4857 : EnergyPlusData &state, CartesianCell &cell, Direction const direction, Real64 &neighbortemp, Real64 &resistance, Real64 &adiabaticMultiplier)
4858 : {
4859 :
4860 : // SUBROUTINE INFORMATION:
4861 : // AUTHOR Edwin Lee
4862 : // DATE WRITTEN Summer 2011
4863 : // MODIFIED na
4864 : // RE-ENGINEERED na
4865 :
4866 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4867 899 : Real64 distance(0.0);
4868 :
4869 899 : switch (direction) {
4870 313 : case Direction::NegativeX:
4871 : case Direction::PositiveX:
4872 313 : distance = (cell.width() / 2.0);
4873 313 : break;
4874 273 : case Direction::NegativeY:
4875 : case Direction::PositiveY:
4876 273 : distance = (cell.height() / 2.0);
4877 273 : break;
4878 313 : case Direction::NegativeZ:
4879 : case Direction::PositiveZ:
4880 313 : distance = (cell.depth() / 2.0);
4881 313 : break;
4882 0 : default:
4883 0 : assert(false);
4884 : }
4885 :
4886 899 : resistance = (distance / 2.0) / (cell.Properties.Conductivity * cell.normalArea(direction));
4887 899 : neighbortemp = this->GetFarfieldTemp(state, cell);
4888 :
4889 899 : adiabaticMultiplier = cell.NeighborInfo[direction].adiabaticMultiplier;
4890 899 : }
4891 :
4892 4514 : Real64 Domain::GetFarfieldTemp(EnergyPlusData &state, CartesianCell const &cell)
4893 : {
4894 :
4895 : // FUNCTION INFORMATION:
4896 : // AUTHOR Edwin Lee
4897 : // DATE WRITTEN Summer 2011
4898 : // MODIFIED na
4899 : // RE-ENGINEERED na
4900 :
4901 4514 : Real64 CurTime = this->Cur.CurSimTimeSeconds;
4902 4514 : Real64 z = this->Extents.yMax - cell.Centroid.Y;
4903 4514 : return this->groundTempModel->getGroundTempAtTimeInSeconds(state, z, CurTime);
4904 : }
4905 :
4906 1 : void Domain::PreparePipeCircuitSimulation(Circuit *thisCircuit)
4907 : {
4908 :
4909 : // SUBROUTINE INFORMATION:
4910 : // AUTHOR Edwin Lee
4911 : // DATE WRITTEN Summer 2011
4912 : // MODIFIED na
4913 : // RE-ENGINEERED na
4914 :
4915 : // SUBROUTINE ARGUMENT DEFINITIONS:
4916 1 : Real64 constexpr StagnantFluidConvCoeff(200.0);
4917 :
4918 : // Setup circuit flow conditions -- convection coefficient
4919 1 : int const CellX = thisCircuit->CircuitInletCell.X;
4920 1 : int const CellY = thisCircuit->CircuitInletCell.Y;
4921 1 : int const CellZ = thisCircuit->CircuitInletCell.Z;
4922 :
4923 : // Look up current fluid properties
4924 1 : Real64 const Density = thisCircuit->CurFluidPropertySet.Density;
4925 1 : Real64 const Viscosity = thisCircuit->CurFluidPropertySet.Viscosity;
4926 1 : Real64 const Conductivity = thisCircuit->CurFluidPropertySet.Conductivity;
4927 1 : Real64 const Prandtl = thisCircuit->CurFluidPropertySet.Prandtl;
4928 :
4929 : // Flow calculations
4930 1 : Real64 const Area_c = (Constant::Pi / 4.0) * pow_2(thisCircuit->PipeSize.InnerDia);
4931 1 : Real64 const Velocity = thisCircuit->CurCircuitFlowRate / (Density * Area_c);
4932 :
4933 : // Determine convection coefficient based on flow conditions
4934 : Real64 ConvCoefficient;
4935 1 : if (Velocity > 0) {
4936 0 : Real64 Reynolds = Density * thisCircuit->PipeSize.InnerDia * Velocity / Viscosity;
4937 : Real64 ExponentTerm;
4938 0 : if (this->Cells(CellX, CellY, CellZ).PipeCellData.Fluid.Temperature > this->Cells(CellX, CellY, CellZ).PipeCellData.Pipe.Temperature) {
4939 0 : ExponentTerm = 0.3;
4940 : } else {
4941 0 : ExponentTerm = 0.4;
4942 : }
4943 0 : Real64 const Nusselt = 0.023 * std::pow(Reynolds, 4.0 / 5.0) * std::pow(Prandtl, ExponentTerm);
4944 0 : ConvCoefficient = Nusselt * Conductivity / thisCircuit->PipeSize.InnerDia;
4945 : } else {
4946 1 : ConvCoefficient = StagnantFluidConvCoeff;
4947 : }
4948 :
4949 : // Assign the convection coefficient
4950 1 : thisCircuit->CurCircuitConvectionCoefficient = ConvCoefficient;
4951 1 : }
4952 :
4953 1 : void Domain::PerformPipeCircuitSimulation(EnergyPlusData &state, Circuit *thisCircuit)
4954 : {
4955 :
4956 : // SUBROUTINE INFORMATION:
4957 : // AUTHOR Edwin Lee
4958 : // DATE WRITTEN Summer 2011
4959 : // MODIFIED na
4960 : // RE-ENGINEERED na
4961 :
4962 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4963 1 : Real64 CircuitCrossTemp = 0.0;
4964 :
4965 : // retrieve initial conditions from the data structure
4966 : // these have been set either by the init routine or by the heat pump routine
4967 1 : Real64 const FlowRate = thisCircuit->CurCircuitFlowRate;
4968 1 : Real64 const EnteringTemp = thisCircuit->CurCircuitInletTemp;
4969 :
4970 : // initialize
4971 1 : int SegmentCellCtr = 0;
4972 1 : unsigned long const NumSegments = thisCircuit->pipeSegments.size();
4973 1 : unsigned long segmentNum = 0;
4974 :
4975 : //'loop across all segments (pipes) of the circuit
4976 1 : auto &cells = this->Cells;
4977 3 : for (auto &segment : thisCircuit->pipeSegments) {
4978 :
4979 2 : segmentNum++;
4980 2 : int StartingZ = 0;
4981 2 : int EndingZ = 0;
4982 2 : int Increment = 0;
4983 :
4984 : //'set simulation flow direction
4985 2 : switch (segment->FlowDirection) {
4986 1 : case SegmentFlow::IncreasingZ:
4987 1 : StartingZ = 0;
4988 1 : EndingZ = this->z_max_index;
4989 1 : Increment = 1;
4990 1 : break;
4991 1 : case SegmentFlow::DecreasingZ:
4992 1 : StartingZ = this->z_max_index;
4993 1 : EndingZ = 0;
4994 1 : Increment = -1;
4995 1 : break;
4996 0 : default:
4997 0 : ShowFatalError(state, "Debug error: invalid flow direction on piping system segment");
4998 : }
4999 :
5000 : //'find the cell we are working on in order to retrieve cell and neighbor information
5001 2 : int PipeX = segment->PipeCellCoordinates.X;
5002 2 : int PipeY = segment->PipeCellCoordinates.Y;
5003 :
5004 : //'loop across all z-direction indeces
5005 2 : int const Zindex_stop(floop_end(StartingZ, EndingZ, Increment));
5006 14 : for (int Zindex = StartingZ; Zindex != Zindex_stop; Zindex += Increment) {
5007 :
5008 : //'overall cell segment counter
5009 12 : ++SegmentCellCtr;
5010 :
5011 12 : if (SegmentCellCtr == 1) {
5012 : //'we have the very first cell, need to pass in circuiting entering temperature
5013 1 : this->PerformPipeCellSimulation(thisCircuit, cells(PipeX, PipeY, Zindex), FlowRate, EnteringTemp);
5014 : } else {
5015 : //'we don't have the first cell so just normal simulation
5016 11 : if (Zindex == EndingZ) {
5017 : // simulate current cell using upstream as entering conditions
5018 2 : this->PerformPipeCellSimulation(thisCircuit,
5019 : cells(PipeX, PipeY, Zindex),
5020 : FlowRate,
5021 2 : cells(PipeX, PipeY, Zindex - Increment).PipeCellData.Fluid.Temperature);
5022 : // store this outlet condition to be passed to the next segment
5023 2 : CircuitCrossTemp = cells(PipeX, PipeY, Zindex).PipeCellData.Fluid.Temperature;
5024 9 : } else if (Zindex == StartingZ) {
5025 : // we are starting another segment, use the previous cross temperature
5026 1 : this->PerformPipeCellSimulation(thisCircuit, cells(PipeX, PipeY, Zindex), FlowRate, CircuitCrossTemp);
5027 : } else {
5028 : // we are in an interior node, so just get the upstream cell and use the main simulation
5029 8 : this->PerformPipeCellSimulation(thisCircuit,
5030 : cells(PipeX, PipeY, Zindex),
5031 : FlowRate,
5032 8 : cells(PipeX, PipeY, Zindex - Increment).PipeCellData.Fluid.Temperature);
5033 : }
5034 : }
5035 :
5036 : // Bookkeeping: segment fluid temperature updates
5037 12 : if (Zindex == StartingZ) {
5038 2 : if (segmentNum == 1) {
5039 1 : segment->InletTemperature = EnteringTemp;
5040 : } else {
5041 1 : segment->InletTemperature = CircuitCrossTemp;
5042 : }
5043 10 : } else if (Zindex == EndingZ) {
5044 2 : segment->OutletTemperature = cells(PipeX, PipeY, Zindex).PipeCellData.Fluid.Temperature;
5045 2 : segment->FluidHeatLoss =
5046 2 : FlowRate * thisCircuit->CurFluidPropertySet.SpecificHeat * (segment->InletTemperature - segment->OutletTemperature);
5047 : }
5048 :
5049 : // Bookkeeping: circuit fluid temperature updates
5050 12 : if ((segmentNum == 1) && (Zindex == StartingZ)) {
5051 1 : thisCircuit->InletTemperature = EnteringTemp;
5052 11 : } else if ((segmentNum == NumSegments) && (Zindex == EndingZ)) {
5053 1 : thisCircuit->OutletTemperature = cells(PipeX, PipeY, Zindex).PipeCellData.Fluid.Temperature;
5054 1 : thisCircuit->FluidHeatLoss =
5055 1 : FlowRate * thisCircuit->CurFluidPropertySet.SpecificHeat * (thisCircuit->InletTemperature - thisCircuit->OutletTemperature);
5056 : }
5057 : }
5058 : }
5059 1 : }
5060 :
5061 12 : void Domain::PerformPipeCellSimulation(Circuit *thisCircuit, CartesianCell &ThisCell, Real64 const FlowRate, Real64 const EnteringTemp)
5062 : {
5063 :
5064 : // SUBROUTINE INFORMATION:
5065 : // AUTHOR Edwin Lee
5066 : // DATE WRITTEN Summer 2011
5067 : // MODIFIED na
5068 : // RE-ENGINEERED na
5069 :
5070 12 : for (int Iter = 1; Iter <= thisCircuit->MaxIterationsPerTS; ++Iter) {
5071 :
5072 : //'shift all the pipe related temperatures for the next internal pipe iteration
5073 12 : ShiftPipeTemperaturesForNewIteration(ThisCell);
5074 :
5075 : //'simulate the funny interface soil cell between the radial and cartesian systems
5076 12 : this->SimulateRadialToCartesianInterface(ThisCell);
5077 :
5078 : //'simulate the outermost radial slice
5079 12 : SimulateOuterMostRadialSoilSlice(thisCircuit, ThisCell);
5080 :
5081 : //'we only need to simulate these if they actually exist!
5082 12 : if (!ThisCell.PipeCellData.Soil.empty()) {
5083 :
5084 : //'simulate all interior radial slices
5085 12 : SimulateAllInteriorRadialSoilSlices(ThisCell);
5086 :
5087 : //'simulate the innermost radial soil slice
5088 12 : SimulateInnerMostRadialSoilSlice(thisCircuit, ThisCell);
5089 : }
5090 :
5091 12 : if (thisCircuit->HasInsulation) {
5092 0 : SimulateRadialInsulationCell(ThisCell);
5093 : }
5094 :
5095 : //'simulate the pipe cell
5096 12 : SimulateRadialPipeCell(thisCircuit, ThisCell);
5097 :
5098 : //'simulate the water cell
5099 12 : SimulateFluidCell(thisCircuit, ThisCell, FlowRate, EnteringTemp);
5100 :
5101 : //'check convergence
5102 12 : if (IsConverged_PipeCurrentToPrevIteration(thisCircuit, ThisCell)) break; // potential diff source
5103 : }
5104 12 : }
5105 :
5106 12 : void Domain::SimulateRadialToCartesianInterface(CartesianCell &cell)
5107 : {
5108 :
5109 : // SUBROUTINE INFORMATION:
5110 : // AUTHOR Edwin Lee
5111 : // DATE WRITTEN Summer 2011
5112 : // MODIFIED na
5113 : // RE-ENGINEERED na
5114 :
5115 : // SUBROUTINE PARAMETER DEFINITIONS:
5116 14 : static std::vector<Direction> const Directions = {Direction::NegativeX, Direction::NegativeY, Direction::PositiveX, Direction::PositiveY};
5117 :
5118 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5119 12 : Real64 Numerator = 0.0;
5120 12 : Real64 Denominator = 0.0;
5121 :
5122 : //'add effects from this cell history
5123 12 : Numerator += cell.Temperature_PrevTimeStep;
5124 12 : ++Denominator;
5125 :
5126 : //'add effects from outermost radial cell
5127 12 : auto &outerRadialCell = cell.PipeCellData.Soil.back();
5128 12 : Real64 OutermostRadialCellOuterRadius = outerRadialCell.OuterRadius;
5129 12 : Real64 OutermostRadialCellRadialCentroid = outerRadialCell.RadialCentroid;
5130 12 : Real64 OutermostRadialCellTemperature = outerRadialCell.Temperature;
5131 24 : Real64 Resistance = std::log(OutermostRadialCellOuterRadius / OutermostRadialCellRadialCentroid) /
5132 12 : (2.0 * Constant::Pi * cell.depth() * cell.Properties.Conductivity);
5133 12 : Numerator += (cell.Beta / Resistance) * OutermostRadialCellTemperature;
5134 12 : Denominator += (cell.Beta / Resistance);
5135 :
5136 : //'add effects from neighboring Cartesian cells
5137 60 : for (Direction curDirection : Directions) {
5138 48 : Real64 AdiabaticMultiplier = 1.0;
5139 48 : Real64 NeighborTemp = 0.0;
5140 48 : this->EvaluateNeighborCharacteristics(cell, curDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
5141 48 : Numerator += AdiabaticMultiplier * (cell.Beta / Resistance) * NeighborTemp;
5142 48 : Denominator += AdiabaticMultiplier * (cell.Beta / Resistance);
5143 : }
5144 :
5145 : //'calculate the new temperature
5146 12 : cell.Temperature = Numerator / Denominator;
5147 12 : }
5148 :
5149 12 : void SimulateOuterMostRadialSoilSlice(Circuit *thisCircuit, CartesianCell &cell)
5150 : {
5151 :
5152 : // SUBROUTINE INFORMATION:
5153 : // AUTHOR Edwin Lee
5154 : // DATE WRITTEN Summer 2011
5155 : // MODIFIED na
5156 : // RE-ENGINEERED na
5157 :
5158 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5159 : Real64 NextOuterRadialCellOuterRadius;
5160 : Real64 NextOuterRadialCellRadialCentroid;
5161 : Real64 NextOuterRadialCellConductivity;
5162 : Real64 NextOuterRadialCellTemperature;
5163 :
5164 12 : Real64 Numerator = 0.0;
5165 12 : Real64 Denominator = 0.0;
5166 :
5167 : //'convenience variables
5168 12 : int const numSoilCells = static_cast<int>(cell.PipeCellData.Soil.size());
5169 12 : auto &outerMostSoilCell = cell.PipeCellData.Soil[numSoilCells - 1];
5170 12 : Real64 ThisRadialCellOuterRadius = outerMostSoilCell.OuterRadius;
5171 12 : Real64 ThisRadialCellRadialCentroid = outerMostSoilCell.RadialCentroid;
5172 12 : Real64 ThisRadialCellConductivity = outerMostSoilCell.Properties.Conductivity;
5173 12 : Real64 ThisRadialCellInnerRadius = outerMostSoilCell.InnerRadius;
5174 12 : Real64 ThisRadialCellTemperature_PrevTimeStep = outerMostSoilCell.Temperature_PrevTimeStep;
5175 12 : if (numSoilCells == 1) {
5176 0 : if (thisCircuit->HasInsulation) {
5177 0 : NextOuterRadialCellOuterRadius = cell.PipeCellData.Insulation.OuterRadius;
5178 0 : NextOuterRadialCellRadialCentroid = cell.PipeCellData.Insulation.RadialCentroid;
5179 0 : NextOuterRadialCellConductivity = cell.PipeCellData.Insulation.Properties.Conductivity;
5180 0 : NextOuterRadialCellTemperature = cell.PipeCellData.Insulation.Temperature;
5181 : } else {
5182 0 : NextOuterRadialCellOuterRadius = cell.PipeCellData.Pipe.OuterRadius;
5183 0 : NextOuterRadialCellRadialCentroid = cell.PipeCellData.Pipe.RadialCentroid;
5184 0 : NextOuterRadialCellConductivity = cell.PipeCellData.Pipe.Properties.Conductivity;
5185 0 : NextOuterRadialCellTemperature = cell.PipeCellData.Pipe.Temperature;
5186 : }
5187 : } else {
5188 12 : auto const &nextOuterMostSoilCell = cell.PipeCellData.Soil[numSoilCells - 2];
5189 12 : NextOuterRadialCellOuterRadius = nextOuterMostSoilCell.OuterRadius;
5190 12 : NextOuterRadialCellRadialCentroid = nextOuterMostSoilCell.RadialCentroid;
5191 12 : NextOuterRadialCellConductivity = nextOuterMostSoilCell.Properties.Conductivity;
5192 12 : NextOuterRadialCellTemperature = nextOuterMostSoilCell.Temperature;
5193 : }
5194 :
5195 : //'any broadly defined variables
5196 12 : Real64 Beta = outerMostSoilCell.Beta;
5197 :
5198 : //'add effects from this cell history
5199 12 : Numerator += ThisRadialCellTemperature_PrevTimeStep;
5200 12 : ++Denominator;
5201 :
5202 : //'add effects from interface cell
5203 : Real64 Resistance =
5204 12 : std::log(ThisRadialCellOuterRadius / ThisRadialCellRadialCentroid) / (2 * Constant::Pi * cell.depth() * ThisRadialCellConductivity);
5205 12 : Numerator += (Beta / Resistance) * cell.Temperature;
5206 12 : Denominator += (Beta / Resistance);
5207 :
5208 : //'add effects from inner radial cell
5209 12 : Resistance =
5210 12 : (std::log(ThisRadialCellRadialCentroid / ThisRadialCellInnerRadius) / (2 * Constant::Pi * cell.depth() * ThisRadialCellConductivity)) +
5211 24 : (std::log(NextOuterRadialCellOuterRadius / NextOuterRadialCellRadialCentroid) /
5212 12 : (2 * Constant::Pi * cell.depth() * NextOuterRadialCellConductivity));
5213 12 : Numerator += (Beta / Resistance) * NextOuterRadialCellTemperature;
5214 12 : Denominator += (Beta / Resistance);
5215 :
5216 : //'calculate the new temperature
5217 12 : outerMostSoilCell.Temperature = Numerator / Denominator;
5218 12 : }
5219 :
5220 12 : void SimulateAllInteriorRadialSoilSlices(CartesianCell &cell)
5221 : {
5222 :
5223 : // SUBROUTINE INFORMATION:
5224 : // AUTHOR Edwin Lee
5225 : // DATE WRITTEN Summer 2011
5226 : // MODIFIED na
5227 : // RE-ENGINEERED na
5228 :
5229 12 : for (int rCtr = (int)cell.PipeCellData.Soil.size() - 2; rCtr >= 1; --rCtr) {
5230 :
5231 0 : Real64 Numerator = 0.0;
5232 0 : Real64 Denominator = 0.0;
5233 :
5234 : //'convenience variables
5235 0 : auto &thisSoilCell = cell.PipeCellData.Soil[rCtr];
5236 0 : Real64 ThisRadialCellOuterRadius = thisSoilCell.OuterRadius;
5237 0 : Real64 ThisRadialCellRadialCentroid = thisSoilCell.RadialCentroid;
5238 0 : Real64 ThisRadialCellConductivity = thisSoilCell.Properties.Conductivity;
5239 0 : Real64 ThisRadialCellInnerRadius = thisSoilCell.InnerRadius;
5240 0 : Real64 ThisRadialCellTemperature_PrevTimeStep = thisSoilCell.Temperature_PrevTimeStep;
5241 :
5242 0 : auto const &minusSoilCell = cell.PipeCellData.Soil[rCtr - 1];
5243 0 : Real64 InnerRadialCellOuterRadius = minusSoilCell.OuterRadius;
5244 0 : Real64 InnerRadialCellRadialCentroid = minusSoilCell.RadialCentroid;
5245 0 : Real64 InnerRadialCellConductivity = minusSoilCell.Properties.Conductivity;
5246 0 : Real64 InnerRadialCellTemperature = minusSoilCell.Temperature;
5247 :
5248 0 : auto const &plusSoilCell = cell.PipeCellData.Soil[rCtr + 1];
5249 0 : Real64 OuterRadialCellRadialCentroid = plusSoilCell.RadialCentroid;
5250 0 : Real64 OuterRadialCellConductivity = plusSoilCell.Properties.Conductivity;
5251 0 : Real64 OuterRadialCellInnerRadius = plusSoilCell.InnerRadius;
5252 0 : Real64 OuterRadialCellTemperature = plusSoilCell.Temperature;
5253 :
5254 : //'add effects from this cell history
5255 0 : Numerator += ThisRadialCellTemperature_PrevTimeStep;
5256 0 : ++Denominator;
5257 :
5258 : //'add effects from outer cell
5259 : Real64 Resistance =
5260 0 : (std::log(OuterRadialCellRadialCentroid / OuterRadialCellInnerRadius) /
5261 0 : (2 * Constant::Pi * cell.depth() * OuterRadialCellConductivity)) +
5262 0 : (std::log(ThisRadialCellOuterRadius / ThisRadialCellRadialCentroid) / (2 * Constant::Pi * cell.depth() * ThisRadialCellConductivity));
5263 0 : Numerator += (thisSoilCell.Beta / Resistance) * OuterRadialCellTemperature;
5264 0 : Denominator += (thisSoilCell.Beta / Resistance);
5265 :
5266 : //'add effects from inner cell
5267 0 : Resistance = (std::log(ThisRadialCellRadialCentroid / ThisRadialCellInnerRadius) /
5268 0 : (2 * Constant::Pi * cell.depth() * ThisRadialCellConductivity)) +
5269 0 : (std::log(InnerRadialCellOuterRadius / InnerRadialCellRadialCentroid) /
5270 0 : (2 * Constant::Pi * cell.depth() * InnerRadialCellConductivity));
5271 0 : Numerator += (thisSoilCell.Beta / Resistance) * InnerRadialCellTemperature;
5272 0 : Denominator += (thisSoilCell.Beta / Resistance);
5273 :
5274 : //'calculate the new temperature
5275 0 : thisSoilCell.Temperature = Numerator / Denominator;
5276 : }
5277 12 : }
5278 :
5279 12 : void SimulateInnerMostRadialSoilSlice(Circuit *thisCircuit, CartesianCell &cell)
5280 : {
5281 :
5282 : // SUBROUTINE INFORMATION:
5283 : // AUTHOR Edwin Lee
5284 : // DATE WRITTEN Summer 2011
5285 : // MODIFIED na
5286 : // RE-ENGINEERED na
5287 :
5288 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5289 : Real64 Resistance;
5290 : Real64 InnerNeighborRadialCellOuterRadius;
5291 : Real64 InnerNeighborRadialCellRadialCentroid;
5292 : Real64 InnerNeighborRadialCellConductivity;
5293 : Real64 InnerNeighborRadialCellTemperature;
5294 :
5295 12 : Real64 Numerator = 0.0;
5296 12 : Real64 Denominator = 0.0;
5297 :
5298 : //'convenience variables
5299 12 : if (thisCircuit->HasInsulation) {
5300 0 : InnerNeighborRadialCellOuterRadius = cell.PipeCellData.Insulation.OuterRadius;
5301 0 : InnerNeighborRadialCellRadialCentroid = cell.PipeCellData.Insulation.RadialCentroid;
5302 0 : InnerNeighborRadialCellConductivity = cell.PipeCellData.Insulation.Properties.Conductivity;
5303 0 : InnerNeighborRadialCellTemperature = cell.PipeCellData.Insulation.Temperature;
5304 : } else {
5305 12 : InnerNeighborRadialCellOuterRadius = cell.PipeCellData.Pipe.OuterRadius;
5306 12 : InnerNeighborRadialCellRadialCentroid = cell.PipeCellData.Pipe.RadialCentroid;
5307 12 : InnerNeighborRadialCellConductivity = cell.PipeCellData.Pipe.Properties.Conductivity;
5308 12 : InnerNeighborRadialCellTemperature = cell.PipeCellData.Pipe.Temperature;
5309 : }
5310 :
5311 12 : auto &soilZero = cell.PipeCellData.Soil[0];
5312 12 : Real64 ThisRadialCellOuterRadius = soilZero.OuterRadius;
5313 12 : Real64 ThisRadialCellRadialCentroid = soilZero.RadialCentroid;
5314 12 : Real64 ThisRadialCellConductivity = soilZero.Properties.Conductivity;
5315 12 : Real64 ThisRadialCellInnerRadius = soilZero.InnerRadius;
5316 12 : Real64 ThisRadialCellTemperature_PrevTimeStep = soilZero.Temperature_PrevTimeStep;
5317 :
5318 12 : auto const &soilOne = cell.PipeCellData.Soil[1];
5319 12 : Real64 OuterNeighborRadialCellRadialCentroid = soilOne.RadialCentroid;
5320 12 : Real64 OuterNeighborRadialCellConductivity = soilOne.Properties.Conductivity;
5321 12 : Real64 OuterNeighborRadialCellInnerRadius = soilOne.InnerRadius;
5322 12 : Real64 OuterNeighborRadialCellTemperature = soilOne.Temperature;
5323 :
5324 : //'add effects from this cell history
5325 12 : Numerator += ThisRadialCellTemperature_PrevTimeStep;
5326 12 : ++Denominator;
5327 :
5328 : //'add effects from outer radial cell
5329 12 : Resistance =
5330 24 : (std::log(OuterNeighborRadialCellRadialCentroid / OuterNeighborRadialCellInnerRadius) /
5331 12 : (2 * Constant::Pi * cell.depth() * OuterNeighborRadialCellConductivity)) +
5332 12 : (std::log(ThisRadialCellOuterRadius / ThisRadialCellRadialCentroid) / (2 * Constant::Pi * cell.depth() * ThisRadialCellConductivity));
5333 12 : Numerator += (soilZero.Beta / Resistance) * OuterNeighborRadialCellTemperature;
5334 12 : Denominator += (soilZero.Beta / Resistance);
5335 :
5336 : //'add effects from pipe cell
5337 12 : Resistance =
5338 12 : (std::log(ThisRadialCellRadialCentroid / ThisRadialCellInnerRadius) / (2 * Constant::Pi * cell.depth() * ThisRadialCellConductivity)) +
5339 24 : (std::log(InnerNeighborRadialCellOuterRadius / InnerNeighborRadialCellRadialCentroid) /
5340 12 : (2 * Constant::Pi * cell.depth() * InnerNeighborRadialCellConductivity));
5341 12 : Numerator += (soilZero.Beta / Resistance) * InnerNeighborRadialCellTemperature;
5342 12 : Denominator += (soilZero.Beta / Resistance);
5343 :
5344 : //'calculate the new temperature
5345 12 : soilZero.Temperature = Numerator / Denominator;
5346 12 : }
5347 :
5348 0 : void SimulateRadialInsulationCell(CartesianCell &cell)
5349 : {
5350 :
5351 : // SUBROUTINE INFORMATION:
5352 : // AUTHOR Edwin Lee
5353 : // DATE WRITTEN Summer 2011
5354 : // MODIFIED na
5355 : // RE-ENGINEERED na
5356 :
5357 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5358 0 : Real64 Numerator = 0.0;
5359 0 : Real64 Denominator = 0.0;
5360 :
5361 : //'convenience variables
5362 0 : auto const &PipeCell = cell.PipeCellData.Pipe;
5363 0 : auto const &ThisInsulationCell = cell.PipeCellData.Insulation;
5364 0 : auto const &NextInnerRadialCell = cell.PipeCellData.Soil[0];
5365 :
5366 : //'add effects from this cell history
5367 0 : Numerator += ThisInsulationCell.Temperature_PrevTimeStep;
5368 0 : ++Denominator;
5369 :
5370 : //'add effects from outer radial cell
5371 0 : Real64 Resistance = (std::log(NextInnerRadialCell.RadialCentroid / NextInnerRadialCell.InnerRadius) /
5372 0 : (2 * Constant::Pi * cell.depth() * NextInnerRadialCell.Properties.Conductivity)) +
5373 0 : (std::log(ThisInsulationCell.OuterRadius / ThisInsulationCell.RadialCentroid) /
5374 0 : (2 * Constant::Pi * cell.depth() * ThisInsulationCell.Properties.Conductivity));
5375 0 : Numerator += (ThisInsulationCell.Beta / Resistance) * NextInnerRadialCell.Temperature;
5376 0 : Denominator += (ThisInsulationCell.Beta / Resistance);
5377 :
5378 : //'add effects from pipe cell
5379 0 : Resistance =
5380 0 : (std::log(ThisInsulationCell.RadialCentroid / ThisInsulationCell.InnerRadius) /
5381 0 : (2 * Constant::Pi * cell.depth() * ThisInsulationCell.Properties.Conductivity)) +
5382 0 : (std::log(PipeCell.OuterRadius / PipeCell.RadialCentroid) / (2 * Constant::Pi * cell.depth() * PipeCell.Properties.Conductivity));
5383 0 : Numerator += (ThisInsulationCell.Beta / Resistance) * PipeCell.Temperature;
5384 0 : Denominator += (ThisInsulationCell.Beta / Resistance);
5385 :
5386 : //'calculate the new temperature
5387 0 : cell.PipeCellData.Insulation.Temperature = Numerator / Denominator;
5388 0 : }
5389 :
5390 12 : void SimulateRadialPipeCell(Circuit const *thisCircuit, CartesianCell &cell)
5391 : {
5392 :
5393 : // SUBROUTINE INFORMATION:
5394 : // AUTHOR Edwin Lee
5395 : // DATE WRITTEN Summer 2011
5396 : // MODIFIED na
5397 : // RE-ENGINEERED na
5398 :
5399 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5400 : Real64 OuterNeighborRadialCellRadialCentroid;
5401 : Real64 OuterNeighborRadialCellConductivity;
5402 : Real64 OuterNeighborRadialCellInnerRadius;
5403 : Real64 OuterNeighborRadialCellTemperature;
5404 :
5405 12 : Real64 Numerator = 0.0;
5406 12 : Real64 Denominator = 0.0;
5407 :
5408 : //'convenience variables
5409 12 : if (thisCircuit->HasInsulation) {
5410 0 : OuterNeighborRadialCellRadialCentroid = cell.PipeCellData.Insulation.RadialCentroid;
5411 0 : OuterNeighborRadialCellConductivity = cell.PipeCellData.Insulation.Properties.Conductivity;
5412 0 : OuterNeighborRadialCellInnerRadius = cell.PipeCellData.Insulation.InnerRadius;
5413 0 : OuterNeighborRadialCellTemperature = cell.PipeCellData.Insulation.Temperature;
5414 : } else {
5415 12 : auto const &soilZero = cell.PipeCellData.Soil[0];
5416 12 : OuterNeighborRadialCellRadialCentroid = soilZero.RadialCentroid;
5417 12 : OuterNeighborRadialCellConductivity = soilZero.Properties.Conductivity;
5418 12 : OuterNeighborRadialCellInnerRadius = soilZero.InnerRadius;
5419 12 : OuterNeighborRadialCellTemperature = soilZero.Temperature;
5420 : }
5421 :
5422 12 : Real64 const ThisPipeCellOuterRadius = cell.PipeCellData.Pipe.OuterRadius;
5423 12 : Real64 const ThisPipeCellRadialCentroid = cell.PipeCellData.Pipe.RadialCentroid;
5424 12 : Real64 const ThisPipeCellConductivity = cell.PipeCellData.Pipe.Properties.Conductivity;
5425 12 : Real64 const ThisPipeCellInnerRadius = cell.PipeCellData.Pipe.InnerRadius;
5426 12 : Real64 const ThisPipeCellTemperature_PrevTimeStep = cell.PipeCellData.Pipe.Temperature_PrevTimeStep;
5427 :
5428 12 : Real64 const FluidCellTemperature = cell.PipeCellData.Fluid.Temperature;
5429 :
5430 : //'add effects from this cell history
5431 12 : Numerator += ThisPipeCellTemperature_PrevTimeStep;
5432 12 : ++Denominator;
5433 :
5434 : //'add effects from outer radial cell
5435 : Real64 Resistance =
5436 24 : (std::log(OuterNeighborRadialCellRadialCentroid / OuterNeighborRadialCellInnerRadius) /
5437 12 : (2 * Constant::Pi * cell.depth() * OuterNeighborRadialCellConductivity)) +
5438 12 : (std::log(ThisPipeCellOuterRadius / ThisPipeCellRadialCentroid) / (2 * Constant::Pi * cell.depth() * ThisPipeCellConductivity));
5439 12 : Numerator += (cell.PipeCellData.Pipe.Beta / Resistance) * OuterNeighborRadialCellTemperature;
5440 12 : Denominator += (cell.PipeCellData.Pipe.Beta / Resistance);
5441 :
5442 : //'add effects from water cell
5443 : Real64 PipeConductionResistance =
5444 12 : std::log(ThisPipeCellRadialCentroid / ThisPipeCellInnerRadius) / (2 * Constant::Pi * cell.depth() * ThisPipeCellConductivity);
5445 : Real64 ConvectiveResistance =
5446 12 : 1.0 / (thisCircuit->CurCircuitConvectionCoefficient * 2 * Constant::Pi * ThisPipeCellInnerRadius * cell.depth());
5447 12 : Resistance = PipeConductionResistance + ConvectiveResistance;
5448 12 : Numerator += (cell.PipeCellData.Pipe.Beta / Resistance) * FluidCellTemperature;
5449 12 : Denominator += (cell.PipeCellData.Pipe.Beta / Resistance);
5450 :
5451 : //'calculate new temperature
5452 12 : cell.PipeCellData.Pipe.Temperature = Numerator / Denominator;
5453 12 : }
5454 :
5455 12 : void SimulateFluidCell(Circuit const *thisCircuit, CartesianCell &cell, Real64 const FlowRate, Real64 const EnteringFluidTemp)
5456 : {
5457 :
5458 : // SUBROUTINE INFORMATION:
5459 : // AUTHOR Edwin Lee
5460 : // DATE WRITTEN Summer 2011
5461 : // MODIFIED na
5462 : // RE-ENGINEERED na
5463 :
5464 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5465 12 : Real64 Numerator = 0.0;
5466 12 : Real64 Denominator = 0.0;
5467 :
5468 : //'convenience variables
5469 12 : Real64 const FluidCellTemperature_PrevTimeStep = cell.PipeCellData.Fluid.Temperature_PrevTimeStep;
5470 12 : Real64 const FluidCellSpecificHeat = cell.PipeCellData.Fluid.Properties.SpecificHeat;
5471 :
5472 12 : Real64 const PipeCellRadialCentroid = cell.PipeCellData.Pipe.RadialCentroid;
5473 12 : Real64 const PipeCellConductivity = cell.PipeCellData.Pipe.Properties.Conductivity;
5474 12 : Real64 const PipeCellInnerRadius = cell.PipeCellData.Pipe.InnerRadius;
5475 12 : Real64 const PipeCellTemperature = cell.PipeCellData.Pipe.Temperature;
5476 :
5477 : //'add effects from this cell history
5478 12 : Numerator += FluidCellTemperature_PrevTimeStep;
5479 12 : ++Denominator;
5480 :
5481 : //'add effects from outer pipe cell
5482 : Real64 PipeConductionResistance =
5483 12 : std::log(PipeCellRadialCentroid / PipeCellInnerRadius) / (2 * Constant::Pi * cell.depth() * PipeCellConductivity);
5484 12 : Real64 ConvectiveResistance = 1.0 / (thisCircuit->CurCircuitConvectionCoefficient * 2 * Constant::Pi * PipeCellInnerRadius * cell.depth());
5485 12 : Real64 TotalPipeResistance = PipeConductionResistance + ConvectiveResistance;
5486 12 : Numerator += (cell.PipeCellData.Fluid.Beta / TotalPipeResistance) * PipeCellTemperature;
5487 12 : Denominator += (cell.PipeCellData.Fluid.Beta / TotalPipeResistance);
5488 :
5489 : //'add effects from upstream flow
5490 12 : if (FlowRate > 0.0) {
5491 0 : Real64 UpstreamResistance = 1 / (FlowRate * FluidCellSpecificHeat);
5492 0 : Numerator += (cell.PipeCellData.Fluid.Beta / UpstreamResistance) * EnteringFluidTemp;
5493 0 : Denominator += (cell.PipeCellData.Fluid.Beta / UpstreamResistance);
5494 : }
5495 :
5496 : //'calculate new temperature
5497 12 : cell.PipeCellData.Fluid.Temperature = Numerator / Denominator;
5498 12 : }
5499 :
5500 2 : void Domain::DoOneTimeInitializations(EnergyPlusData &state, Circuit *thisCircuit)
5501 : {
5502 :
5503 : // SUBROUTINE INFORMATION:
5504 : // AUTHOR Edwin Lee
5505 : // DATE WRITTEN Summer 2011
5506 : // MODIFIED na
5507 : // RE-ENGINEERED na
5508 :
5509 : //'initialize cell properties
5510 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
5511 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
5512 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
5513 3615 : auto &cell = this->Cells(X, Y, Z);
5514 3615 : switch (cell.cellType) {
5515 12 : case CellType::Pipe:
5516 12 : cell.Properties = this->GroundProperties;
5517 36 : for (auto &soilCell : cell.PipeCellData.Soil) {
5518 24 : soilCell.Properties = this->GroundProperties;
5519 : }
5520 12 : if (thisCircuit) {
5521 12 : cell.PipeCellData.Pipe.Properties = thisCircuit->PipeProperties;
5522 12 : if (thisCircuit->HasInsulation) {
5523 0 : cell.PipeCellData.Insulation.Properties = thisCircuit->InsulationProperties;
5524 : }
5525 : }
5526 12 : break;
5527 2503 : case CellType::GeneralField:
5528 : case CellType::GroundSurface:
5529 : case CellType::FarfieldBoundary:
5530 2503 : cell.Properties = this->GroundProperties;
5531 2503 : break;
5532 243 : case CellType::BasementWall:
5533 : case CellType::BasementFloor:
5534 : case CellType::BasementCorner:
5535 243 : if (this->HasZoneCoupledBasement) { // Basement interface layer
5536 243 : cell.Properties = this->BasementInterfaceProperties;
5537 : } else { // Basement cells are partially ground, give them some props
5538 0 : cell.Properties = this->GroundProperties;
5539 : }
5540 243 : break;
5541 0 : case CellType::Slab:
5542 0 : cell.Properties = this->SlabProperties;
5543 0 : break;
5544 56 : case CellType::HorizInsulation:
5545 56 : cell.Properties = this->HorizInsProperties;
5546 56 : break;
5547 72 : case CellType::VertInsulation:
5548 72 : cell.Properties = this->VertInsProperties;
5549 72 : break;
5550 0 : case CellType::ZoneGroundInterface:
5551 0 : if (this->slabPosition == SlabPosition::InGrade) {
5552 0 : cell.Properties = this->SlabProperties;
5553 : } else {
5554 0 : cell.Properties = this->GroundProperties;
5555 : }
5556 0 : break;
5557 729 : case CellType::BasementCutaway:
5558 729 : break;
5559 0 : default:
5560 0 : assert(false);
5561 : }
5562 : }
5563 : }
5564 : }
5565 :
5566 : //'calculate one-time resistance terms for cartesian cells
5567 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
5568 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
5569 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
5570 3615 : auto &cell = this->Cells(X, Y, Z);
5571 3615 : int NumFieldCells = 0, NumBoundaryCells = 0;
5572 3615 : this->EvaluateCellNeighborDirections(cell, NumFieldCells, NumBoundaryCells);
5573 23719 : for (int DirectionCtr = 0; DirectionCtr <= NumFieldCells; ++DirectionCtr) {
5574 20104 : Direction CurDirection = this->NeighborFieldCells[DirectionCtr];
5575 20104 : Real64 AdiabaticMultiplier = 1.0, NeighborTemp = 0.0, Resistance = 0.0;
5576 20104 : this->EvaluateNeighborCharacteristics(cell, CurDirection, NeighborTemp, Resistance, AdiabaticMultiplier);
5577 20104 : int NX = 0, NY = 0, NZ = 0;
5578 20104 : cell.EvaluateNeighborCoordinates(CurDirection, NX, NY, NZ);
5579 : }
5580 : }
5581 : }
5582 : }
5583 :
5584 : //'initialize freezing calculation variables
5585 2 : this->InitializeSoilMoistureCalcs();
5586 :
5587 : //'we can also initialize the domain based on the farfield temperature here
5588 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
5589 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
5590 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
5591 3615 : auto &cell = this->Cells(X, Y, Z);
5592 :
5593 : // On OneTimeInit, the cur sim time should be zero, so this will be OK
5594 3615 : Real64 ThisCellTemp = this->GetFarfieldTemp(state, cell);
5595 3615 : cell.Temperature = ThisCellTemp;
5596 3615 : cell.Temperature_PrevIteration = ThisCellTemp;
5597 3615 : cell.Temperature_PrevTimeStep = ThisCellTemp;
5598 :
5599 3615 : if (cell.cellType == CellType::Pipe) {
5600 :
5601 36 : for (auto &soilCell : cell.PipeCellData.Soil) {
5602 24 : soilCell.Temperature = ThisCellTemp;
5603 24 : soilCell.Temperature_PrevIteration = ThisCellTemp;
5604 24 : soilCell.Temperature_PrevTimeStep = ThisCellTemp;
5605 : }
5606 12 : cell.PipeCellData.Pipe.Temperature = ThisCellTemp;
5607 12 : cell.PipeCellData.Pipe.Temperature_PrevIteration = ThisCellTemp;
5608 12 : cell.PipeCellData.Pipe.Temperature_PrevTimeStep = ThisCellTemp;
5609 12 : if (thisCircuit) {
5610 12 : if (thisCircuit->HasInsulation) {
5611 0 : cell.PipeCellData.Insulation.Temperature = ThisCellTemp;
5612 0 : cell.PipeCellData.Insulation.Temperature_PrevIteration = ThisCellTemp;
5613 0 : cell.PipeCellData.Insulation.Temperature_PrevTimeStep = ThisCellTemp;
5614 : }
5615 : }
5616 12 : cell.PipeCellData.Fluid.Temperature = ThisCellTemp;
5617 12 : cell.PipeCellData.Fluid.Temperature_PrevIteration = ThisCellTemp;
5618 12 : cell.PipeCellData.Fluid.Temperature_PrevTimeStep = ThisCellTemp;
5619 : }
5620 : }
5621 : }
5622 : }
5623 2 : }
5624 :
5625 2 : void Domain::DoStartOfTimeStepInitializations(EnergyPlusData &state)
5626 : {
5627 : // Update environmental conditions
5628 2 : this->Cur.CurAirTemp = state.dataEnvrn->OutDryBulbTemp;
5629 2 : this->Cur.CurWindSpeed = state.dataEnvrn->WindSpeed;
5630 2 : this->Cur.CurRelativeHumidity = state.dataEnvrn->OutRelHum;
5631 2 : this->Cur.CurIncidentSolar = state.dataEnvrn->BeamSolarRad * max(state.dataEnvrn->SOLCOS(3), 0.0);
5632 :
5633 : //'now update cell properties
5634 2 : auto &cells = this->Cells;
5635 25 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
5636 288 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
5637 3880 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
5638 3615 : auto &cell = cells(X, Y, Z);
5639 3615 : switch (cell.cellType) {
5640 2746 : case CellType::GeneralField:
5641 : case CellType::FarfieldBoundary:
5642 : case CellType::GroundSurface:
5643 : case CellType::BasementCorner:
5644 : case CellType::BasementFloor:
5645 : case CellType::BasementWall:
5646 : // UPDATE CELL PROPERTY SETS
5647 : //'main ground cells, update with soil properties
5648 : Real64 CellRhoCp;
5649 2746 : this->EvaluateSoilRhoCp(cell.Temperature, CellRhoCp);
5650 2746 : cell.Properties.SpecificHeat = CellRhoCp / cell.Properties.Density;
5651 : // UPDATE BETA VALUE
5652 : //'these are basic cartesian calculation cells
5653 2746 : cell.Beta = this->Cur.CurSimTimeStepSize / (cell.Properties.Density * cell.volume() * cell.Properties.SpecificHeat);
5654 2746 : break;
5655 128 : case CellType::HorizInsulation:
5656 : case CellType::VertInsulation:
5657 : case CellType::Slab:
5658 : case CellType::ZoneGroundInterface:
5659 256 : this->Cells(X, Y, Z).Beta =
5660 128 : this->Cur.CurSimTimeStepSize / (cell.Properties.Density * cell.volume() * cell.Properties.SpecificHeat);
5661 128 : break;
5662 12 : case CellType::Pipe:
5663 : // No pipe circuit with this call
5664 12 : break;
5665 729 : case CellType::BasementCutaway:
5666 729 : break;
5667 0 : default:
5668 0 : assert(false);
5669 : }
5670 : }
5671 : }
5672 : }
5673 2 : }
5674 :
5675 1 : void Domain::DoStartOfTimeStepInitializations(EnergyPlusData &state, Circuit *thisCircuit)
5676 : {
5677 :
5678 : // SUBROUTINE INFORMATION:
5679 : // AUTHOR Edwin Lee
5680 : // DATE WRITTEN Summer 2011
5681 : // MODIFIED na
5682 : // RE-ENGINEERED na
5683 :
5684 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5685 : static constexpr std::string_view RoutineName("PipingSystemCircuit::DoStartOfTimeStepInitializations");
5686 : Real64 CellTemp;
5687 : Real64 CellRhoCp;
5688 : Real64 FluidCp;
5689 : Real64 FluidDensity;
5690 : Real64 FluidConductivity;
5691 : Real64 FluidViscosity;
5692 : Real64 FluidPrandtl;
5693 :
5694 : // do the regular, non-circuit related inits
5695 1 : this->DoStartOfTimeStepInitializations(state);
5696 :
5697 : // retrieve fluid properties based on the circuit inlet temperature -- which varies during the simulation
5698 : // but need to verify the value of inlet temperature during warm up, etc.
5699 1 : FluidCp = thisCircuit->plantLoc.loop->glycol->getSpecificHeat(state, thisCircuit->InletTemperature, RoutineName);
5700 1 : FluidDensity = thisCircuit->plantLoc.loop->glycol->getDensity(state, thisCircuit->InletTemperature, RoutineName);
5701 1 : FluidConductivity = thisCircuit->plantLoc.loop->glycol->getConductivity(state, thisCircuit->InletTemperature, RoutineName);
5702 1 : FluidViscosity = thisCircuit->plantLoc.loop->glycol->getViscosity(state, thisCircuit->InletTemperature, RoutineName);
5703 :
5704 : // Doesn't anyone care about poor Ludwig Prandtl?
5705 1 : FluidPrandtl = 3.0;
5706 :
5707 : // then assign these fluid properties to the current fluid property set for easy lookup as needed
5708 1 : thisCircuit->CurFluidPropertySet.Conductivity = FluidConductivity;
5709 1 : thisCircuit->CurFluidPropertySet.Density = FluidDensity;
5710 1 : thisCircuit->CurFluidPropertySet.SpecificHeat = FluidCp;
5711 1 : thisCircuit->CurFluidPropertySet.Viscosity = FluidViscosity;
5712 1 : thisCircuit->CurFluidPropertySet.Prandtl = FluidPrandtl;
5713 :
5714 : //'now update cell properties
5715 1 : auto &cells = this->Cells;
5716 9 : for (int X = 0, X_end = this->x_max_index; X <= X_end; ++X) {
5717 48 : for (int Y = 0, Y_end = this->y_max_index; Y <= Y_end; ++Y) {
5718 280 : for (int Z = 0, Z_end = this->z_max_index; Z <= Z_end; ++Z) {
5719 240 : auto &cell = cells(X, Y, Z);
5720 240 : if (cell.cellType == CellType::Pipe) {
5721 : // UPDATE CELL PROPERTY SETS
5722 : //'first update the outer cell itself
5723 12 : CellTemp = cell.Temperature;
5724 12 : this->EvaluateSoilRhoCp(CellTemp, CellRhoCp);
5725 12 : cell.Properties.SpecificHeat = CellRhoCp / cell.Properties.Density;
5726 : //'then update all the soil radial cells
5727 36 : for (auto &soilCell : cell.PipeCellData.Soil) {
5728 24 : CellTemp = soilCell.Temperature;
5729 24 : this->EvaluateSoilRhoCp(CellTemp, CellRhoCp);
5730 24 : soilCell.Properties.SpecificHeat = CellRhoCp / soilCell.Properties.Density;
5731 : }
5732 :
5733 : // UPDATE BETA VALUES
5734 : //'set the interface cell
5735 12 : cell.Beta = this->Cur.CurSimTimeStepSize /
5736 12 : (cell.Properties.Density * cell.PipeCellData.InterfaceVolume * cell.Properties.SpecificHeat);
5737 :
5738 : //'set the radial soil cells
5739 36 : for (auto &soilCell : cell.PipeCellData.Soil) {
5740 24 : soilCell.Beta = this->Cur.CurSimTimeStepSize / (soilCell.Properties.Density * soilCell.XY_CrossSectArea() * cell.depth() *
5741 24 : soilCell.Properties.SpecificHeat);
5742 : }
5743 :
5744 : //'then insulation if it exists
5745 12 : if (thisCircuit->HasInsulation) {
5746 0 : cell.PipeCellData.Insulation.Beta =
5747 0 : this->Cur.CurSimTimeStepSize /
5748 0 : (cell.PipeCellData.Insulation.Properties.Density * cell.PipeCellData.Insulation.XY_CrossSectArea() * cell.depth() *
5749 0 : cell.PipeCellData.Insulation.Properties.SpecificHeat);
5750 : }
5751 :
5752 : //'set the pipe cell
5753 12 : cell.PipeCellData.Pipe.Beta =
5754 12 : this->Cur.CurSimTimeStepSize / (cell.PipeCellData.Pipe.Properties.Density * cell.PipeCellData.Pipe.XY_CrossSectArea() *
5755 12 : cell.depth() * cell.PipeCellData.Pipe.Properties.SpecificHeat);
5756 :
5757 : // now the fluid cell also
5758 12 : cell.PipeCellData.Fluid.Properties = thisCircuit->CurFluidPropertySet;
5759 12 : cell.PipeCellData.Fluid.Beta =
5760 12 : this->Cur.CurSimTimeStepSize / (cell.PipeCellData.Fluid.Properties.Density * cell.PipeCellData.Fluid.Volume *
5761 12 : cell.PipeCellData.Fluid.Properties.SpecificHeat);
5762 : }
5763 : }
5764 : }
5765 : }
5766 1 : }
5767 :
5768 2 : void Domain::DoEndOfIterationOperations(EnergyPlusData &state, bool &Finished)
5769 : {
5770 :
5771 : // SUBROUTINE INFORMATION:
5772 : // AUTHOR Edwin Lee
5773 : // DATE WRITTEN Summer 2011
5774 : // MODIFIED na
5775 : // RE-ENGINEERED na
5776 :
5777 : // SUBROUTINE PARAMETER DEFINITIONS:
5778 : static constexpr std::string_view RoutineName("DoEndOfIterationOperations");
5779 :
5780 : //'check if we have converged for this iteration
5781 2 : Finished = this->IsConverged_CurrentToPrevIteration();
5782 :
5783 : //'check for out of range temperatures here so they aren't plotted
5784 : //'this routine should be *much* more restrictive than the exceptions, so we should be safe with this location
5785 2 : bool OutOfRange = this->CheckForOutOfRangeTemps();
5786 2 : if (OutOfRange) {
5787 0 : if (this->HasZoneCoupledSlab) {
5788 0 : ShowSevereError(state, format("Site:GroundDomain:Slab{}: Out of range temperatures detected in the ground domain.", RoutineName));
5789 0 : ShowContinueError(state, "This could be due to the size of the loads on the domain.");
5790 0 : ShowContinueError(state, "Verify inputs are correct. If problem persists, notify EnergyPlus support.");
5791 0 : ShowFatalError(state, "Preceding error(s) cause program termination");
5792 0 : } else if (this->HasZoneCoupledBasement) {
5793 0 : ShowSevereError(state, format("Site:GroundDomain:Basement{}: Out of range temperatures detected in the ground domain.", RoutineName));
5794 0 : ShowContinueError(state, "This could be due to the size of the loads on the domain.");
5795 0 : ShowContinueError(state, "Verify inputs are correct. If problem persists, notify EnergyPlus support.");
5796 0 : ShowFatalError(state, "Preceding error(s) cause program termination");
5797 : } else {
5798 0 : ShowSevereError(state, format("PipingSystems:{}: Out of range temperatures detected in piping system simulation.", RoutineName));
5799 0 : ShowContinueError(state, "This could be due to the size of the pipe circuit in relation to the loads being imposed.");
5800 0 : ShowContinueError(state, "Try increasing the size of the pipe circuit and investigate sizing effects.");
5801 0 : ShowFatalError(state, "Preceding error(s) cause program termination");
5802 : }
5803 : }
5804 2 : }
5805 :
5806 2 : void Domain::InitializeSoilMoistureCalcs()
5807 : {
5808 :
5809 : // These vary by domain now, so we must be careful to retrieve them every time
5810 2 : Real64 const Theta_liq = this->Moisture.Theta_liq;
5811 2 : Real64 const Theta_sat = this->Moisture.Theta_sat;
5812 :
5813 : // Assumption
5814 2 : Real64 const Theta_ice = Theta_liq;
5815 :
5816 : //'Cp (freezing) calculations
5817 2 : Real64 constexpr rho_ice = 917.0; //'Kg / m3
5818 2 : Real64 constexpr rho_liq = 1000.0; //'kg / m3
5819 :
5820 : //'from( " An improved model for predicting soil thermal conductivity from water content at room temperature, Fig 4" )
5821 2 : Real64 constexpr CP_liq = 4180.0; //'J / KgK
5822 2 : Real64 constexpr CP_ice = 2066.0; //'J / KgK
5823 2 : Real64 constexpr Lat_fus = 334000.0; //'J / Kg
5824 2 : Real64 const Cp_transient = Lat_fus / 0.4 + (0.5 * CP_ice - (CP_liq + CP_ice) / 2.0 * 0.1) / 0.4;
5825 :
5826 : //'from( " Numerical and experimental investigation of melting and freezing processes in phase change material storage" )
5827 2 : this->Moisture.rhoCp_soil_liq_1 = 1225000.0 / (1.0 - Theta_sat); //'J/m3K
5828 2 : this->Moisture.rhoCP_soil_liq = this->Moisture.rhoCp_soil_liq_1 * (1.0 - Theta_sat) + rho_liq * CP_liq * Theta_liq;
5829 2 : this->Moisture.rhoCP_soil_transient =
5830 2 : this->Moisture.rhoCp_soil_liq_1 * (1.0 - Theta_sat) + ((rho_liq + rho_ice) / 2.0) * Cp_transient * Theta_ice;
5831 2 : this->Moisture.rhoCP_soil_ice = this->Moisture.rhoCp_soil_liq_1 * (1.0 - Theta_sat) + rho_ice * CP_ice * Theta_ice; //'!J / m3K
5832 2 : }
5833 :
5834 2782 : void Domain::EvaluateSoilRhoCp(Real64 const CellTemp, Real64 &rhoCp) const
5835 : {
5836 :
5837 : // SUBROUTINE INFORMATION:
5838 : // AUTHOR Edwin Lee
5839 : // DATE WRITTEN Summer 2011
5840 : // MODIFIED na
5841 : // RE-ENGINEERED na
5842 :
5843 : //'set some temperatures here for generalization -- these could be set in the input file
5844 2782 : Real64 constexpr frzAllIce = -0.5;
5845 2782 : Real64 constexpr frzIceTrans = -0.4;
5846 2782 : Real64 constexpr frzLiqTrans = -0.1;
5847 2782 : Real64 constexpr frzAllLiq = 0.0;
5848 :
5849 : //'calculate this cell's new Cp value based on the cell temperature
5850 2782 : if (CellTemp <= frzAllIce) { // totally frozen
5851 0 : rhoCp = this->Moisture.rhoCP_soil_ice;
5852 2782 : } else if (CellTemp < frzIceTrans) { // in between totally frozen and ice transition
5853 0 : rhoCp = this->Moisture.rhoCP_soil_ice +
5854 0 : (this->Moisture.rhoCP_soil_transient - this->Moisture.rhoCP_soil_ice) / (frzIceTrans - frzAllIce) * (CellTemp - frzAllIce);
5855 2782 : } else if (CellTemp <= frzLiqTrans) { // in between ice transition and liquid transition
5856 0 : rhoCp = this->Moisture.rhoCP_soil_transient;
5857 2782 : } else if (CellTemp < frzAllLiq) { // in between liquid transition and all liquid
5858 0 : rhoCp = this->Moisture.rhoCp_soil_liq_1 +
5859 0 : (this->Moisture.rhoCP_soil_transient - this->Moisture.rhoCP_soil_liq) / (frzAllLiq - frzLiqTrans) * (frzAllLiq - CellTemp);
5860 : } else { // ( CellTemp >= frzAllLiq ) --- greater than or equal to all liquid
5861 2782 : rhoCp = this->Moisture.rhoCp_soil_liq_1;
5862 : }
5863 2782 : }
5864 :
5865 55918 : void CartesianCell::EvaluateNeighborCoordinates(Direction const CurDirection, int &NX, int &NY, int &NZ) const
5866 : {
5867 :
5868 : // SUBROUTINE INFORMATION:
5869 : // AUTHOR Edwin Lee
5870 : // DATE WRITTEN Summer 2011
5871 : // MODIFIED na
5872 : // RE-ENGINEERED na
5873 :
5874 55918 : int const X = this->X_index;
5875 55918 : int const Y = this->Y_index;
5876 55918 : int const Z = this->Z_index;
5877 :
5878 55918 : switch (CurDirection) {
5879 9297 : case Direction::PositiveY:
5880 9297 : NX = X;
5881 9297 : NY = Y + 1;
5882 9297 : NZ = Z;
5883 9297 : break;
5884 9297 : case Direction::NegativeY:
5885 9297 : NX = X;
5886 9297 : NY = Y - 1;
5887 9297 : NZ = Z;
5888 9297 : break;
5889 9351 : case Direction::PositiveX:
5890 9351 : NX = X + 1;
5891 9351 : NY = Y;
5892 9351 : NZ = Z;
5893 9351 : break;
5894 9351 : case Direction::NegativeX:
5895 9351 : NX = X - 1;
5896 9351 : NY = Y;
5897 9351 : NZ = Z;
5898 9351 : break;
5899 9311 : case Direction::PositiveZ:
5900 9311 : NX = X;
5901 9311 : NY = Y;
5902 9311 : NZ = Z + 1;
5903 9311 : break;
5904 9311 : case Direction::NegativeZ:
5905 9311 : NX = X;
5906 9311 : NY = Y;
5907 9311 : NZ = Z - 1;
5908 9311 : break;
5909 0 : default:
5910 0 : assert(false);
5911 : }
5912 55918 : }
5913 :
5914 35814 : void Domain::EvaluateNeighborCharacteristics(
5915 : CartesianCell &ThisCell, Direction const CurDirection, Real64 &NeighborTemp, Real64 &Resistance, Real64 &AdiabaticMultiplier)
5916 : {
5917 :
5918 : // SUBROUTINE INFORMATION:
5919 : // AUTHOR Edwin Lee
5920 : // DATE WRITTEN Summer 2011
5921 : // MODIFIED na
5922 : // RE-ENGINEERED na
5923 :
5924 35814 : int NX = 0, NY = 0, NZ = 0;
5925 35814 : ThisCell.EvaluateNeighborCoordinates(CurDirection, NX, NY, NZ);
5926 :
5927 : //'split effects between the two cells so we can carefully calculate resistance values
5928 : Real64 ThisCellLength;
5929 : Real64 NeighborCellLength;
5930 35814 : Real64 ThisCellConductivity = 10000.0;
5931 35814 : if (ThisCell.Properties.Conductivity > 0.0) ThisCellConductivity = ThisCell.Properties.Conductivity;
5932 35814 : Real64 NeighborConductivity = 10000.0;
5933 35814 : auto const &cell = this->Cells(NX, NY, NZ);
5934 35814 : if (cell.Properties.Conductivity > 0.0) NeighborConductivity = cell.Properties.Conductivity;
5935 :
5936 : //'calculate normal surface area
5937 35814 : Real64 const ThisNormalArea = ThisCell.normalArea(CurDirection);
5938 :
5939 : //'set distance based on cell types
5940 35814 : auto const &TempNeighborInfo = ThisCell.NeighborInfo[CurDirection];
5941 35814 : if (ThisCell.cellType == CellType::Pipe) {
5942 : //'we need to be a bit careful with pipes, as they are full centroid to centroid in the z direction,
5943 : //' but only centroid to wall in the x and y directions
5944 116 : if (CurDirection == Direction::NegativeZ || CurDirection == Direction::PositiveZ) {
5945 20 : ThisCellLength = TempNeighborInfo.ThisCentroidToNeighborWall;
5946 20 : NeighborCellLength = TempNeighborInfo.ThisWallToNeighborCentroid;
5947 : } else {
5948 96 : ThisCellLength = 0.0;
5949 96 : NeighborCellLength = TempNeighborInfo.ThisWallToNeighborCentroid;
5950 : }
5951 35698 : } else if (cell.cellType == CellType::Pipe) {
5952 96 : ThisCellLength = TempNeighborInfo.ThisCentroidToNeighborWall;
5953 96 : NeighborCellLength = 0.0;
5954 : } else {
5955 35602 : ThisCellLength = TempNeighborInfo.ThisCentroidToNeighborWall;
5956 35602 : NeighborCellLength = TempNeighborInfo.ThisWallToNeighborCentroid;
5957 : }
5958 :
5959 : //'calculate resistance based on different conductivities between the two cells
5960 35814 : Resistance = (ThisCellLength / (ThisNormalArea * ThisCellConductivity)) + (NeighborCellLength / (ThisNormalArea * NeighborConductivity));
5961 :
5962 : //'return proper temperature for the given simulation type
5963 35814 : NeighborTemp = cell.Temperature;
5964 :
5965 : //'return the adiabatic multiplier
5966 35814 : AdiabaticMultiplier = TempNeighborInfo.adiabaticMultiplier;
5967 35814 : }
5968 :
5969 6489 : void Domain::EvaluateCellNeighborDirections(CartesianCell const &cell, int &NumFieldCells, int &NumBoundaryCells)
5970 : {
5971 :
5972 : // SUBROUTINE INFORMATION:
5973 : // AUTHOR Edwin Lee
5974 : // DATE WRITTEN Summer 2011
5975 : // MODIFIED na
5976 : // RE-ENGINEERED na
5977 :
5978 6489 : NumFieldCells = -1;
5979 6489 : NumBoundaryCells = -1;
5980 :
5981 6489 : if (cell.X_index < this->x_max_index) {
5982 6060 : ++NumFieldCells;
5983 6060 : this->NeighborFieldCells[NumFieldCells] = Direction::PositiveX;
5984 : } else {
5985 429 : ++NumBoundaryCells;
5986 429 : this->NeighborBoundaryCells[NumBoundaryCells] = Direction::PositiveX;
5987 : }
5988 :
5989 6489 : if (cell.X_index > 0) {
5990 5979 : ++NumFieldCells;
5991 5979 : this->NeighborFieldCells[NumFieldCells] = Direction::NegativeX;
5992 : } else {
5993 510 : ++NumBoundaryCells;
5994 510 : this->NeighborBoundaryCells[NumBoundaryCells] = Direction::NegativeX;
5995 : }
5996 :
5997 6489 : if (cell.Y_index < this->y_max_index) {
5998 6024 : ++NumFieldCells;
5999 6024 : this->NeighborFieldCells[NumFieldCells] = Direction::PositiveY;
6000 : } else {
6001 465 : ++NumBoundaryCells;
6002 465 : this->NeighborBoundaryCells[NumBoundaryCells] = Direction::PositiveY;
6003 : }
6004 :
6005 6489 : if (cell.Y_index > 0) {
6006 5943 : ++NumFieldCells;
6007 5943 : this->NeighborFieldCells[NumFieldCells] = Direction::NegativeY;
6008 : } else {
6009 546 : ++NumBoundaryCells;
6010 546 : this->NeighborBoundaryCells[NumBoundaryCells] = Direction::NegativeY;
6011 : }
6012 :
6013 6489 : if (cell.Z_index < this->z_max_index) {
6014 6042 : ++NumFieldCells;
6015 6042 : this->NeighborFieldCells[NumFieldCells] = Direction::PositiveZ;
6016 : } else {
6017 447 : ++NumBoundaryCells;
6018 447 : this->NeighborBoundaryCells[NumBoundaryCells] = Direction::PositiveZ;
6019 : }
6020 :
6021 6489 : if (cell.Z_index > 0) {
6022 5961 : ++NumFieldCells;
6023 5961 : this->NeighborFieldCells[NumFieldCells] = Direction::NegativeZ;
6024 : } else {
6025 528 : ++NumBoundaryCells;
6026 528 : this->NeighborBoundaryCells[NumBoundaryCells] = Direction::NegativeZ;
6027 : }
6028 6489 : }
6029 :
6030 : } // namespace PlantPipingSystemsManager
6031 :
6032 : } // namespace EnergyPlus
|