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 <string>
52 :
53 : // ObjexxFCL Headers
54 : #include <ObjexxFCL/Array.functions.hh>
55 : #include <ObjexxFCL/Fmath.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <AirflowNetwork/Solver.hpp>
59 : #include <EnergyPlus/Construction.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataEnvironment.hh>
62 : #include <EnergyPlus/DataHeatBalFanSys.hh>
63 : #include <EnergyPlus/DataHeatBalSurface.hh>
64 : #include <EnergyPlus/DataHeatBalance.hh>
65 : #include <EnergyPlus/DataIPShortCuts.hh>
66 : #include <EnergyPlus/DataMoistureBalance.hh>
67 : #include <EnergyPlus/DataSurfaces.hh>
68 : #include <EnergyPlus/EMSManager.hh>
69 : #include <EnergyPlus/General.hh>
70 : #include <EnergyPlus/HeatBalFiniteDiffManager.hh>
71 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
72 : #include <EnergyPlus/Material.hh>
73 : #include <EnergyPlus/MoistureBalanceEMPDManager.hh>
74 : #include <EnergyPlus/OutputProcessor.hh>
75 : #include <EnergyPlus/PhaseChangeModeling/HysteresisModel.hh>
76 : #include <EnergyPlus/PluginManager.hh>
77 : #include <EnergyPlus/UtilityRoutines.hh>
78 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
79 :
80 : namespace EnergyPlus {
81 :
82 : namespace HeatBalFiniteDiffManager {
83 :
84 : // Module containing the heat balance simulation routines
85 :
86 : // MODULE INFORMATION:
87 : // AUTHOR Richard J. Liesen
88 : // DATE WRITTEN October 2003
89 : // RE-ENGINEERED Curtis Pedersen, 2006, Changed to Implicit FD calc for conduction.
90 : // and included enthalpy formulations for phase change materials
91 : // PURPOSE OF THIS MODULE:
92 : // To encapsulate the data and algorithms required to
93 : // manage the finite difference heat balance simulation on the building.
94 :
95 : // REFERENCES:
96 : // The MFD moisture balance method
97 : // C. O. Pedersen, Enthalpy Formulation of conduction heat transfer problems
98 : // involving latent heat, Simulation, Vol 18, No. 2, February 1972
99 : // Fan system Source/Sink heat value, and source/sink location temp from CondFD
100 :
101 599172 : void ManageHeatBalFiniteDiff(EnergyPlusData &state,
102 : int const SurfNum,
103 : Real64 &SurfTempInTmp, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
104 : Real64 &TempSurfOutTmp // Outside Surface Temperature of each Heat Transfer Surface
105 : )
106 : {
107 : // SUBROUTINE INFORMATION:
108 : // AUTHOR Richard Liesen
109 : // DATE WRITTEN May 2000
110 :
111 : // PURPOSE OF THIS SUBROUTINE:
112 : // This subroutine manages the moisture balance method. It is called
113 : // from the HeatBalanceManager at the time step level.
114 : // This driver manages the calls to all of
115 : // the other drivers and simulation algorithms.
116 :
117 : // Get the moisture balance input at the beginning of the simulation only
118 599172 : if (state.dataHeatBalFiniteDiffMgr->GetHBFiniteDiffInputFlag) {
119 : // Obtains conduction FD related parameters from input file
120 0 : GetCondFDInput(state);
121 0 : state.dataHeatBalFiniteDiffMgr->GetHBFiniteDiffInputFlag = false;
122 : }
123 : // Solve the zone heat & moisture balance using a finite difference solution
124 599172 : CalcHeatBalFiniteDiff(state, SurfNum, SurfTempInTmp, TempSurfOutTmp);
125 599172 : }
126 :
127 2 : void GetCondFDInput(EnergyPlusData &state)
128 : {
129 : // SUBROUTINE INFORMATION:
130 : // AUTHOR Curtis Pedersen
131 : // DATE WRITTEN July 2006
132 : // MODIFIED Brent Griffith Mar 2011, user settings
133 :
134 : // PURPOSE OF THIS SUBROUTINE:
135 : // This subroutine is the main driver for initializations for the variable property CondFD part of the
136 : // MFD algorithm
137 :
138 : static constexpr std::string_view routineName = "GetCondFDInput";
139 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
140 : int IOStat; // IO Status when calling get input subroutine
141 2 : Array1D_string MaterialNames(3); // Number of Material Alpha names defined
142 2 : Array1D_string ConstructionName(3); // Name of Construction with CondFDsimplified
143 : int MaterialNumAlpha; // Number of material alpha names being passed
144 : int MaterialNumProp; // Number of material properties being passed
145 2 : Array1D<Real64> MaterialProps; // Temporary array to transfer material properties (allocated based on user input)
146 2 : bool ErrorsFound(false); // If errors detected in input
147 : int propNum;
148 : int pcMat;
149 : int vcMat;
150 : int inegptr;
151 : bool nonInc;
152 :
153 2 : auto &s_ip = state.dataInputProcessing->inputProcessor;
154 2 : auto &s_ipsc = state.dataIPShortCut;
155 2 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
156 2 : auto &s_mat = state.dataMaterial;
157 :
158 : // user settings for numerical parameters
159 2 : s_ipsc->cCurrentModuleObject = "HeatBalanceSettings:ConductionFiniteDifference";
160 :
161 2 : if (s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject) > 0) {
162 : int NumAlphas;
163 : int NumNumbers;
164 0 : s_ip->getObjectItem(state,
165 0 : s_ipsc->cCurrentModuleObject,
166 : 1,
167 0 : s_ipsc->cAlphaArgs,
168 : NumAlphas,
169 0 : s_ipsc->rNumericArgs,
170 : NumNumbers,
171 : IOStat,
172 0 : s_ipsc->lNumericFieldBlanks,
173 0 : s_ipsc->lAlphaFieldBlanks,
174 0 : s_ipsc->cAlphaFieldNames,
175 0 : s_ipsc->cNumericFieldNames);
176 :
177 0 : if (!s_ipsc->lAlphaFieldBlanks(1)) {
178 : {
179 0 : s_hbfd->CondFDSchemeType =
180 0 : static_cast<CondFDScheme>(getEnumValue(CondFDSchemeTypeNamesUC, Util::makeUPPER(s_ipsc->cAlphaArgs(1))));
181 0 : if (s_hbfd->CondFDSchemeType == CondFDScheme::Invalid) {
182 0 : ShowSevereError(state,
183 0 : format("{}: invalid {} entered={}, must match CrankNicholsonSecondOrder or FullyImplicitFirstOrder.",
184 0 : s_ipsc->cCurrentModuleObject,
185 0 : s_ipsc->cAlphaFieldNames(1),
186 0 : s_ipsc->cAlphaArgs(1)));
187 0 : ErrorsFound = true;
188 : }
189 : }
190 : }
191 :
192 0 : if (!s_ipsc->lNumericFieldBlanks(1)) {
193 0 : s_hbfd->SpaceDescritConstant = s_ipsc->rNumericArgs(1);
194 : }
195 0 : if (!s_ipsc->lNumericFieldBlanks(2)) {
196 0 : state.dataHeatBal->CondFDRelaxFactorInput = s_ipsc->rNumericArgs(2);
197 0 : state.dataHeatBal->CondFDRelaxFactor = state.dataHeatBal->CondFDRelaxFactorInput;
198 : }
199 0 : if (!s_ipsc->lNumericFieldBlanks(3)) {
200 0 : state.dataHeatBal->MaxAllowedDelTempCondFD = s_ipsc->rNumericArgs(3);
201 : }
202 :
203 : } // settings object
204 :
205 2 : pcMat = s_ip->getNumObjectsFound(state, "MaterialProperty:PhaseChange");
206 2 : vcMat = s_ip->getNumObjectsFound(state, "MaterialProperty:VariableThermalConductivity");
207 :
208 2 : int numProps = setSizeMaxProperties(state);
209 2 : MaterialProps.allocate(numProps);
210 :
211 2 : s_hbfd->MaterialFD.allocate(s_mat->materials.size());
212 :
213 : // Load the additional CondFD Material properties
214 2 : s_ipsc->cCurrentModuleObject = "MaterialProperty:PhaseChange"; // Phase Change Information First
215 :
216 2 : if (pcMat != 0) { // Get Phase Change info
217 : // CondFDVariableProperties = .TRUE.
218 0 : for (int Loop = 1; Loop <= pcMat; ++Loop) {
219 :
220 : // Call Input Get routine to retrieve material data
221 0 : s_ip->getObjectItem(state,
222 0 : s_ipsc->cCurrentModuleObject,
223 : Loop,
224 : MaterialNames,
225 : MaterialNumAlpha,
226 : MaterialProps,
227 : MaterialNumProp,
228 : IOStat,
229 0 : s_ipsc->lNumericFieldBlanks,
230 0 : s_ipsc->lAlphaFieldBlanks,
231 0 : s_ipsc->cAlphaFieldNames,
232 0 : s_ipsc->cNumericFieldNames);
233 :
234 : // Load the material derived type from the input data.
235 0 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, MaterialNames(1)};
236 0 : int matNum = Material::GetMaterialNum(state, MaterialNames(1));
237 0 : if (matNum == 0) {
238 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(1), MaterialNames(1));
239 0 : ErrorsFound = true;
240 0 : continue;
241 : }
242 0 : auto const *mat = state.dataMaterial->materials(matNum);
243 :
244 0 : if (mat->group != Material::Group::Regular) {
245 0 : ShowSevereError(state,
246 0 : format("{}: Reference Material is not appropriate type for CondFD properties, material={}, must have regular "
247 : "properties (L,Cp,K,D)",
248 0 : s_ipsc->cCurrentModuleObject,
249 0 : mat->Name));
250 0 : ErrorsFound = true;
251 : }
252 :
253 : // Once the material derived type number is found then load the additional CondFD variable material properties
254 : // Some or all may be zero (default). They will be checked when calculating node temperatures
255 0 : auto &matFD = s_hbfd->MaterialFD(matNum);
256 0 : matFD.tk1 = MaterialProps(1);
257 0 : matFD.numTempEnth = (MaterialNumProp - 1) / 2;
258 0 : if (matFD.numTempEnth * 2 != (MaterialNumProp - 1)) {
259 0 : ShowSevereError(state, format("GetCondFDInput: {}=\"{}\", mismatched pairs", s_ipsc->cCurrentModuleObject, MaterialNames(1)));
260 0 : ShowContinueError(state, format("...expected {} pairs, but only entered {} numbers.", matFD.numTempEnth, MaterialNumProp - 1));
261 0 : ErrorsFound = true;
262 : }
263 0 : matFD.TempEnth.dimension(2, matFD.numTempEnth, 0.0);
264 0 : propNum = 2;
265 : // Temperature first
266 0 : for (int pcount = 1, pcount_end = matFD.numTempEnth; pcount <= pcount_end; ++pcount) {
267 0 : matFD.TempEnth(1, pcount) = MaterialProps(propNum);
268 0 : propNum += 2;
269 : }
270 0 : propNum = 3;
271 : // Then Enthalpy
272 0 : for (int pcount = 1, pcount_end = matFD.numTempEnth; pcount <= pcount_end; ++pcount) {
273 0 : matFD.TempEnth(2, pcount) = MaterialProps(propNum);
274 0 : propNum += 2;
275 : }
276 0 : nonInc = false;
277 0 : inegptr = 0;
278 0 : for (int pcount = 1, pcount_end = matFD.numTempEnth - 1; pcount <= pcount_end; ++pcount) {
279 0 : if (matFD.TempEnth(1, pcount) < matFD.TempEnth(1, pcount + 1)) {
280 0 : continue;
281 : }
282 0 : nonInc = true;
283 0 : inegptr = pcount + 1;
284 0 : break;
285 : }
286 0 : if (nonInc) {
287 0 : ShowSevereError(state,
288 0 : format("GetCondFDInput: {}=\"{}\", non increasing Temperatures. Temperatures must be strictly increasing.",
289 0 : s_ipsc->cCurrentModuleObject,
290 : MaterialNames(1)));
291 0 : ShowContinueError(state,
292 0 : format("...occurs first at item=[{}], value=[{:.2R}].", fmt::to_string(inegptr), matFD.TempEnth(1, inegptr)));
293 0 : ErrorsFound = true;
294 : }
295 0 : nonInc = false;
296 0 : inegptr = 0;
297 0 : for (int pcount = 1, pcount_end = matFD.numTempEnth - 1; pcount <= pcount_end; ++pcount) {
298 0 : if (matFD.TempEnth(2, pcount) <= matFD.TempEnth(2, pcount + 1)) {
299 0 : continue;
300 : }
301 0 : nonInc = true;
302 0 : inegptr = pcount + 1;
303 0 : break;
304 : }
305 0 : if (nonInc) {
306 0 : ShowSevereError(state,
307 0 : format("GetCondFDInput: {}=\"{}\", non increasing Enthalpy.", s_ipsc->cCurrentModuleObject, MaterialNames(1)));
308 0 : ShowContinueError(state, format("...occurs first at item=[{}], value=[{:.2R}].", inegptr, matFD.TempEnth(2, inegptr)));
309 0 : ShowContinueError(state, "...These values may be Cp (Specific Heat) rather than Enthalpy. Please correct.");
310 0 : ErrorsFound = true;
311 : }
312 : }
313 : }
314 : // Get CondFD Variable Thermal Conductivity Input
315 :
316 2 : s_ipsc->cCurrentModuleObject = "MaterialProperty:VariableThermalConductivity"; // Variable Thermal Conductivity Info next
317 2 : if (vcMat != 0) { // variable k info
318 : // CondFDVariableProperties = .TRUE.
319 0 : for (int Loop = 1; Loop <= vcMat; ++Loop) {
320 :
321 : // Call Input Get routine to retrieve material data
322 0 : s_ip->getObjectItem(state,
323 0 : s_ipsc->cCurrentModuleObject,
324 : Loop,
325 : MaterialNames,
326 : MaterialNumAlpha,
327 : MaterialProps,
328 : MaterialNumProp,
329 : IOStat,
330 0 : s_ipsc->lNumericFieldBlanks,
331 0 : s_ipsc->lAlphaFieldBlanks,
332 0 : s_ipsc->cAlphaFieldNames,
333 0 : s_ipsc->cNumericFieldNames);
334 :
335 0 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, MaterialNames(1)};
336 : // Load the material derived type from the input data.
337 0 : int matNum = Material::GetMaterialNum(state, MaterialNames(1));
338 0 : if (matNum == 0) {
339 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(1), MaterialNames(1));
340 0 : ErrorsFound = true;
341 0 : continue;
342 : }
343 :
344 0 : auto *mat = s_mat->materials(matNum);
345 :
346 0 : if (mat->group != Material::Group::Regular) {
347 0 : ShowSevereError(state,
348 0 : format("{}: Reference Material is not appropriate type for CondFD properties, material={}, must have regular "
349 : "properties (L,Cp,K,D)",
350 0 : s_ipsc->cCurrentModuleObject,
351 0 : mat->Name));
352 0 : ErrorsFound = true;
353 : }
354 :
355 : // Once the material derived type number is found then load the additional CondFD variable material properties
356 : // Some or all may be zero (default). They will be checked when calculating node temperatures
357 :
358 0 : auto &matFD = s_hbfd->MaterialFD(matNum);
359 0 : matFD.numTempCond = MaterialNumProp / 2;
360 0 : if (matFD.numTempCond * 2 != MaterialNumProp) {
361 0 : ShowSevereError(state, format("GetCondFDInput: {}=\"{}\", mismatched pairs", s_ipsc->cCurrentModuleObject, MaterialNames(1)));
362 0 : ShowContinueError(state, format("...expected {} pairs, but only entered {} numbers.", matFD.numTempCond, MaterialNumProp));
363 0 : ErrorsFound = true;
364 : }
365 0 : matFD.TempCond.dimension(2, matFD.numTempCond, 0.0);
366 0 : propNum = 1;
367 : // Temperature first
368 0 : for (int pcount = 1, pcount_end = matFD.numTempCond; pcount <= pcount_end; ++pcount) {
369 0 : matFD.TempCond(1, pcount) = MaterialProps(propNum);
370 0 : propNum += 2;
371 : }
372 0 : propNum = 2;
373 : // Then Conductivity
374 0 : for (int pcount = 1, pcount_end = matFD.numTempCond; pcount <= pcount_end; ++pcount) {
375 0 : matFD.TempCond(2, pcount) = MaterialProps(propNum);
376 0 : propNum += 2;
377 : }
378 0 : nonInc = false;
379 0 : inegptr = 0;
380 0 : for (int pcount = 1, pcount_end = matFD.numTempCond - 1; pcount <= pcount_end; ++pcount) {
381 0 : if (matFD.TempCond(1, pcount) < matFD.TempCond(1, pcount + 1)) {
382 0 : continue;
383 : }
384 0 : nonInc = true;
385 0 : inegptr = pcount + 1;
386 0 : break;
387 : }
388 0 : if (nonInc) {
389 0 : ShowSevereError(state,
390 0 : format("GetCondFDInput: {}=\"{}\", non increasing Temperatures. Temperatures must be strictly increasing.",
391 0 : s_ipsc->cCurrentModuleObject,
392 : MaterialNames(1)));
393 0 : ShowContinueError(state, format("...occurs first at item=[{}], value=[{:.2R}].", inegptr, matFD.TempCond(1, inegptr)));
394 0 : ErrorsFound = true;
395 : }
396 : }
397 : }
398 :
399 18 : for (auto &matFD : s_hbfd->MaterialFD) {
400 16 : if (matFD.numTempEnth == 0) {
401 16 : matFD.numTempEnth = 3;
402 16 : matFD.TempEnth.dimension(2, 3, -100.0);
403 : }
404 16 : if (matFD.numTempCond == 0) {
405 16 : matFD.numTempCond = 3;
406 16 : matFD.TempCond.dimension(2, 3, -100.0);
407 : }
408 : }
409 :
410 2 : if (ErrorsFound) {
411 0 : ShowFatalError(state, "GetCondFDInput: Errors found getting ConductionFiniteDifference properties. Program terminates.");
412 : }
413 :
414 2 : InitialInitHeatBalFiniteDiff(state);
415 2 : }
416 :
417 3 : int setSizeMaxProperties(EnergyPlusData &state)
418 : {
419 : int numArgs;
420 : int numAlphas;
421 : int numNumerics;
422 3 : int maxTotalProps = 0;
423 :
424 3 : auto &s_ip = state.dataInputProcessing->inputProcessor;
425 :
426 3 : s_ip->getObjectDefMaxArgs(state, "MaterialProperty:PhaseChange", numArgs, numAlphas, numNumerics);
427 3 : maxTotalProps = max(maxTotalProps, numNumerics);
428 :
429 3 : s_ip->getObjectDefMaxArgs(state, "MaterialProperty:VariableThermalConductivity", numArgs, numAlphas, numNumerics);
430 3 : maxTotalProps = max(maxTotalProps, numNumerics);
431 :
432 3 : return maxTotalProps;
433 : }
434 :
435 48972 : void InitHeatBalFiniteDiff(EnergyPlusData &state)
436 : {
437 :
438 : // SUBROUTINE INFORMATION:
439 : // AUTHOR Richard J. Liesen
440 : // DATE WRITTEN Oct 2003
441 : // RE-ENGINEERED C O Pedersen 2006
442 : // B. Griffith May 2011 move begin-environment and every-timestep inits, cleanup formatting
443 :
444 : // PURPOSE OF THIS SUBROUTINE:
445 : // This subroutine sets the initial values for the FD moisture calculation
446 :
447 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
448 : bool ErrorsFound;
449 :
450 48972 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
451 :
452 48972 : if (s_hbfd->GetHBFiniteDiffInputFlag) {
453 : // Obtains conduction FD related parameters from input file
454 2 : GetCondFDInput(state);
455 2 : s_hbfd->GetHBFiniteDiffInputFlag = false;
456 : }
457 :
458 48972 : auto &SurfaceFD = s_hbfd->SurfaceFD;
459 48972 : ErrorsFound = false;
460 :
461 : // now do begin environment inits.
462 48972 : if (state.dataGlobal->BeginEnvrnFlag && s_hbfd->MyEnvrnFlag) {
463 56 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
464 48 : if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) {
465 0 : continue;
466 : }
467 48 : if (state.dataSurface->Surface(SurfNum).Construction <= 0) {
468 0 : continue; // Shading surface, not really a heat transfer surface
469 : }
470 48 : int ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
471 48 : if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) {
472 0 : continue; // Windows simulated in Window module
473 : }
474 48 : auto &thisSurface = SurfaceFD(SurfNum);
475 48 : thisSurface.T = TempInitValue;
476 48 : thisSurface.TOld = TempInitValue;
477 48 : thisSurface.TT = TempInitValue;
478 48 : thisSurface.Rhov = RhovInitValue;
479 48 : thisSurface.RhovOld = RhovInitValue;
480 48 : thisSurface.RhoT = RhovInitValue;
481 48 : thisSurface.TD = TempInitValue;
482 48 : thisSurface.TDT = TempInitValue;
483 48 : thisSurface.TDTLast = TempInitValue;
484 48 : thisSurface.TDOld = TempInitValue;
485 48 : thisSurface.TDreport = TempInitValue;
486 48 : thisSurface.RH = 0.0;
487 48 : thisSurface.RHreport = 0.0;
488 48 : thisSurface.EnthOld = EnthInitValue;
489 48 : thisSurface.EnthNew = EnthInitValue;
490 48 : thisSurface.EnthLast = EnthInitValue;
491 48 : thisSurface.QDreport = 0.0;
492 48 : thisSurface.CpDelXRhoS1 = 0.0;
493 48 : thisSurface.CpDelXRhoS2 = 0.0;
494 48 : thisSurface.TDpriortimestep = 0.0;
495 48 : thisSurface.PhaseChangeState = Material::Phase::Transition;
496 48 : thisSurface.PhaseChangeStateOld = Material::Phase::Transition;
497 48 : thisSurface.PhaseChangeStateOldOld = Material::Phase::Transition;
498 48 : thisSurface.PhaseChangeStateRep = Material::phaseInts[(int)Material::Phase::Transition];
499 48 : thisSurface.PhaseChangeStateOldRep = Material::phaseInts[(int)Material::Phase::Transition];
500 48 : thisSurface.PhaseChangeStateOldOldRep = Material::phaseInts[(int)Material::Phase::Transition];
501 48 : thisSurface.PhaseChangeTemperatureReverse = 50;
502 :
503 48 : state.dataMstBal->TempOutsideAirFD(SurfNum) = 0.0;
504 48 : state.dataMstBal->RhoVaporAirOut(SurfNum) = 0.0;
505 48 : state.dataMstBal->RhoVaporSurfIn(SurfNum) = 0.0;
506 48 : state.dataMstBal->RhoVaporAirIn(SurfNum) = 0.0;
507 48 : state.dataMstBal->HConvExtFD(SurfNum) = 0.0;
508 48 : state.dataMstBal->HMassConvExtFD(SurfNum) = 0.0;
509 48 : state.dataMstBal->HConvInFD(SurfNum) = 0.0;
510 48 : state.dataMstBal->HMassConvInFD(SurfNum) = 0.0;
511 48 : state.dataMstBal->HSkyFD(SurfNum) = 0.0;
512 48 : state.dataMstBal->HGrndFD(SurfNum) = 0.0;
513 48 : state.dataMstBal->HAirFD(SurfNum) = 0.0;
514 : }
515 8 : s_hbfd->WarmupSurfTemp = 0;
516 8 : s_hbfd->MyEnvrnFlag = false;
517 : }
518 48972 : if (!state.dataGlobal->BeginEnvrnFlag) {
519 48964 : s_hbfd->MyEnvrnFlag = true;
520 : }
521 :
522 : // now do every timestep inits
523 :
524 342804 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
525 293832 : if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) {
526 0 : continue;
527 : }
528 293832 : if (state.dataSurface->Surface(SurfNum).Construction <= 0) {
529 0 : continue; // Shading surface, not really a heat transfer surface
530 : }
531 293832 : int ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
532 293832 : if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) {
533 0 : continue; // Windows simulated in Window module
534 : }
535 293832 : auto &thisSurface = SurfaceFD(SurfNum);
536 293832 : thisSurface.T = thisSurface.TOld;
537 293832 : thisSurface.Rhov = thisSurface.RhovOld;
538 293832 : thisSurface.TD = thisSurface.TDOld;
539 293832 : thisSurface.TDT = thisSurface.TDreport; // PT changes from TDold to TDreport
540 293832 : thisSurface.TDTLast = thisSurface.TDOld;
541 293832 : thisSurface.EnthOld = thisSurface.EnthOld;
542 293832 : thisSurface.EnthNew = thisSurface.EnthOld;
543 293832 : thisSurface.EnthLast = thisSurface.EnthOld;
544 293832 : thisSurface.TDpriortimestep = thisSurface.TDreport; // Save TD for heat flux calc
545 : }
546 48972 : }
547 :
548 3 : void InitialInitHeatBalFiniteDiff(EnergyPlusData &state)
549 : {
550 :
551 : // SUBROUTINE INFORMATION:
552 : // AUTHOR Linda Lawrie
553 : // DATE WRITTEN March 2012
554 :
555 : // PURPOSE OF THIS SUBROUTINE:
556 : // This routine performs the original allocate, inits and setup output variables for the
557 : // module.
558 :
559 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
560 : Real64 dxn; // Intermediate calculation of nodal spacing. This is the full dx. There is
561 : // a half dxn thick node at each surface. dxn is the "capacitor" spacing.
562 : int Ipts1; // Intermediate calculation for number of full thickness nodes per layer. There
563 : // are always two half nodes at the layer faces.
564 : int Layer; // Loop counter
565 : int Delt;
566 :
567 : Real64 Alpha;
568 : Real64 mAlpha;
569 : Real64 StabilityTemp;
570 : Real64 StabilityMoist;
571 : Real64 kt;
572 : Real64 RhoS;
573 : Real64 Por;
574 : Real64 Cp;
575 : Real64 Dv;
576 : Real64 DeltaTimestep; // zone timestep in seconds, for local check of properties
577 : Real64 ThicknessThreshold; // min thickness consistent with other thermal properties, for local check
578 :
579 3 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
580 3 : auto &ConstructFD = s_hbfd->ConstructFD;
581 3 : auto &SigmaR = s_hbfd->SigmaR;
582 3 : auto &SigmaC = s_hbfd->SigmaC;
583 3 : auto &SurfaceFD = s_hbfd->SurfaceFD;
584 3 : auto &QHeatInFlux = s_hbfd->QHeatInFlux;
585 3 : auto &QHeatOutFlux = s_hbfd->QHeatOutFlux;
586 :
587 3 : ConstructFD.allocate(state.dataHeatBal->TotConstructs);
588 3 : SigmaR.allocate(state.dataHeatBal->TotConstructs);
589 3 : SigmaC.allocate(state.dataHeatBal->TotConstructs);
590 :
591 3 : SurfaceFD.allocate(state.dataSurface->TotSurfaces);
592 3 : QHeatInFlux.allocate(state.dataSurface->TotSurfaces);
593 3 : QHeatOutFlux.allocate(state.dataSurface->TotSurfaces);
594 :
595 : // And then initialize
596 17 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
597 14 : QHeatInFlux(SurfNum) = 0.0;
598 14 : QHeatOutFlux(SurfNum) = 0.0;
599 14 : state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(SurfNum) = 0.0;
600 14 : state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(SurfNum) = 0.0;
601 : }
602 :
603 : // Setup Output Variables
604 :
605 : // set a Delt that fits the zone time step and keeps it below 200s.
606 :
607 3 : s_hbfd->fracTimeStepZone_Hour = 1.0 / double(state.dataGlobal->TimeStepsInHour);
608 :
609 5 : for (int index = 1; index <= 20; ++index) {
610 5 : Delt = (s_hbfd->fracTimeStepZone_Hour * Constant::rSecsInHour) / index; // TimeStepZone = Zone time step in fractional hours
611 5 : if (Delt <= 200) {
612 3 : break;
613 : }
614 : }
615 :
616 12 : for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
617 10 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
618 10 : auto &thisConstructFD = ConstructFD(ConstrNum);
619 : // Need to skip window constructions, IRT, air wall and construction not in use.
620 : // Need to also skip constructions for surfaces that do not use CondFD.
621 10 : if (thisConstruct.TypeIsWindow) {
622 0 : continue;
623 : }
624 10 : if (thisConstruct.TypeIsIRT) {
625 0 : continue;
626 : }
627 10 : if (thisConstruct.TypeIsAirBoundary) {
628 0 : continue;
629 : }
630 10 : if (!thisConstruct.IsUsed) {
631 0 : continue;
632 : }
633 10 : if (!findAnySurfacesUsingConstructionAndCondFD(state, ConstrNum)) {
634 0 : continue;
635 : }
636 :
637 10 : thisConstructFD.Name.allocate(thisConstruct.TotLayers);
638 10 : thisConstructFD.Thickness.allocate(thisConstruct.TotLayers);
639 10 : thisConstructFD.NodeNumPoint.allocate(thisConstruct.TotLayers);
640 10 : thisConstructFD.DelX.allocate(thisConstruct.TotLayers);
641 10 : thisConstructFD.TempStability.allocate(thisConstruct.TotLayers);
642 10 : thisConstructFD.MoistStability.allocate(thisConstruct.TotLayers);
643 :
644 10 : int TotNodes = 0;
645 10 : SigmaR(ConstrNum) = 0.0;
646 10 : SigmaC(ConstrNum) = 0.0;
647 :
648 36 : for (Layer = 1; Layer <= thisConstruct.TotLayers; ++Layer) { // Begin layer loop ...
649 :
650 : // Loop through all of the layers in the current construct. The purpose
651 : // of this loop is to define the thermal properties and to.
652 : // determine the total number of full size nodes in each layer.
653 : // The number of temperature points is one more than this
654 : // because of the two half nodes at the layer faces.
655 : // The calculation of dxn used here is based on a standard stability
656 : // criteria for explicit finite difference solutions. This criteria
657 : // was chosen not because it is viewed to be correct, but rather for
658 : // lack of any better criteria at this time. The use of a Fourier
659 : // number based criteria such as this is probably physically correct.
660 : // Change to implicit formulation still uses explicit stability, but
661 : // now there are special equations for R-only layers.
662 :
663 27 : int CurrentLayer = thisConstruct.LayerPoint(Layer);
664 27 : auto *mat = state.dataMaterial->materials(CurrentLayer);
665 :
666 27 : thisConstructFD.Name(Layer) = mat->Name;
667 27 : thisConstructFD.Thickness(Layer) = mat->Thickness;
668 :
669 : // Do some quick error checks for this section.
670 :
671 27 : if (mat->ROnly) { // Rlayer
672 :
673 : // These values are only needed temporarily and to calculate flux,
674 : // Layer will be handled
675 : // as a pure R in the temperature calc.
676 : // assign other properties based on resistance
677 :
678 3 : mat->SpecHeat = 0.0001;
679 3 : mat->Density = 1.0;
680 3 : mat->Thickness = 0.1; // arbitrary thickness for R layer
681 3 : mat->Conductivity = mat->Thickness / mat->Resistance;
682 3 : kt = mat->Conductivity;
683 3 : thisConstructFD.Thickness(Layer) = mat->Thickness;
684 :
685 3 : SigmaR(ConstrNum) += mat->Resistance; // add resistance of R layer
686 3 : SigmaC(ConstrNum) += 0.0; // no capacitance for R layer
687 :
688 3 : Alpha = kt / (mat->Density * mat->SpecHeat);
689 :
690 3 : mAlpha = 0.0;
691 :
692 24 : } else if (mat->group == Material::Group::AirGap) { // Group 1 = Air
693 :
694 : // Again, these values are only needed temporarily and to calculate flux,
695 : // Air layer will be handled
696 : // as a pure R in the temperature calc.
697 : // assign
698 : // other properties based on resistance
699 :
700 0 : mat->SpecHeat = 0.0001;
701 0 : mat->Density = 1.0;
702 0 : mat->Thickness = 0.1; // arbitrary thickness for R layer
703 0 : mat->Conductivity = mat->Thickness / mat->Resistance;
704 0 : kt = mat->Conductivity;
705 0 : thisConstructFD.Thickness(Layer) = mat->Thickness;
706 :
707 0 : SigmaR(ConstrNum) += mat->Resistance; // add resistance of R layer
708 0 : SigmaC(ConstrNum) += 0.0; // no capacitance for R layer
709 :
710 0 : Alpha = kt / (mat->Density * mat->SpecHeat);
711 0 : mAlpha = 0.0;
712 24 : } else if (thisConstruct.TypeIsIRT) { // make similar to air? (that didn't seem to work well)
713 0 : ShowSevereError(state,
714 0 : format("InitHeatBalFiniteDiff: Construction =\"{}\" uses Material:InfraredTransparent. Cannot be used currently "
715 : "with finite difference calculations.",
716 0 : thisConstruct.Name));
717 0 : if (thisConstruct.IsUsed) {
718 0 : ShowContinueError(state, "...since this construction is used in a surface, the simulation is not allowed.");
719 : } else {
720 0 : ShowContinueError(state, "...if this construction were used in a surface, the simulation would be terminated.");
721 : }
722 0 : continue;
723 : } else {
724 24 : auto *matReg = mat;
725 24 : assert(matReg != nullptr);
726 : // Regular material Properties
727 24 : kt = matReg->Conductivity;
728 24 : RhoS = matReg->Density;
729 24 : Por = matReg->Porosity;
730 24 : Cp = matReg->SpecHeat;
731 : // Need Resistance for reg layer
732 24 : matReg->Resistance = matReg->Thickness / matReg->Conductivity;
733 24 : Dv = matReg->VaporDiffus;
734 24 : SigmaR(ConstrNum) += matReg->Resistance; // add resistance
735 24 : SigmaC(ConstrNum) += matReg->Density * matReg->SpecHeat * matReg->Thickness;
736 24 : Alpha = kt / (RhoS * Cp);
737 24 : mAlpha = 0.0;
738 :
739 : // check for Material layers that are too thin and highly conductivity (not appropriate for surface models)
740 24 : if (Alpha > DataHeatBalance::HighDiffusivityThreshold) {
741 5 : DeltaTimestep = state.dataGlobal->TimeStepZoneSec;
742 5 : ThicknessThreshold = std::sqrt(Alpha * DeltaTimestep * 3.0);
743 5 : if (mat->Thickness < ThicknessThreshold) {
744 2 : ShowSevereError(
745 : state,
746 2 : format(
747 : "InitialInitHeatBalFiniteDiff: Found Material that is too thin and/or too highly conductive, material name = {}",
748 1 : mat->Name));
749 2 : ShowContinueError(state,
750 2 : format("High conductivity Material layers are not well supported by Conduction Finite Difference, "
751 : "material conductivity = {:.3R} [W/m-K]",
752 1 : mat->Conductivity));
753 1 : ShowContinueError(state, format("Material thermal diffusivity = {:.3R} [m2/s]", Alpha));
754 2 : ShowContinueError(
755 2 : state, format("Material with this thermal diffusivity should have thickness > {:.5R} [m]", ThicknessThreshold));
756 1 : if (mat->Thickness < DataHeatBalance::ThinMaterialLayerThreshold) {
757 0 : ShowContinueError(state,
758 0 : format("Material may be too thin to be modeled well, thickness = {:.5R} [m]", mat->Thickness));
759 0 : ShowContinueError(state,
760 0 : format("Material with this thermal diffusivity should have thickness > {:.5R} [m]",
761 : DataHeatBalance::ThinMaterialLayerThreshold));
762 : }
763 3 : ShowFatalError(state, "Preceding conditions cause termination.");
764 : }
765 : }
766 :
767 : } // R, Air or regular material properties and parameters
768 :
769 : // Proceed with setting node sizes in layers
770 :
771 26 : dxn = std::sqrt(Alpha * Delt * s_hbfd->SpaceDescritConstant); // The Fourier number is set using user constant
772 :
773 : // number of nodes=thickness/spacing. This is number of full size node spaces across layer.
774 26 : Ipts1 = int(mat->Thickness / dxn);
775 : // set high conductivity layers to a single full size node thickness. (two half nodes)
776 26 : if (Ipts1 <= 1) {
777 19 : Ipts1 = 1;
778 : }
779 26 : if (mat->ROnly || mat->group == Material::Group::AirGap) {
780 :
781 3 : Ipts1 = 1; // single full node in R layers- surfaces of adjacent material or inside/outside layer
782 : }
783 :
784 26 : dxn = mat->Thickness / double(Ipts1); // full node thickness
785 :
786 26 : StabilityTemp = Alpha * Delt / pow_2(dxn);
787 26 : StabilityMoist = mAlpha * Delt / pow_2(dxn);
788 26 : thisConstructFD.TempStability(Layer) = StabilityTemp;
789 26 : thisConstructFD.MoistStability(Layer) = StabilityMoist;
790 26 : thisConstructFD.DelX(Layer) = dxn;
791 :
792 26 : TotNodes += Ipts1; // number of full size nodes
793 26 : thisConstructFD.NodeNumPoint(Layer) = Ipts1; // number of full size nodes
794 : } // end of layer loop.
795 :
796 9 : thisConstructFD.TotNodes = TotNodes;
797 9 : thisConstructFD.DeltaTime = Delt;
798 :
799 : } // End of Construction Loop. TotNodes in each construction now set
800 :
801 : // now determine x location, or distance that nodes are from the outside face in meters
802 10 : for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
803 8 : auto &thisConstructFD = ConstructFD(ConstrNum);
804 8 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
805 8 : if (thisConstructFD.TotNodes > 0) {
806 8 : thisConstructFD.NodeXlocation.allocate(thisConstructFD.TotNodes + 1);
807 8 : thisConstructFD.NodeXlocation = 0.0; // init them all
808 8 : Ipts1 = 0; // init counter
809 30 : for (Layer = 1; Layer <= thisConstruct.TotLayers; ++Layer) {
810 22 : int OutwardMatLayerNum = Layer - 1;
811 60 : for (int LayerNode = 1; LayerNode <= thisConstructFD.NodeNumPoint(Layer); ++LayerNode) {
812 38 : ++Ipts1;
813 38 : if (Ipts1 == 1) {
814 8 : thisConstructFD.NodeXlocation(Ipts1) = 0.0; // first node is on outside face
815 :
816 30 : } else if (LayerNode == 1) {
817 14 : if (OutwardMatLayerNum > 0 && OutwardMatLayerNum <= thisConstruct.TotLayers) {
818 : // later nodes are Delx away from previous, but use Delx from previous layer
819 14 : thisConstructFD.NodeXlocation(Ipts1) =
820 14 : thisConstructFD.NodeXlocation(Ipts1 - 1) + thisConstructFD.DelX(OutwardMatLayerNum);
821 : }
822 : } else {
823 : // later nodes are Delx away from previous
824 16 : thisConstructFD.NodeXlocation(Ipts1) = thisConstructFD.NodeXlocation(Ipts1 - 1) + thisConstructFD.DelX(Layer);
825 : }
826 : }
827 : }
828 8 : Layer = thisConstruct.TotLayers;
829 8 : ++Ipts1;
830 8 : thisConstructFD.NodeXlocation(Ipts1) = thisConstructFD.NodeXlocation(Ipts1 - 1) + thisConstructFD.DelX(Layer);
831 : }
832 : }
833 :
834 14 : for (int Surf = 1; Surf <= state.dataSurface->TotSurfaces; ++Surf) {
835 12 : if (!state.dataSurface->Surface(Surf).HeatTransSurf) {
836 0 : continue;
837 : }
838 12 : if (state.dataSurface->Surface(Surf).Class == DataSurfaces::SurfaceClass::Window) {
839 0 : continue;
840 : }
841 12 : if (state.dataSurface->Surface(Surf).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) {
842 0 : continue;
843 : }
844 12 : int ConstrNum = state.dataSurface->Surface(Surf).Construction;
845 12 : int TotNodes = ConstructFD(ConstrNum).TotNodes;
846 12 : int TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
847 :
848 : // Allocate the Surface Arrays
849 12 : SurfaceFD(Surf).T.allocate(TotNodes + 1);
850 12 : SurfaceFD(Surf).TOld.allocate(TotNodes + 1);
851 12 : SurfaceFD(Surf).TT.allocate(TotNodes + 1);
852 12 : SurfaceFD(Surf).Rhov.allocate(TotNodes + 1);
853 12 : SurfaceFD(Surf).RhovOld.allocate(TotNodes + 1);
854 12 : SurfaceFD(Surf).RhoT.allocate(TotNodes + 1);
855 12 : SurfaceFD(Surf).TD.allocate(TotNodes + 1);
856 12 : SurfaceFD(Surf).TDT.allocate(TotNodes + 1);
857 12 : SurfaceFD(Surf).TDTLast.allocate(TotNodes + 1);
858 12 : SurfaceFD(Surf).TDOld.allocate(TotNodes + 1);
859 12 : SurfaceFD(Surf).TDreport.allocate(TotNodes + 1);
860 12 : SurfaceFD(Surf).RH.allocate(TotNodes + 1);
861 12 : SurfaceFD(Surf).RHreport.allocate(TotNodes + 1);
862 12 : SurfaceFD(Surf).EnthOld.allocate(TotNodes + 1);
863 12 : SurfaceFD(Surf).EnthNew.allocate(TotNodes + 1);
864 12 : SurfaceFD(Surf).EnthLast.allocate(TotNodes + 1);
865 12 : SurfaceFD(Surf).QDreport.allocate(TotNodes + 1);
866 12 : SurfaceFD(Surf).CpDelXRhoS1.allocate(TotNodes + 1);
867 12 : SurfaceFD(Surf).CpDelXRhoS2.allocate(TotNodes + 1);
868 12 : SurfaceFD(Surf).TDpriortimestep.allocate(TotNodes + 1);
869 12 : SurfaceFD(Surf).PhaseChangeState.allocate(TotNodes + 1);
870 12 : SurfaceFD(Surf).PhaseChangeStateOld.allocate(TotNodes + 1);
871 12 : SurfaceFD(Surf).PhaseChangeStateOldOld.allocate(TotNodes + 1);
872 12 : SurfaceFD(Surf).PhaseChangeStateRep.allocate(TotNodes + 1);
873 12 : SurfaceFD(Surf).PhaseChangeStateOldRep.allocate(TotNodes + 1);
874 12 : SurfaceFD(Surf).PhaseChangeStateOldOldRep.allocate(TotNodes + 1);
875 12 : SurfaceFD(Surf).PhaseChangeTemperatureReverse.allocate(TotNodes + 1);
876 12 : SurfaceFD(Surf).condMaterialActuators.allocate(TotLayers);
877 12 : SurfaceFD(Surf).specHeatMaterialActuators.allocate(TotLayers);
878 12 : SurfaceFD(Surf).condNodeReport.allocate(TotNodes + 1);
879 12 : SurfaceFD(Surf).specHeatNodeReport.allocate(TotNodes + 1);
880 12 : SurfaceFD(Surf).heatSourceFluxMaterialActuators.allocate(TotLayers - 1);
881 12 : SurfaceFD(Surf).heatSourceInternalFluxLayerReport.allocate(TotLayers - 1);
882 12 : SurfaceFD(Surf).heatSourceInternalFluxEnergyLayerReport.allocate(TotLayers - 1);
883 12 : SurfaceFD(Surf).heatSourceEMSFluxLayerReport.allocate(TotLayers - 1);
884 12 : SurfaceFD(Surf).heatSourceEMSFluxEnergyLayerReport.allocate(TotLayers - 1);
885 :
886 : // Initialize the allocated arrays.
887 12 : SurfaceFD(Surf).T = TempInitValue;
888 12 : SurfaceFD(Surf).TOld = TempInitValue;
889 12 : SurfaceFD(Surf).TT = TempInitValue;
890 12 : SurfaceFD(Surf).Rhov = RhovInitValue;
891 12 : SurfaceFD(Surf).RhovOld = RhovInitValue;
892 12 : SurfaceFD(Surf).RhoT = RhovInitValue;
893 12 : SurfaceFD(Surf).TD = TempInitValue;
894 12 : SurfaceFD(Surf).TDT = TempInitValue;
895 12 : SurfaceFD(Surf).TDTLast = TempInitValue;
896 12 : SurfaceFD(Surf).TDOld = TempInitValue;
897 12 : SurfaceFD(Surf).TDreport = TempInitValue;
898 12 : SurfaceFD(Surf).RH = 0.0;
899 12 : SurfaceFD(Surf).RHreport = 0.0;
900 12 : SurfaceFD(Surf).EnthOld = EnthInitValue;
901 12 : SurfaceFD(Surf).EnthNew = EnthInitValue;
902 12 : SurfaceFD(Surf).EnthLast = EnthInitValue;
903 12 : SurfaceFD(Surf).QDreport = 0.0;
904 12 : SurfaceFD(Surf).CpDelXRhoS1 = 0.0;
905 12 : SurfaceFD(Surf).CpDelXRhoS2 = 0.0;
906 12 : SurfaceFD(Surf).TDpriortimestep = 0.0;
907 12 : SurfaceFD(Surf).PhaseChangeState = Material::Phase::Transition;
908 12 : SurfaceFD(Surf).PhaseChangeStateOld = Material::Phase::Transition;
909 12 : SurfaceFD(Surf).PhaseChangeStateOldOld = Material::Phase::Transition;
910 12 : SurfaceFD(Surf).PhaseChangeStateRep = Material::phaseInts[(int)Material::Phase::Transition];
911 12 : SurfaceFD(Surf).PhaseChangeStateOldRep = Material::phaseInts[(int)Material::Phase::Transition];
912 12 : SurfaceFD(Surf).PhaseChangeStateOldOldRep = Material::phaseInts[(int)Material::Phase::Transition];
913 12 : SurfaceFD(Surf).PhaseChangeTemperatureReverse = 50;
914 12 : SurfaceFD(Surf).condNodeReport = 0.0;
915 12 : SurfaceFD(Surf).specHeatNodeReport = 0.0;
916 12 : SurfaceFD(Surf).heatSourceInternalFluxLayerReport = 0.0;
917 12 : SurfaceFD(Surf).heatSourceInternalFluxEnergyLayerReport = 0.0;
918 12 : SurfaceFD(Surf).heatSourceEMSFluxLayerReport = 0.0;
919 12 : SurfaceFD(Surf).heatSourceEMSFluxEnergyLayerReport = 0.0;
920 :
921 : // Setup EMS data
922 50 : for (int lay = 1; lay <= TotLayers; ++lay) {
923 : // Setup material layer names actuators
924 38 : int matLay = state.dataConstruction->Construct(ConstrNum).LayerPoint(lay);
925 : // Actuator name format: "{SurfName}:{MaterialLayerName}"
926 38 : std::string actName = fmt::format("{}:{}", state.dataSurface->Surface(Surf).Name, state.dataMaterial->materials(matLay)->Name);
927 38 : SurfaceFD(Surf).condMaterialActuators(lay).actuatorName = actName;
928 38 : SurfaceFD(Surf).specHeatMaterialActuators(lay).actuatorName = actName;
929 :
930 : // only setup for heat source actuator for layers 1 to N-1
931 38 : if (lay != TotLayers) {
932 26 : SurfaceFD(Surf).heatSourceFluxMaterialActuators(lay).actuatorName = actName;
933 : }
934 38 : }
935 : }
936 :
937 14 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
938 12 : if (!state.dataSurface->Surface(SurfNum).HeatTransSurf) {
939 0 : continue;
940 : }
941 12 : if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
942 0 : continue;
943 : }
944 12 : if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) {
945 0 : continue;
946 : }
947 :
948 12 : SetupOutputVariable(state,
949 : "CondFD Inner Solver Loop Iteration Count",
950 : Constant::Units::None,
951 12 : SurfaceFD(SurfNum).GSloopCounter,
952 : OutputProcessor::TimeStepType::Zone,
953 : OutputProcessor::StoreType::Sum,
954 12 : state.dataSurface->Surface(SurfNum).Name);
955 :
956 : // Setup EMS Material Actuators for Conductivity and Specific Heat
957 12 : int ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
958 :
959 12 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
960 : // Setup internal heat source output variables
961 : // Only setup for layers 1 to N-1
962 38 : for (int lay = 1; lay < thisConstruct.TotLayers; ++lay) {
963 78 : SetupOutputVariable(state,
964 52 : format("CondFD Internal Heat Source Power After Layer {}", lay),
965 : Constant::Units::W,
966 26 : SurfaceFD(SurfNum).heatSourceInternalFluxLayerReport(lay),
967 : OutputProcessor::TimeStepType::Zone,
968 : OutputProcessor::StoreType::Average,
969 26 : state.dataSurface->Surface(SurfNum).Name);
970 78 : SetupOutputVariable(state,
971 52 : format("CondFD Internal Heat Source Energy After Layer {}", lay),
972 : Constant::Units::J,
973 26 : SurfaceFD(SurfNum).heatSourceInternalFluxEnergyLayerReport(lay),
974 : OutputProcessor::TimeStepType::Zone,
975 : OutputProcessor::StoreType::Sum,
976 26 : state.dataSurface->Surface(SurfNum).Name);
977 : }
978 :
979 12 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
980 50 : for (int lay = 1; lay <= thisConstruct.TotLayers; ++lay) {
981 76 : EnergyPlus::SetupEMSActuator(state,
982 : "CondFD Surface Material Layer",
983 38 : SurfaceFD(SurfNum).condMaterialActuators(lay).actuatorName,
984 : "Thermal Conductivity",
985 : "[W/m-K]",
986 38 : SurfaceFD(SurfNum).condMaterialActuators(lay).isActuated,
987 38 : SurfaceFD(SurfNum).condMaterialActuators(lay).actuatedValue);
988 76 : EnergyPlus::SetupEMSActuator(state,
989 : "CondFD Surface Material Layer",
990 38 : SurfaceFD(SurfNum).specHeatMaterialActuators(lay).actuatorName,
991 : "Specific Heat",
992 : "[J/kg-C]",
993 38 : SurfaceFD(SurfNum).specHeatMaterialActuators(lay).isActuated,
994 38 : SurfaceFD(SurfNum).specHeatMaterialActuators(lay).actuatedValue);
995 : }
996 :
997 : // Setup EMS Actuator and Output Variables for Heat Flux
998 : // Only setup for layers 1 to N-1
999 38 : for (int lay = 1; lay < thisConstruct.TotLayers; ++lay) {
1000 52 : EnergyPlus::SetupEMSActuator(state,
1001 : "CondFD Surface Material Layer",
1002 26 : SurfaceFD(SurfNum).heatSourceFluxMaterialActuators(lay).actuatorName,
1003 : "Heat Flux",
1004 : "[W/m2]",
1005 26 : SurfaceFD(SurfNum).heatSourceFluxMaterialActuators(lay).isActuated,
1006 26 : SurfaceFD(SurfNum).heatSourceFluxMaterialActuators(lay).actuatedValue);
1007 78 : SetupOutputVariable(state,
1008 52 : format("CondFD EMS Heat Source Power After Layer {}", lay),
1009 : Constant::Units::W,
1010 26 : SurfaceFD(SurfNum).heatSourceEMSFluxLayerReport(lay),
1011 : OutputProcessor::TimeStepType::Zone,
1012 : OutputProcessor::StoreType::Average,
1013 26 : state.dataSurface->Surface(SurfNum).Name);
1014 78 : SetupOutputVariable(state,
1015 52 : format("CondFD EMS Heat Source Energy After Layer {}", lay),
1016 : Constant::Units::J,
1017 26 : SurfaceFD(SurfNum).heatSourceEMSFluxEnergyLayerReport(lay),
1018 : OutputProcessor::TimeStepType::Zone,
1019 : OutputProcessor::StoreType::Sum,
1020 26 : state.dataSurface->Surface(SurfNum).Name,
1021 : Constant::eResource::Electricity,
1022 : OutputProcessor::Group::Building,
1023 : OutputProcessor::EndUseCat::Heating);
1024 : }
1025 : }
1026 :
1027 12 : int TotNodes = ConstructFD(state.dataSurface->Surface(SurfNum).Construction).TotNodes; // Full size nodes, start with outside face.
1028 82 : for (int node = 1; node <= TotNodes + 1; ++node) { // include inside face node
1029 210 : SetupOutputVariable(state,
1030 140 : format("CondFD Surface Temperature Node {}", node),
1031 : Constant::Units::C,
1032 70 : SurfaceFD(SurfNum).TDreport(node),
1033 : OutputProcessor::TimeStepType::Zone,
1034 : OutputProcessor::StoreType::Average,
1035 70 : state.dataSurface->Surface(SurfNum).Name);
1036 210 : SetupOutputVariable(state,
1037 140 : format("CondFD Surface Heat Flux Node {}", node),
1038 : Constant::Units::W_m2,
1039 70 : SurfaceFD(SurfNum).QDreport(node),
1040 : OutputProcessor::TimeStepType::Zone,
1041 : OutputProcessor::StoreType::Average,
1042 70 : state.dataSurface->Surface(SurfNum).Name);
1043 70 : SetupOutputVariable(state,
1044 140 : format("CondFD Phase Change State {}", node),
1045 : Constant::Units::None,
1046 70 : (int &)SurfaceFD(SurfNum).PhaseChangeStateRep(node),
1047 : OutputProcessor::TimeStepType::Zone,
1048 : OutputProcessor::StoreType::Average,
1049 70 : state.dataSurface->Surface(SurfNum).Name);
1050 70 : SetupOutputVariable(state,
1051 140 : format("CondFD Phase Change Previous State {}", node),
1052 : Constant::Units::None,
1053 70 : (int &)SurfaceFD(SurfNum).PhaseChangeStateOldRep(node),
1054 : OutputProcessor::TimeStepType::Zone,
1055 : OutputProcessor::StoreType::Average,
1056 70 : state.dataSurface->Surface(SurfNum).Name);
1057 210 : SetupOutputVariable(state,
1058 140 : format("CondFD Phase Change Node Temperature {}", node),
1059 : Constant::Units::C,
1060 70 : SurfaceFD(SurfNum).TDT(node),
1061 : OutputProcessor::TimeStepType::Zone,
1062 : OutputProcessor::StoreType::Average,
1063 70 : state.dataSurface->Surface(SurfNum).Name);
1064 210 : SetupOutputVariable(state,
1065 140 : format("CondFD Phase Change Node Conductivity {}", node),
1066 : Constant::Units::W_mK,
1067 70 : SurfaceFD(SurfNum).condNodeReport(node),
1068 : OutputProcessor::TimeStepType::Zone,
1069 : OutputProcessor::StoreType::Average,
1070 70 : state.dataSurface->Surface(SurfNum).Name);
1071 210 : SetupOutputVariable(state,
1072 140 : format("CondFD Phase Change Node Specific Heat {}", node),
1073 : Constant::Units::J_kgK,
1074 70 : SurfaceFD(SurfNum).specHeatNodeReport(node),
1075 : OutputProcessor::TimeStepType::Zone,
1076 : OutputProcessor::StoreType::Average,
1077 70 : state.dataSurface->Surface(SurfNum).Name);
1078 70 : if (state.dataGlobal->DisplayAdvancedReportVariables) {
1079 0 : SetupOutputVariable(state,
1080 0 : format("CondFD Surface Heat Capacitance Outer Half Node {}", node),
1081 : Constant::Units::W_m2K,
1082 0 : SurfaceFD(SurfNum).CpDelXRhoS1(node),
1083 : OutputProcessor::TimeStepType::Zone,
1084 : OutputProcessor::StoreType::Average,
1085 0 : state.dataSurface->Surface(SurfNum).Name);
1086 0 : SetupOutputVariable(state,
1087 0 : format("CondFD Surface Heat Capacitance Inner Half Node {}", node),
1088 : Constant::Units::W_m2K,
1089 0 : SurfaceFD(SurfNum).CpDelXRhoS2(node),
1090 : OutputProcessor::TimeStepType::Zone,
1091 : OutputProcessor::StoreType::Average,
1092 0 : state.dataSurface->Surface(SurfNum).Name);
1093 : }
1094 : }
1095 :
1096 : } // End of the Surface Loop for Report Variable Setup
1097 :
1098 2 : ReportFiniteDiffInits(state); // Report the results from the Finite Diff Inits
1099 2 : }
1100 :
1101 26 : int numNodesInMaterialLayer(EnergyPlusData &state, std::string const &surfName, std::string const &matName)
1102 : {
1103 26 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
1104 182 : for (auto const &surface : state.dataSurface->Surface) {
1105 156 : if (surface.Name == surfName) {
1106 26 : int constrNum = surface.Construction;
1107 132 : for (int lay = 1; lay <= state.dataConstruction->Construct(constrNum).TotLayers; ++lay) {
1108 106 : int matLay = state.dataConstruction->Construct(constrNum).LayerPoint(lay);
1109 106 : if (state.dataMaterial->materials(matLay)->Name == matName) {
1110 0 : return s_hbfd->ConstructFD(constrNum).NodeNumPoint(lay);
1111 : }
1112 : }
1113 : }
1114 26 : }
1115 :
1116 26 : return 0;
1117 : }
1118 :
1119 5518 : void relax_array(Array1D<Real64> &a, // Array to relax
1120 : Array1D<Real64> const &b, // Array to relax towards
1121 : Real64 const r // Relaxation factor [0-1]
1122 : )
1123 : {
1124 5518 : assert(equal_dimensions(a, b));
1125 5518 : assert((0.0 <= r) && (r <= 1.0));
1126 5518 : Real64 const q(1.0 - r);
1127 39416 : for (int i = a.l(), e = a.u(); i <= e; ++i) {
1128 33898 : a(i) = r * b(i) + q * a(i);
1129 : }
1130 5518 : }
1131 :
1132 694234 : Real64 sum_array_diff(Array1D<Real64> const &a, Array1D<Real64> const &b)
1133 : {
1134 694234 : assert(equal_dimensions(a, b));
1135 694234 : Real64 s(0.0);
1136 4801564 : for (int i = a.l(), e = a.u(); i <= e; ++i) {
1137 4107330 : s += a(i) - b(i); //? Should this be in abs?
1138 : }
1139 694234 : return s;
1140 : }
1141 :
1142 599172 : void CalcHeatBalFiniteDiff(EnergyPlusData &state,
1143 : int const Surf, // Surface number
1144 : Real64 &SurfTempInTmp, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1145 : Real64 &TempSurfOutTmp // Outside Surface Temperature of each Heat Transfer Surface
1146 : )
1147 : {
1148 :
1149 : // SUBROUTINE INFORMATION:
1150 : // AUTHOR Richard J. Liesen
1151 : // DATE WRITTEN Oct 2003
1152 : // MODIFIED Aug 2006 by C O Pedersen to include implicit solution and variable properties with
1153 : // material enthalpy added for Phase Change Materials.
1154 : // Sept 2010 B. Griffith, remove allocate/deallocate, use structure variables
1155 : // March 2011 P. Tabares, add relaxation factor and add surfIteration to
1156 : // update TD and TDT, correct interzone partition
1157 : // May 2011 B. Griffith add logging and errors when inner GS loop does not converge
1158 : // November 2011 P. Tabares fixed problems with adiabatic walls/massless walls and PCM stability problems
1159 :
1160 : // PURPOSE OF THIS SUBROUTINE:
1161 : // this routine controls the calculation of the fluxes and temperatures using
1162 : // finite difference procedures for
1163 : // all building surface constructs.
1164 :
1165 599172 : int const ConstrNum = state.dataSurface->Surface(Surf).Construction;
1166 :
1167 599172 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
1168 599172 : auto &constructFD = s_hbfd->ConstructFD(ConstrNum);
1169 :
1170 599172 : int const TotNodes = constructFD.TotNodes;
1171 599172 : int const TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
1172 :
1173 599172 : SurfTempInTmp = 0.0;
1174 599172 : TempSurfOutTmp = 0.0;
1175 :
1176 599172 : int const Delt = constructFD.DeltaTime; // (seconds)
1177 :
1178 : // Aliases
1179 599172 : auto &surfaceFD = s_hbfd->SurfaceFD(Surf);
1180 :
1181 599172 : Real64 HMovInsul = 0;
1182 599172 : if (state.dataSurface->AnyMovableInsulation) {
1183 0 : HMovInsul = state.dataSurface->extMovInsuls(Surf).H; // Even if this is not a movable insulation surface?
1184 : }
1185 : // Start stepping through the slab with time.
1186 1198344 : for (int J = 1, J_end = nint(state.dataGlobal->TimeStepZoneSec / Delt); J <= J_end; ++J) { // PT testing higher time steps
1187 :
1188 : int GSiter; // iteration counter for implicit repeat calculation
1189 1892578 : for (GSiter = 1; GSiter <= s_hbfd->MaxGSiter; ++GSiter) { // Iterate implicit equations
1190 1892578 : surfaceFD.TDTLast = surfaceFD.TDT; // Save last iteration's TDT (New temperature) values
1191 1892578 : surfaceFD.EnthLast = surfaceFD.EnthNew; // Last iterations new enthalpy value
1192 :
1193 : // Original loop version
1194 1892578 : int i(1); // Node counter
1195 7960170 : for (int Lay = 1; Lay <= TotLayers; ++Lay) { // Begin layer loop ...
1196 :
1197 : // For the exterior surface node with a convective boundary condition
1198 6067592 : if ((i == 1) && (Lay == 1)) {
1199 1892578 : ExteriorBCEqns(state,
1200 : Delt,
1201 : i,
1202 : Lay,
1203 : Surf,
1204 1892578 : surfaceFD.T,
1205 1892578 : surfaceFD.TT,
1206 1892578 : surfaceFD.Rhov,
1207 1892578 : surfaceFD.RhoT,
1208 1892578 : surfaceFD.RH,
1209 1892578 : surfaceFD.TD,
1210 1892578 : surfaceFD.TDT,
1211 1892578 : surfaceFD.EnthOld,
1212 1892578 : surfaceFD.EnthNew,
1213 : TotNodes,
1214 : HMovInsul);
1215 : }
1216 :
1217 : // For the Layer Interior nodes. Arrive here after exterior surface node or interface node
1218 6067592 : if (TotNodes != 1) {
1219 8905444 : for (int ctr = 2, ctr_end = constructFD.NodeNumPoint(Lay); ctr <= ctr_end; ++ctr) {
1220 3137500 : ++i;
1221 3137500 : InteriorNodeEqns(state,
1222 : Delt,
1223 : i,
1224 : Lay,
1225 : Surf,
1226 3137500 : surfaceFD.T,
1227 3137500 : surfaceFD.TT,
1228 3137500 : surfaceFD.Rhov,
1229 3137500 : surfaceFD.RhoT,
1230 3137500 : surfaceFD.RH,
1231 3137500 : surfaceFD.TD,
1232 3137500 : surfaceFD.TDT,
1233 3137500 : surfaceFD.EnthOld,
1234 3137500 : surfaceFD.EnthNew);
1235 : }
1236 : }
1237 :
1238 6067592 : if ((Lay < TotLayers) && (TotNodes != 1)) { // Interface equations for 2 capacitive materials
1239 4175014 : ++i;
1240 4175014 : IntInterfaceNodeEqns(state,
1241 : Delt,
1242 : i,
1243 : Lay,
1244 : Surf,
1245 4175014 : surfaceFD.T,
1246 4175014 : surfaceFD.TT,
1247 4175014 : surfaceFD.Rhov,
1248 4175014 : surfaceFD.RhoT,
1249 4175014 : surfaceFD.RH,
1250 4175014 : surfaceFD.TD,
1251 4175014 : surfaceFD.TDT,
1252 4175014 : surfaceFD.EnthOld,
1253 4175014 : surfaceFD.EnthNew,
1254 : GSiter);
1255 1892578 : } else if (Lay == TotLayers) { // For the Interior surface node with a convective boundary condition
1256 1892578 : ++i;
1257 1892578 : InteriorBCEqns(state,
1258 : Delt,
1259 : i,
1260 : Lay,
1261 : Surf,
1262 1892578 : surfaceFD.T,
1263 1892578 : surfaceFD.TT,
1264 1892578 : surfaceFD.Rhov,
1265 1892578 : surfaceFD.RhoT,
1266 1892578 : surfaceFD.RH,
1267 1892578 : surfaceFD.TD,
1268 1892578 : surfaceFD.TDT,
1269 1892578 : surfaceFD.EnthOld,
1270 1892578 : surfaceFD.EnthNew,
1271 1892578 : surfaceFD.TDreport);
1272 : }
1273 :
1274 : } // layer loop
1275 :
1276 : // Apply Relaxation factor for stability, use current (TDT) and previous (TDTLast) iteration temperature values
1277 : // to obtain the actual temperature that is going to be used for next iteration. This would mostly happen with PCM
1278 : // Tuned Function call to eliminate array temporaries and multiple relaxation passes
1279 1892578 : if (GSiter > 15) {
1280 0 : relax_array(surfaceFD.TDT, surfaceFD.TDTLast, 0.9875);
1281 1892578 : } else if (GSiter > 10) {
1282 14 : relax_array(surfaceFD.TDT, surfaceFD.TDTLast, 0.875);
1283 1892564 : } else if (GSiter > 5) {
1284 2516 : relax_array(surfaceFD.TDT, surfaceFD.TDTLast, 0.5);
1285 : }
1286 :
1287 : // the following could blow up when all the node temps sum to less than 1.0. seems poorly formulated for temperature in C.
1288 : // PT delete one zero and decrease number of minimum iterations, from 3 (which actually requires 4 iterations) to 2.
1289 :
1290 1892578 : if ((GSiter > 2) && (std::abs(sum_array_diff(surfaceFD.TDT, surfaceFD.TDTLast) / sum(surfaceFD.TDT)) < 0.00001)) {
1291 599172 : break;
1292 : }
1293 :
1294 : } // End of Gauss Seidell iteration loop
1295 :
1296 599172 : surfaceFD.GSloopCounter = GSiter; // outputs GSloop iterations, useful for pinpointing stability issues with condFD
1297 599172 : if (state.dataHeatBal->CondFDRelaxFactor != 1.0) {
1298 : // Apply Relaxation factor for stability, use current (TDT) and previous (TDreport) temperature values
1299 : // to obtain the actual temperature that is going to be exported/use
1300 2988 : relax_array(surfaceFD.TDT, surfaceFD.TDreport, 1.0 - state.dataHeatBal->CondFDRelaxFactor);
1301 2988 : surfaceFD.EnthOld = surfaceFD.EnthNew;
1302 : }
1303 :
1304 4094342 : for (int I = 1; I <= (TotNodes + 1); I++) {
1305 : // When the phase change process reverses its direction while melting or freezing (without completing its phase
1306 : // to either liquid or solid), the temperature at which it changes its direction is saved
1307 : // in the variable PhaseChangeTemperatureReverse, and this variable will hold the value of the temperature until
1308 : // the next reverse in the process takes place.
1309 3495170 : if (((surfaceFD.PhaseChangeStateOld(I) == Material::Phase::Freezing &&
1310 0 : surfaceFD.PhaseChangeState(I) == Material::Phase::Transition) ||
1311 3495170 : (surfaceFD.PhaseChangeStateOld(I) == Material::Phase::Transition &&
1312 10485510 : surfaceFD.PhaseChangeState(I) == Material::Phase::Freezing)) ||
1313 3495170 : ((surfaceFD.PhaseChangeStateOld(I) == Material::Phase::Melting && surfaceFD.PhaseChangeState(I) == Material::Phase::Transition) ||
1314 3495170 : (surfaceFD.PhaseChangeStateOld(I) == Material::Phase::Transition &&
1315 3495170 : surfaceFD.PhaseChangeState(I) == Material::Phase::Melting))) {
1316 0 : surfaceFD.PhaseChangeTemperatureReverse(I) = surfaceFD.TDT(I);
1317 : }
1318 : }
1319 :
1320 599172 : surfaceFD.PhaseChangeStateOldOld = surfaceFD.PhaseChangeStateOld;
1321 599172 : surfaceFD.PhaseChangeStateOld = surfaceFD.PhaseChangeState;
1322 :
1323 599172 : surfaceFD.PhaseChangeStateOldOldRep = surfaceFD.PhaseChangeStateOldRep;
1324 599172 : surfaceFD.PhaseChangeStateOldRep = surfaceFD.PhaseChangeStateRep;
1325 : } // Time Loop //PT solving time steps
1326 :
1327 599172 : TempSurfOutTmp = surfaceFD.TDT(1);
1328 599172 : SurfTempInTmp = surfaceFD.TDT(TotNodes + 1);
1329 599172 : state.dataMstBal->RhoVaporSurfIn(Surf) = 0.0;
1330 :
1331 : // For ground surfaces or when raining, outside face inner half-node heat capacity was unknown and set to -1 in ExteriorBCEqns
1332 : // Now check for the flag and set equal to the second node's outer half-node heat capacity if needed
1333 599172 : if (surfaceFD.CpDelXRhoS2(1) == -1.0) {
1334 0 : surfaceFD.CpDelXRhoS2(1) = surfaceFD.CpDelXRhoS1(2); // Set to node 2's outer half node heat capacity
1335 : }
1336 599172 : CalcNodeHeatFlux(state, Surf, TotNodes);
1337 :
1338 : // Determine largest change in node temps
1339 599172 : Real64 MaxDelTemp = 0.0;
1340 4094342 : for (int NodeNum = 1; NodeNum <= TotNodes + 1; ++NodeNum) { // need to consider all nodes
1341 3495170 : MaxDelTemp = max(std::abs(surfaceFD.TDT(NodeNum) - surfaceFD.TDreport(NodeNum)), MaxDelTemp);
1342 : }
1343 599172 : surfaceFD.MaxNodeDelTemp = MaxDelTemp;
1344 599172 : surfaceFD.TDreport = surfaceFD.TDT;
1345 599172 : surfaceFD.EnthOld = surfaceFD.EnthNew;
1346 599172 : }
1347 :
1348 2 : void ReportFiniteDiffInits(EnergyPlusData &state)
1349 : {
1350 :
1351 : // SUBROUTINE INFORMATION:
1352 : // AUTHOR Richard Liesen
1353 : // DATE WRITTEN November 2003
1354 : // MODIFIED B. Griffith, May 2011 add reporting of node x locations
1355 :
1356 : // PURPOSE OF THIS SUBROUTINE:
1357 : // This routine gives a detailed report to the user about
1358 : // the initializations for the Finite Difference calculations
1359 : // of each construction.
1360 :
1361 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1362 : bool DoReport;
1363 :
1364 2 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
1365 :
1366 : // Formats
1367 : static constexpr std::string_view Format_702(" ConductionFiniteDifference Node,{},{:.8R},{},{},{}\n");
1368 :
1369 2 : print(state.files.eio,
1370 : "! <ConductionFiniteDifference HeatBalanceSettings>,Scheme Type,Space Discretization Constant,Relaxation Factor,Inside Face Surface "
1371 : "Temperature Convergence Criteria\n");
1372 2 : print(state.files.eio,
1373 : " ConductionFiniteDifference HeatBalanceSettings,{},{:.2R},{:.2R},{:.4R}\n",
1374 2 : CondFDSchemeTypeNamesCC[static_cast<int>(s_hbfd->CondFDSchemeType)],
1375 2 : s_hbfd->SpaceDescritConstant,
1376 2 : state.dataHeatBal->CondFDRelaxFactorInput,
1377 2 : state.dataHeatBal->MaxAllowedDelTempCondFD);
1378 :
1379 6 : General::ScanForReports(state, "Constructions", DoReport, "Constructions");
1380 :
1381 2 : if (DoReport) {
1382 :
1383 : // Write Descriptions
1384 2 : print(state.files.eio, "{}\n", "! <Construction CondFD>,Construction Name,Index,#Layers,#Nodes,Time Step {hours}");
1385 2 : print(state.files.eio,
1386 : "{}\n",
1387 : "! <Material CondFD Summary>,Material Name,Thickness {m},#Layer Elements,Layer Delta X,Layer Alpha*Delt/Delx**2,Layer Moisture "
1388 : "Stability");
1389 :
1390 : // HT Algo issue
1391 2 : if (state.dataHeatBal->AnyCondFD) {
1392 2 : print(state.files.eio,
1393 : "{}\n",
1394 : "! <ConductionFiniteDifference Node>,Node Identifier, Node Distance From Outside Face {m}, Construction Name, Outward Material "
1395 : "Name (or Face), Inward Material Name (or Face)");
1396 : }
1397 :
1398 10 : for (int ThisNum = 1; ThisNum <= state.dataHeatBal->TotConstructs; ++ThisNum) {
1399 8 : auto &construct = state.dataConstruction->Construct(ThisNum);
1400 :
1401 8 : if (construct.TypeIsWindow) {
1402 0 : continue;
1403 : }
1404 8 : if (construct.TypeIsIRT) {
1405 0 : continue;
1406 : }
1407 8 : if (construct.TypeIsAirBoundary) {
1408 0 : continue;
1409 : }
1410 8 : if (!construct.IsUsed) {
1411 0 : continue;
1412 : }
1413 8 : if (!findAnySurfacesUsingConstructionAndCondFD(state, ThisNum)) {
1414 0 : continue;
1415 : }
1416 :
1417 8 : auto &constructFD = s_hbfd->ConstructFD(ThisNum);
1418 : static constexpr std::string_view Format_700(" Construction CondFD,{},{},{},{},{:.6R}\n");
1419 8 : print(state.files.eio,
1420 : Format_700,
1421 8 : construct.Name,
1422 : ThisNum,
1423 8 : construct.TotLayers,
1424 0 : int(constructFD.TotNodes + 1),
1425 8 : constructFD.DeltaTime / Constant::rSecsInHour);
1426 :
1427 30 : for (int Layer = 1; Layer <= construct.TotLayers; ++Layer) {
1428 : static constexpr std::string_view Format_701(" Material CondFD Summary,{},{:.4R},{},{:.8R},{:.8R},{:.8R}\n");
1429 22 : print(state.files.eio,
1430 : Format_701,
1431 : constructFD.Name(Layer),
1432 : constructFD.Thickness(Layer),
1433 : constructFD.NodeNumPoint(Layer),
1434 : constructFD.DelX(Layer),
1435 : constructFD.TempStability(Layer),
1436 : constructFD.MoistStability(Layer));
1437 : }
1438 :
1439 : // now list each CondFD Node with its X distance from outside face in m along with other identifiers
1440 8 : int Inodes = 0;
1441 :
1442 30 : for (int Layer = 1; Layer <= construct.TotLayers; ++Layer) {
1443 22 : int OutwardMatLayerNum = Layer - 1;
1444 60 : for (int LayerNode = 1; LayerNode <= constructFD.NodeNumPoint(Layer); ++LayerNode) {
1445 38 : ++Inodes;
1446 38 : if (Inodes == 1) {
1447 16 : print(state.files.eio,
1448 : Format_702,
1449 16 : format("Node #{}", Inodes),
1450 : constructFD.NodeXlocation(Inodes),
1451 8 : construct.Name,
1452 : "Surface Outside Face",
1453 : constructFD.Name(Layer));
1454 :
1455 30 : } else if (LayerNode == 1) {
1456 :
1457 14 : if (OutwardMatLayerNum > 0 && OutwardMatLayerNum <= construct.TotLayers) {
1458 28 : print(state.files.eio,
1459 : Format_702,
1460 28 : format("Node #{}", Inodes),
1461 : constructFD.NodeXlocation(Inodes),
1462 14 : construct.Name,
1463 : constructFD.Name(OutwardMatLayerNum),
1464 : constructFD.Name(Layer));
1465 : }
1466 16 : } else if (LayerNode > 1) {
1467 16 : OutwardMatLayerNum = Layer;
1468 32 : print(state.files.eio,
1469 : Format_702,
1470 32 : format("Node #{}", Inodes),
1471 : constructFD.NodeXlocation(Inodes),
1472 16 : construct.Name,
1473 : constructFD.Name(OutwardMatLayerNum),
1474 : constructFD.Name(Layer));
1475 : }
1476 : }
1477 : }
1478 :
1479 8 : int Layer = construct.TotLayers;
1480 8 : ++Inodes;
1481 16 : print(state.files.eio,
1482 : Format_702,
1483 16 : format("Node #{}", Inodes),
1484 : constructFD.NodeXlocation(Inodes),
1485 8 : construct.Name,
1486 : constructFD.Name(Layer),
1487 : "Surface Inside Face");
1488 : }
1489 : }
1490 2 : }
1491 :
1492 0 : Real64 terpld(Array2<Real64> const &a, Real64 const x1, int const nind, int const ndep)
1493 : {
1494 : // author:c. o. pedersen
1495 : // purpose:
1496 : // this function performs a linear interpolation
1497 : // on a two dimensional array containing both
1498 : // dependent and independent variables.
1499 :
1500 : // inputs:
1501 : // a = two dimensional array
1502 : // nind=row containing independent variable
1503 : // ndep=row containing the dependent variable
1504 : // x1 = specific independent variable value for which
1505 : // interpolated output is wanted
1506 : // outputs:
1507 : // the value of dependent variable corresponding
1508 : // to x1
1509 : // routine returns first or last dependent variable
1510 : // for out of range x1.
1511 :
1512 0 : int const first(a.l2());
1513 :
1514 0 : assert(a.size() > 0u);
1515 0 : Array2<Real64>::size_type l(1);
1516 0 : Real64 r(a[0]);
1517 0 : int last(first);
1518 0 : for (int i1 = first + 1, e1 = a.u2(); i1 <= e1; ++i1, ++l) {
1519 0 : if (a[l] > r) {
1520 0 : r = a[l];
1521 0 : last = i1;
1522 : }
1523 : }
1524 :
1525 0 : Array2<Real64>::size_type lind(a.index(nind, 0));
1526 0 : Array2<Real64>::size_type ldep(a.index(ndep, 0));
1527 0 : if ((a.size2() == 1u) || (x1 <= a[lind + first])) { // [ lind + first ] == ( nind, first )
1528 0 : return a[ldep + first]; // [ ldep + first ] == ( ndep, first )
1529 0 : } else if (x1 >= a[lind + last]) { // [ lind + last ] == ( nind, last )
1530 0 : return a[ldep + last]; // [ ldep + last ] == ( ndep, last )
1531 : } else {
1532 : int i;
1533 0 : int i1(first);
1534 0 : int i2(last);
1535 0 : while ((i2 - i1) > 1) {
1536 0 : i = i1 + ((i2 - i1) >> 1); // Tuned bit shift replaces / 2
1537 0 : if (x1 < a[lind + i]) { // [ lind + i ] == ( nind, i )
1538 0 : i2 = i;
1539 : } else {
1540 0 : i1 = i;
1541 : }
1542 : }
1543 0 : i = i2;
1544 0 : lind += i;
1545 0 : ldep += i;
1546 0 : Real64 const fract((x1 - a[lind - 1]) / (a[lind] - a[lind - 1])); // [ lind ] == ( nind, i ), [ lind - 1 ] == ( nind, i - 1 )
1547 0 : return a[ldep - 1] + fract * (a[ldep] - a[ldep - 1]); // [ ldep ] == ( ndep, i ), [ ldep - 1 ] == ( ndep, i - 1 )
1548 : }
1549 : }
1550 :
1551 1892578 : void ExteriorBCEqns(EnergyPlusData &state,
1552 : int const Delt, // Time Increment
1553 : int const i, // Node Index
1554 : int const Lay, // Layer Number for Construction
1555 : int const Surf, // Surface number
1556 : [[maybe_unused]] Array1D<Real64> const &T, // Old node Temperature in MFD finite difference solution
1557 : Array1D<Real64> &TT, // New node Temperature in MFD finite difference solution.
1558 : [[maybe_unused]] Array1D<Real64> const &Rhov, // MFD Nodal Vapor Density[kg/m3] and is the old or last time step result.
1559 : Array1D<Real64> &RhoT, // MFD vapor density for the new time step.
1560 : [[maybe_unused]] Array1D<Real64> &RH, // Nodal relative humidity
1561 : Array1D<Real64> const &TD, // The old dry Temperature at each node for the CondFD algorithm..
1562 : Array1D<Real64> &TDT, // The current or new Temperature at each node location for the CondFD solution..
1563 : Array1D<Real64> &EnthOld, // Old Nodal enthalpy
1564 : Array1D<Real64> &EnthNew, // New Nodal enthalpy
1565 : int const TotNodes, // Total nodes in layer
1566 : Real64 const HMovInsul // Conductance of movable(transparent) insulation.
1567 : )
1568 : {
1569 :
1570 : // SUBROUTINE INFORMATION:
1571 : // AUTHOR Richard Liesen
1572 : // DATE WRITTEN November, 2003
1573 : // MODIFIED B. Griffith 2010, fix adiabatic and other side surfaces
1574 : // May 2011, B. Griffith, P. Tabares
1575 : // November 2011 P. Tabares fixed problems with adiabatic walls/massless walls
1576 : // November 2011 P. Tabares fixed problems PCM stability problems
1577 : // RE-ENGINEERED Curtis Pedersen 2006
1578 :
1579 1892578 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
1580 1892578 : auto const &surface(state.dataSurface->Surface(Surf));
1581 1892578 : int const surface_ExtBoundCond(surface.ExtBoundCond);
1582 :
1583 : Real64 Tsky;
1584 : Real64 QRadSWOutFD; // Short wave radiation absorbed on outside of opaque surface
1585 1892578 : Real64 QRadSWOutMvInsulFD(0.0); // SW radiation at outside of Movable Insulation
1586 1892578 : if (surface_ExtBoundCond == DataSurfaces::OtherSideCondModeledExt) {
1587 : // CR8046 switch modeled rad temp for sky temp.
1588 0 : Tsky = state.dataSurface->OSCM(surface.OSCMPtr).TRad;
1589 0 : QRadSWOutFD = 0.0; // eliminate incident shortwave on underlying surface
1590 : } else { // Set the external conditions to local variables
1591 1892578 : QRadSWOutFD = state.dataHeatBalSurf->SurfOpaqQRadSWOutAbs(Surf);
1592 1892578 : QRadSWOutMvInsulFD = state.dataHeatBalSurf->SurfQRadSWOutMvIns(Surf);
1593 1892578 : Tsky = state.dataEnvrn->SkyTemp;
1594 : }
1595 :
1596 1892578 : if (surface_ExtBoundCond == DataSurfaces::Ground || state.dataEnvrn->IsRain) {
1597 0 : TDT(i) = TT(i) = state.dataMstBal->TempOutsideAirFD(Surf);
1598 0 : RhoT(i) = state.dataMstBal->RhoVaporAirOut(Surf);
1599 0 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0; // Outside face does not have an outer half node
1600 0 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS2(i) = -1.0; // Set this to -1 as a flag, then set to node 2's outer half node heat capacity
1601 1892578 : } else if (surface_ExtBoundCond > 0) {
1602 : // this is actually the inside face of another surface, or maybe this same surface if adiabatic
1603 : // switch around arguments for the other surf and call routines as for interior side BC from opposite face
1604 :
1605 308914 : int const ext_bound_construction(state.dataSurface->Surface(surface_ExtBoundCond).Construction);
1606 308914 : int const LayIn(state.dataConstruction->Construct(ext_bound_construction).TotLayers); // layer number for call to interior eqs
1607 308914 : int const NodeIn(s_hbfd->ConstructFD(ext_bound_construction).TotNodes + 1); // node number "I" for call to interior eqs
1608 308914 : int const TotNodesPlusOne(TotNodes + 1);
1609 308914 : if (surface_ExtBoundCond == Surf) { // adiabatic surface, PT added since it is not the same as interzone wall
1610 : // as Outside Boundary Condition Object can be left blank.
1611 :
1612 308914 : auto &surfaceFD = s_hbfd->SurfaceFD(Surf);
1613 308914 : InteriorBCEqns(state,
1614 : Delt,
1615 : NodeIn,
1616 : LayIn,
1617 : Surf,
1618 308914 : surfaceFD.T,
1619 308914 : surfaceFD.TT,
1620 308914 : surfaceFD.Rhov,
1621 308914 : surfaceFD.RhoT,
1622 308914 : surfaceFD.RH,
1623 308914 : surfaceFD.TD,
1624 308914 : surfaceFD.TDT,
1625 308914 : surfaceFD.EnthOld,
1626 308914 : surfaceFD.EnthNew,
1627 308914 : surfaceFD.TDreport);
1628 308914 : TDT(i) = surfaceFD.TDT(TotNodesPlusOne);
1629 308914 : TT(i) = surfaceFD.TT(TotNodesPlusOne);
1630 308914 : RhoT(i) = surfaceFD.RhoT(TotNodesPlusOne);
1631 :
1632 308914 : surfaceFD.CpDelXRhoS1(i) = 0.0; // Outside face does not have an outer half node
1633 308914 : surfaceFD.CpDelXRhoS2(i) = surfaceFD.CpDelXRhoS1(TotNodesPlusOne); // Save this for computing node flux values
1634 :
1635 : } else {
1636 :
1637 : // potential-lkl-from old CALL InteriorBCEqns(Delt,nodeIn,LayIn,Surf,SurfaceFD(Surface(Surf)%ExtBoundCond)%T, &
1638 0 : auto &surfaceFDEBC = s_hbfd->SurfaceFD(surface_ExtBoundCond);
1639 0 : InteriorBCEqns(state,
1640 : Delt,
1641 : NodeIn,
1642 : LayIn,
1643 : surface_ExtBoundCond,
1644 0 : surfaceFDEBC.T,
1645 0 : surfaceFDEBC.TT,
1646 0 : surfaceFDEBC.Rhov,
1647 0 : surfaceFDEBC.RhoT,
1648 0 : surfaceFDEBC.RH,
1649 0 : surfaceFDEBC.TD,
1650 0 : surfaceFDEBC.TDT,
1651 0 : surfaceFDEBC.EnthOld,
1652 0 : surfaceFDEBC.EnthNew,
1653 0 : surfaceFDEBC.TDreport);
1654 :
1655 0 : TDT(i) = surfaceFDEBC.TDT(TotNodesPlusOne);
1656 0 : TT(i) = surfaceFDEBC.TT(TotNodesPlusOne);
1657 0 : RhoT(i) = surfaceFDEBC.RhoT(TotNodesPlusOne);
1658 :
1659 0 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0; // Outside face does not have an outer half node
1660 0 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS2(i) = surfaceFDEBC.CpDelXRhoS1(TotNodesPlusOne); // Save this for computing node flux values
1661 : }
1662 :
1663 308914 : Real64 const QNetSurfFromOutside(state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(surface_ExtBoundCond)); // filled in InteriorBCEqns
1664 : // QFluxOutsideToOutSurf(Surf) = QnetSurfFromOutside
1665 308914 : state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(Surf) = -QNetSurfFromOutside;
1666 308914 : s_hbfd->QHeatOutFlux(Surf) = QNetSurfFromOutside;
1667 :
1668 : } else { // regular outside conditions
1669 1583664 : Real64 TDT_i(TDT(i));
1670 1583664 : Real64 const TDT_p(TDT(i + 1));
1671 :
1672 1583664 : Real64 Tgndsurface = 0.0;
1673 1583664 : if (state.dataSurface->Surface(Surf).UseSurfPropertyGndSurfTemp) {
1674 0 : Tgndsurface = state.dataSurface->GroundSurfsProperty(Surf).SurfsTempAvg;
1675 : } else {
1676 1583664 : Tgndsurface = state.dataMstBal->TempOutsideAirFD(Surf);
1677 : }
1678 :
1679 : // Boundary Conditions from Simulation for Exterior
1680 1583664 : Real64 const hconvo(state.dataMstBal->HConvExtFD(Surf));
1681 :
1682 1583664 : Real64 const hrad(state.dataMstBal->HAirFD(Surf));
1683 1583664 : Real64 const hsky(state.dataMstBal->HSkyFD(Surf));
1684 1583664 : Real64 const hgnd(state.dataMstBal->HGrndFD(Surf));
1685 1583664 : Real64 const Toa(state.dataMstBal->TempOutsideAirFD(Surf));
1686 1583664 : Real64 const Tgnd(Tgndsurface);
1687 :
1688 1583664 : if (surface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
1689 :
1690 1583664 : int const ConstrNum(surface.Construction);
1691 1583664 : int const MatLay(state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay));
1692 1583664 : auto *mat = state.dataMaterial->materials(MatLay);
1693 1583664 : auto const &matFD(s_hbfd->MaterialFD(MatLay));
1694 1583664 : auto const &condActuator = s_hbfd->SurfaceFD(Surf).condMaterialActuators(Lay);
1695 1583664 : auto const &specHeatActuator = s_hbfd->SurfaceFD(Surf).specHeatMaterialActuators(Lay);
1696 :
1697 : // regular outside conditions
1698 :
1699 : // Calculate the Dry Heat Conduction Equation
1700 :
1701 1583664 : if (mat->ROnly || mat->group == Material::Group::AirGap) { // R Layer or Air Layer **********
1702 : // Use algebraic equation for TDT based on R
1703 299648 : Real64 const Rlayer(mat->Resistance);
1704 299648 : TDT_i = (TDT_p + (QRadSWOutFD + hgnd * Tgnd + (hconvo + hrad) * Toa + hsky * Tsky) * Rlayer) /
1705 299648 : (1.0 + (hconvo + hgnd + hrad + hsky) * Rlayer);
1706 :
1707 299648 : } else { // Regular or phase change material layer
1708 :
1709 : // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
1710 1284016 : auto const &matFD_TempCond(matFD.TempCond);
1711 1284016 : assert(matFD_TempCond.u2() >= 3);
1712 1284016 : Real64 const lTC(matFD_TempCond.index(2, 1));
1713 : Real64 kt;
1714 1284016 : if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
1715 : // Use average temp of surface and first node for k
1716 0 : kt = terpld(matFD_TempCond, (TDT_i + TDT_p) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
1717 : } else {
1718 1284016 : kt = mat->Conductivity; // 20C base conductivity
1719 1284016 : Real64 const kt1(matFD.tk1); // linear coefficient (normally zero)
1720 1284016 : if (kt1 != 0.0) {
1721 0 : kt = +kt1 * ((TDT_i + TDT_p) / 2.0 - 20.0);
1722 : }
1723 : }
1724 :
1725 : // Check for phase change material
1726 1284016 : Real64 const TD_i(TD(i));
1727 1284016 : Real64 const Cpo(mat->SpecHeat); // Specific heat from idf
1728 1284016 : Real64 Cp(Cpo); // Specific heat modified if PCM, otherwise equal to Cpo // Will be changed if PCM
1729 1284016 : auto const &matFD_TempEnth(matFD.TempEnth);
1730 1284016 : assert(matFD_TempEnth.u2() >= 3);
1731 1284016 : Real64 const lTE(matFD_TempEnth.index(2, 1));
1732 1284016 : Real64 RhoS(mat->Density);
1733 1284016 : if (mat->hasPCM) {
1734 0 : auto *matPC = dynamic_cast<Material::MaterialPhaseChange *>(mat);
1735 0 : adjustPropertiesForPhaseChange(state, i, Surf, matPC, TD_i, TDT_i, Cp, RhoS, kt);
1736 0 : s_hbfd->SurfaceFD(Surf).EnthalpyF = matPC->enthalpyF;
1737 0 : s_hbfd->SurfaceFD(Surf).EnthalpyM = matPC->enthalpyM;
1738 1284016 : } else if (matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2] >=
1739 : 0.0) { // Phase change material: Use TempEnth data to generate Cp
1740 : // Enthalpy function used to get average specific heat. Updated by GS so enthalpy function is followed.
1741 0 : EnthOld(i) = terpld(matFD_TempEnth, TD_i, 1, 2); // 1: Temperature, 2: Enthalpy
1742 0 : EnthNew(i) = terpld(matFD_TempEnth, TDT_i, 1, 2); // 1: Temperature, 2: Enthalpy
1743 0 : if (EnthNew(i) != EnthOld(i)) {
1744 0 : Cp = max(Cpo, (EnthNew(i) - EnthOld(i)) / (TDT_i - TD_i));
1745 : }
1746 : } // Phase Change Material option
1747 :
1748 : // EMS Conductivity Override
1749 1284016 : if (condActuator.isActuated) {
1750 0 : kt = condActuator.actuatedValue;
1751 : }
1752 :
1753 : // EMS Specific Heat Override
1754 1284016 : if (specHeatActuator.isActuated) {
1755 0 : Cp = specHeatActuator.actuatedValue;
1756 : }
1757 :
1758 : // Update EMS internal variables
1759 1284016 : s_hbfd->SurfaceFD(Surf).condNodeReport(i) = kt;
1760 1284016 : s_hbfd->SurfaceFD(Surf).specHeatNodeReport(i) = Cp;
1761 :
1762 : // Choose Regular or Transparent Insulation Case
1763 1284016 : Real64 const DelX = s_hbfd->ConstructFD(ConstrNum).DelX(Lay);
1764 1284016 : Real64 const Delt_DelX(Delt * DelX);
1765 1284016 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0; // Outside face does not have an outer half node
1766 1284016 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS2(i) = (Cp * DelX * RhoS) / 2.0; // Save this for computing node flux values
1767 :
1768 1284016 : if (HMovInsul <= 0.0) { // Regular case
1769 :
1770 1284016 : if (s_hbfd->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) { // Second Order equation
1771 0 : Real64 const Cp_DelX_RhoS_2Delt(Cp * DelX * RhoS / (2.0 * Delt));
1772 0 : Real64 const kt_2DelX(kt / (2.0 * DelX));
1773 0 : Real64 const hsum(0.5 * (hconvo + hgnd + hrad + hsky));
1774 0 : TDT_i = (QRadSWOutFD + Cp_DelX_RhoS_2Delt * TD_i + kt_2DelX * (TDT_p - TD_i + TD(i + 1)) + hgnd * Tgnd +
1775 0 : (hconvo + hrad) * Toa + hsky * Tsky - hsum * TD_i) /
1776 0 : (hsum + kt_2DelX + Cp_DelX_RhoS_2Delt);
1777 1284016 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) { // First Order
1778 1284016 : Real64 const Two_Delt_DelX(2.0 * Delt_DelX);
1779 1284016 : Real64 const Cp_DelX2_RhoS(Cp * pow_2(DelX) * RhoS);
1780 1284016 : Real64 const Two_Delt_kt(2.0 * Delt * kt);
1781 1284016 : TDT_i = (Two_Delt_DelX * (QRadSWOutFD + hgnd * Tgnd + (hconvo + hrad) * Toa + hsky * Tsky) + Cp_DelX2_RhoS * TD_i +
1782 1284016 : Two_Delt_kt * TDT_p) /
1783 1284016 : (Two_Delt_DelX * (hconvo + hgnd + hrad + hsky) + Two_Delt_kt + Cp_DelX2_RhoS);
1784 : }
1785 :
1786 : } else { // HMovInsul > 0.0: Transparent insulation on outside
1787 : // Transparent insulation additions
1788 :
1789 : // Movable Insulation Layer Outside surface temp
1790 :
1791 0 : Real64 const TInsulOut((QRadSWOutMvInsulFD + hgnd * Tgnd + HMovInsul * TDT_i + (hconvo + hrad) * Toa + hsky * Tsky) /
1792 0 : (hconvo + hgnd + HMovInsul + hrad + hsky)); // Temperature of outside face of Outside Insulation
1793 0 : Real64 const Two_Delt_DelX(2.0 * Delt_DelX);
1794 0 : Real64 const Cp_DelX2_RhoS(Cp * pow_2(DelX) * RhoS);
1795 0 : Real64 const Two_Delt_kt(2.0 * Delt * kt);
1796 :
1797 : // Wall first node temperature behind Movable insulation
1798 0 : if (s_hbfd->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
1799 0 : TDT_i = (Two_Delt_DelX * (QRadSWOutFD + HMovInsul * TInsulOut) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT_p) /
1800 0 : (Two_Delt_DelX * HMovInsul + Two_Delt_kt + Cp_DelX2_RhoS);
1801 0 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
1802 : // Currently same as Crank Nicholson, need fully implicit formulation
1803 0 : TDT_i = (Two_Delt_DelX * (QRadSWOutFD + HMovInsul * TInsulOut) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT_p) /
1804 0 : (Two_Delt_DelX * HMovInsul + Two_Delt_kt + Cp_DelX2_RhoS);
1805 : } else {
1806 0 : assert(false); // Illegal CondFDSchemeType
1807 : }
1808 :
1809 : } // Regular layer or Movable insulation cases
1810 :
1811 : } // R layer or Regular layer
1812 :
1813 1583664 : CheckFDNodeTempLimits(state, Surf, i, TDT_i);
1814 :
1815 1583664 : TDT(i) = TDT_i;
1816 :
1817 : } // regular detailed FD part or SigmaR SigmaC part
1818 :
1819 : // Determine net heat flux to outside face
1820 : // One formulation that works for Fully Implicit and CrankNicholson and massless wall
1821 :
1822 1583664 : Real64 const Toa_TDT_i(Toa - TDT_i);
1823 1583664 : Real64 const QNetSurfFromOutside(QRadSWOutFD + (hgnd * (-TDT_i + Tgnd) + (hconvo + hrad) * Toa_TDT_i + hsky * (-TDT_i + Tsky)));
1824 :
1825 : // Same sign convention as CTFs
1826 1583664 : state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(Surf) = -QNetSurfFromOutside;
1827 :
1828 : // Report all outside BC heat fluxes
1829 1583664 : state.dataHeatBalSurf->SurfQdotRadOutRepPerArea(Surf) = -(hgnd * (TDT_i - Tgnd) + hrad * (-Toa_TDT_i) + hsky * (TDT_i - Tsky));
1830 1583664 : state.dataHeatBalSurf->SurfQdotRadOutRep(Surf) = surface.Area * state.dataHeatBalSurf->SurfQdotRadOutRepPerArea(Surf);
1831 1583664 : state.dataHeatBalSurf->SurfQRadOutReport(Surf) = state.dataHeatBalSurf->SurfQdotRadOutRep(Surf) * state.dataGlobal->TimeStepZoneSec;
1832 :
1833 : } // regular BC part of the ground and Rain check
1834 1892578 : }
1835 :
1836 3137500 : void InteriorNodeEqns(EnergyPlusData &state,
1837 : int const Delt, // Time Increment
1838 : int const i, // Node Index
1839 : int const Lay, // Layer Number for Construction
1840 : int const Surf, // Surface number
1841 : [[maybe_unused]] Array1D<Real64> const &T, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1842 : [[maybe_unused]] Array1D<Real64> &TT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1843 : [[maybe_unused]] Array1D<Real64> const &Rhov, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1844 : [[maybe_unused]] Array1D<Real64> &RhoT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1845 : [[maybe_unused]] Array1D<Real64> &RH, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1846 : Array1D<Real64> const &TD, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1847 : Array1D<Real64> &TDT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1848 : Array1D<Real64> &EnthOld, // Old Nodal enthalpy
1849 : Array1D<Real64> &EnthNew // New Nodal enthalpy
1850 : )
1851 : {
1852 :
1853 : // SUBROUTINE INFORMATION:
1854 : // AUTHOR Richard Liesen
1855 : // DATE WRITTEN November, 2003
1856 : // MODIFIED May 2011, B. Griffith and P. Tabares
1857 : // RE-ENGINEERED C. O. Pedersen, 2006
1858 :
1859 3137500 : int const ConstrNum(state.dataSurface->Surface(Surf).Construction);
1860 :
1861 3137500 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
1862 :
1863 3137500 : int const MatLay(state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay));
1864 3137500 : auto *mat = state.dataMaterial->materials(MatLay);
1865 3137500 : auto const &matFD = s_hbfd->MaterialFD(MatLay);
1866 3137500 : auto const &condActuator = s_hbfd->SurfaceFD(Surf).condMaterialActuators(Lay);
1867 3137500 : auto const &specHeatActuator = s_hbfd->SurfaceFD(Surf).specHeatMaterialActuators(Lay);
1868 :
1869 3137500 : Real64 const TD_i(TD(i));
1870 :
1871 3137500 : Real64 const TDT_m(TDT(i - 1));
1872 3137500 : Real64 TDT_i(TDT(i));
1873 3137500 : Real64 const TDT_p(TDT(i + 1));
1874 3137500 : Real64 const TDT_mi((TDT_m + TDT_i) / 2.0);
1875 3137500 : Real64 const TDT_ip((TDT_i + TDT_p) / 2.0);
1876 :
1877 : // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
1878 3137500 : auto const &matFD_TempCond(matFD.TempCond);
1879 3137500 : assert(matFD_TempCond.u2() >= 3);
1880 3137500 : Real64 const lTC(matFD_TempCond.index(2, 1));
1881 : Real64 ktA1; // Variable Outer Thermal conductivity in temperature equation
1882 : Real64 ktA2; // Thermal Inner conductivity in temperature equation
1883 3137500 : if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
1884 0 : ktA1 = terpld(matFD.TempCond, TDT_ip, 1, 2); // 1: Temperature, 2: Thermal conductivity
1885 0 : ktA2 = terpld(matFD.TempCond, TDT_mi, 1, 2); // 1: Temperature, 2: Thermal conductivity
1886 : } else {
1887 3137500 : ktA1 = ktA2 = mat->Conductivity; // 20C base conductivity
1888 3137500 : Real64 const kt1(matFD.tk1); // temperature coefficient for simple temp dep k. // linear coefficient (normally zero)
1889 3137500 : if (kt1 != 0.0) {
1890 0 : ktA1 += kt1 * (TDT_ip - 20.0);
1891 0 : ktA2 += kt1 * (TDT_mi - 20.0);
1892 : }
1893 : }
1894 :
1895 3137500 : Real64 const Cpo(mat->SpecHeat); // Const Cp from input
1896 3137500 : Real64 Cp(Cpo); // Cp used // Will be changed if PCM
1897 3137500 : Real64 kt(0.0);
1898 3137500 : auto const &matFD_TempEnth(matFD.TempEnth);
1899 3137500 : assert(matFD_TempEnth.u2() >= 3);
1900 3137500 : Real64 const lTE(matFD_TempEnth.index(2, 1));
1901 3137500 : Real64 RhoS(mat->Density);
1902 3137500 : if (mat->hasPCM) {
1903 0 : auto *matPC = dynamic_cast<Material::MaterialPhaseChange *>(mat);
1904 0 : adjustPropertiesForPhaseChange(state, i, Surf, matPC, TD_i, TDT_i, Cp, RhoS, kt);
1905 0 : ktA1 = matPC->getConductivity(TDT_ip);
1906 0 : ktA2 = matPC->getConductivity(TDT_mi);
1907 3137500 : } else if (matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2] >= 0.0) { // Phase change material: Use TempEnth data
1908 0 : EnthOld(i) = terpld(matFD_TempEnth, TD_i, 1, 2); // 1: Temperature, 2: Enthalpy
1909 0 : EnthNew(i) = terpld(matFD_TempEnth, TDT_i, 1, 2); // 1: Temperature, 2: Enthalpy
1910 0 : if (EnthNew(i) != EnthOld(i)) {
1911 0 : Cp = max(Cpo, (EnthNew(i) - EnthOld(i)) / (TDT_i - TD_i));
1912 : }
1913 : } // Phase Change case
1914 :
1915 : // EMS Conductivity Override
1916 3137500 : if (condActuator.isActuated) {
1917 0 : kt = condActuator.actuatedValue;
1918 0 : ktA1 = kt;
1919 0 : ktA2 = kt;
1920 : }
1921 :
1922 : // EMS Specific Heat Override
1923 3137500 : if (specHeatActuator.isActuated) {
1924 0 : Cp = specHeatActuator.actuatedValue;
1925 : }
1926 :
1927 : // Update EMS internal variables
1928 3137500 : s_hbfd->SurfaceFD(Surf).condNodeReport(i) = kt;
1929 3137500 : s_hbfd->SurfaceFD(Surf).specHeatNodeReport(i) = Cp;
1930 :
1931 3137500 : Real64 const DelX(s_hbfd->ConstructFD(ConstrNum).DelX(Lay));
1932 3137500 : Real64 const Cp_DelX_RhoS_Delt(Cp * DelX * RhoS / Delt);
1933 :
1934 3137500 : switch (s_hbfd->CondFDSchemeType) {
1935 0 : case CondFDScheme::CrankNicholsonSecondOrder: { // Adams-Moulton second order
1936 0 : Real64 const inv2DelX(1.0 / (2.0 * DelX));
1937 0 : TDT_i = ((Cp_DelX_RhoS_Delt * TD_i) + ((ktA1 * (TD(i + 1) - TD_i + TDT_p) + ktA2 * (TD(i - 1) - TD_i + TDT_m)) * inv2DelX)) /
1938 0 : (((ktA1 + ktA2) * inv2DelX) + Cp_DelX_RhoS_Delt);
1939 0 : } break;
1940 3137500 : case CondFDScheme::FullyImplicitFirstOrder: { // Adams-Moulton First order
1941 3137500 : Real64 const invDelX(1.0 / DelX);
1942 3137500 : TDT_i = ((Cp_DelX_RhoS_Delt * TD_i) + ((ktA2 * TDT_m) + (ktA1 * TDT_p)) * invDelX) / (((ktA1 + ktA2) * invDelX) + Cp_DelX_RhoS_Delt);
1943 3137500 : } break;
1944 0 : default:
1945 0 : assert(false); // Illegal CondFDSchemeType
1946 : }
1947 :
1948 3137500 : CheckFDNodeTempLimits(state, Surf, i, TDT_i);
1949 :
1950 3137500 : TDT(i) = TDT_i;
1951 3137500 : s_hbfd->SurfaceFD(Surf).CpDelXRhoS1(i) = s_hbfd->SurfaceFD(Surf).CpDelXRhoS2(i) =
1952 3137500 : (Cp * DelX * RhoS) / 2.0; // Save this for computing node flux values, half nodes are the same here
1953 3137500 : }
1954 :
1955 4175014 : void IntInterfaceNodeEqns(EnergyPlusData &state,
1956 : int const Delt, // Time Increment
1957 : int const i, // Node Index
1958 : int const Lay, // Layer Number for Construction
1959 : int const SurfNum, // Surface number
1960 : [[maybe_unused]] Array1D<Real64> const &T, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1961 : [[maybe_unused]] Array1D<Real64> &TT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1962 : [[maybe_unused]] Array1D<Real64> const &Rhov, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1963 : [[maybe_unused]] Array1D<Real64> &RhoT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
1964 : [[maybe_unused]] Array1D<Real64> &RH, // RELATIVE HUMIDITY.
1965 : Array1D<Real64> const &TD, // OLD NODE TEMPERATURES OF EACH HEAT TRANSFER SURF IN CONDFD.
1966 : Array1D<Real64> &TDT, // NEW NODE TEMPERATURES OF EACH HEAT TRANSFER SURF IN CONDFD.
1967 : [[maybe_unused]] Array1D<Real64> const &EnthOld, // Old Nodal enthalpy
1968 : Array1D<Real64> &EnthNew, // New Nodal enthalpy
1969 : [[maybe_unused]] int const GSiter // Iteration number of Gauss Seidel iteration
1970 : )
1971 : {
1972 :
1973 : // SUBROUTINE INFORMATION:
1974 : // AUTHOR Richard Liesen
1975 : // DATE WRITTEN November, 2003
1976 : // MODIFIED May 2011, B. Griffith, P. Tabares, add first order fully implicit, bug fixes, cleanup
1977 : // RE-ENGINEERED Curtis Pedersen, Changed to Implicit mode and included enthalpy. FY2006
1978 :
1979 : // PURPOSE OF THIS SUBROUTINE:
1980 : // calculate finite difference heat transfer for nodes that interface two different material layers inside construction
1981 :
1982 4175014 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
1983 4175014 : auto const &surface = state.dataSurface->Surface(SurfNum);
1984 4175014 : auto &surfFD = s_hbfd->SurfaceFD(SurfNum);
1985 :
1986 4175014 : if (surface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) { // HT Algo issue
1987 :
1988 4175014 : int const ConstrNum(surface.Construction);
1989 4175014 : auto const &construct(state.dataConstruction->Construct(ConstrNum));
1990 :
1991 4175014 : int const MatLay(construct.LayerPoint(Lay));
1992 4175014 : auto *mat = state.dataMaterial->materials(MatLay);
1993 :
1994 4175014 : int const MatLay2(construct.LayerPoint(Lay + 1));
1995 4175014 : auto *mat2 = state.dataMaterial->materials(MatLay2);
1996 :
1997 4175014 : auto const &condActuator1 = surfFD.condMaterialActuators(Lay);
1998 4175014 : auto const &condActuator2 = surfFD.condMaterialActuators(Lay + 1);
1999 :
2000 4175014 : auto const &specHeatActuator1 = surfFD.specHeatMaterialActuators(Lay);
2001 4175014 : auto const &specHeatActuator2 = surfFD.specHeatMaterialActuators(Lay + 1);
2002 :
2003 4175014 : auto const &heatFluxActuator = surfFD.heatSourceFluxMaterialActuators(Lay);
2004 :
2005 4175014 : Real64 const TDT_m(TDT(i - 1));
2006 4175014 : Real64 const TDT_p(TDT(i + 1));
2007 :
2008 4175014 : bool const RLayerPresent(mat->ROnly || mat->group == Material::Group::AirGap);
2009 4175014 : bool const RLayer2Present(mat2->ROnly || mat2->group == Material::Group::AirGap);
2010 :
2011 4175014 : Real64 const Rlayer(mat->Resistance); // Resistance value of R Layer
2012 4175014 : Real64 const Rlayer2(mat2->Resistance); // Resistance value of next layer to inside
2013 :
2014 4175014 : if (RLayerPresent && RLayer2Present) {
2015 :
2016 0 : TDT(i) = (Rlayer2 * TDT_m + Rlayer * TDT_p) / (Rlayer + Rlayer2); // Two adjacent R layers
2017 :
2018 : } else {
2019 :
2020 4175014 : auto const &matFD(s_hbfd->MaterialFD(MatLay));
2021 4175014 : auto const &matFD2(s_hbfd->MaterialFD(MatLay2));
2022 4175014 : Real64 TDT_i(TDT(i));
2023 :
2024 : // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
2025 :
2026 4175014 : Real64 kt1(0.0);
2027 4175014 : if (!RLayerPresent) {
2028 4175014 : auto const &matFD_TempCond(matFD.TempCond);
2029 4175014 : assert(matFD_TempCond.u2() >= 3);
2030 4175014 : Real64 const lTC(matFD_TempCond.index(2, 1));
2031 4175014 : if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
2032 0 : kt1 = terpld(matFD.TempCond, (TDT_i + TDT_m) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
2033 : } else {
2034 4175014 : kt1 = mat->Conductivity; // 20C base conductivity
2035 4175014 : Real64 const kt11(matFD.tk1); // temperature coefficient for simple temp dep k. // linear coefficient (normally zero)
2036 4175014 : if (kt11 != 0.0) {
2037 0 : kt1 += kt11 * ((TDT_i + TDT_m) / 2.0 - 20.0);
2038 : }
2039 : }
2040 : }
2041 :
2042 4175014 : Real64 kt2(0.0);
2043 4175014 : if (!RLayer2Present) {
2044 4175014 : auto const &matFD2_TempCond(matFD2.TempCond);
2045 4175014 : assert(matFD2_TempCond.u2() >= 3);
2046 4175014 : Real64 const lTC2(matFD2_TempCond.index(2, 1));
2047 4175014 : if (matFD2_TempCond[lTC2] + matFD2_TempCond[lTC2 + 1] + matFD2_TempCond[lTC2 + 2] >= 0.0) { // Multiple Linear Segment Function
2048 0 : kt2 = terpld(matFD2_TempCond, (TDT_i + TDT_p) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
2049 : } else {
2050 4175014 : kt2 = mat2->Conductivity; // 20C base conductivity
2051 4175014 : Real64 const kt21(matFD2.tk1); // temperature coefficient for simple temp dep k. // linear coefficient (normally zero)
2052 4175014 : if (kt21 != 0.0) {
2053 0 : kt2 += kt21 * ((TDT_i + TDT_p) / 2.0 - 20.0);
2054 : }
2055 : }
2056 : }
2057 :
2058 4175014 : Real64 RhoS1(mat->Density);
2059 4175014 : Real64 const Cpo1(mat->SpecHeat); // constant Cp from input file
2060 4175014 : Real64 Cp1(Cpo1); // Will be reset if PCM
2061 4175014 : Real64 const Delx1(s_hbfd->ConstructFD(ConstrNum).DelX(Lay));
2062 :
2063 4175014 : Real64 RhoS2(mat2->Density);
2064 4175014 : Real64 const Cpo2(mat2->SpecHeat);
2065 4175014 : Real64 Cp2(Cpo2); // will be reset if PCM
2066 4175014 : Real64 const Delx2(s_hbfd->ConstructFD(ConstrNum).DelX(Lay + 1));
2067 :
2068 : // Calculate the Dry Heat Conduction Equation
2069 :
2070 : // Source/Sink Flux Capability ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2071 :
2072 4175014 : Real64 QSSFlux = 0.0;
2073 4175014 : if ((surface.Area > 0.0) && (construct.SourceSinkPresent && Lay == construct.SourceAfterLayer)) {
2074 : // Source/Sink flux value at a layer interface // Includes QPV Source
2075 0 : QSSFlux = (state.dataHeatBalFanSys->QRadSysSource(SurfNum) + state.dataHeatBalFanSys->QPVSysSource(SurfNum)) / surface.Area;
2076 : }
2077 :
2078 : // update report variables
2079 4175014 : auto &surfFD = s_hbfd->SurfaceFD(SurfNum);
2080 :
2081 : // only includes internal heat source
2082 4175014 : surfFD.heatSourceInternalFluxLayerReport(Lay) = QSSFlux * surface.Area;
2083 4175014 : surfFD.heatSourceInternalFluxEnergyLayerReport(Lay) = QSSFlux * surface.Area * state.dataGlobal->TimeStepZoneSec;
2084 :
2085 : // Add EMS actuated value
2086 4175014 : if (heatFluxActuator.isActuated) {
2087 321224 : Real64 actuatedVal = heatFluxActuator.actuatedValue;
2088 321224 : if (actuatedVal >= 0) {
2089 321224 : QSSFlux += heatFluxActuator.actuatedValue;
2090 : } else {
2091 0 : ShowSevereError(state, fmt::format("Surface: {}, Material: {}", surface.Name, mat->Name));
2092 0 : ShowContinueError(state, "EMS Actuator does not support negative values");
2093 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
2094 : }
2095 :
2096 : // Update report variables
2097 : // Only includes the EMS values
2098 321224 : surfFD.heatSourceEMSFluxLayerReport(Lay) = heatFluxActuator.actuatedValue * surface.Area;
2099 321224 : surfFD.heatSourceEMSFluxEnergyLayerReport(Lay) =
2100 321224 : heatFluxActuator.actuatedValue * surface.Area * state.dataGlobal->TimeStepZoneSec;
2101 : }
2102 :
2103 : //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2104 :
2105 4175014 : Real64 const TD_i(TD(i));
2106 :
2107 4175014 : auto const &matFD_TempEnth(matFD.TempEnth);
2108 4175014 : assert(matFD_TempEnth.u2() >= 3);
2109 4175014 : Real64 const lTE(matFD_TempEnth.index(2, 1));
2110 4175014 : Real64 const matFD_sum(matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2]);
2111 :
2112 4175014 : auto const &matFD2_TempEnth(matFD2.TempEnth);
2113 4175014 : assert(matFD2_TempEnth.u2() >= 3);
2114 4175014 : Real64 const lTE2(matFD2_TempEnth.index(2, 1));
2115 4175014 : Real64 const matFD2_sum(matFD2_TempEnth[lTE2] + matFD2_TempEnth[lTE2 + 1] + matFD2_TempEnth[lTE2 + 2]);
2116 :
2117 4175014 : if (RLayerPresent && !RLayer2Present) { // R-layer first
2118 :
2119 : // Check for PCM second layer
2120 0 : if (mat2->hasPCM) {
2121 0 : auto *matPC2 = dynamic_cast<Material::MaterialPhaseChange *>(mat);
2122 0 : assert(matPC2 != nullptr);
2123 0 : adjustPropertiesForPhaseChange(state, i, SurfNum, matPC2, TD_i, TDT_i, Cp2, RhoS2, kt2);
2124 0 : } else if ((matFD_sum < 0.0) && (matFD2_sum > 0.0)) { // Phase change material Layer2, Use TempEnth Data
2125 0 : Real64 const Enth2Old(terpld(matFD2_TempEnth, TD_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2126 0 : Real64 const Enth2New(terpld(matFD2_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2127 0 : EnthNew(i) = Enth2New; // This node really doesn't have an enthalpy, this gives it a value
2128 0 : if ((std::abs(Enth2New - Enth2Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2129 0 : Cp2 = max(Cpo2, (Enth2New - Enth2Old) / (TDT_i - TD_i));
2130 : }
2131 : }
2132 :
2133 : // EMS Conductivity 2 Override
2134 0 : if (condActuator2.isActuated) {
2135 0 : kt2 = condActuator1.actuatedValue;
2136 : }
2137 :
2138 : // EMS Specific Heat 2 Override
2139 0 : if (specHeatActuator2.isActuated) {
2140 0 : Cp2 = specHeatActuator1.actuatedValue;
2141 : }
2142 :
2143 : // Update EMS internal variables
2144 0 : surfFD.condNodeReport(i) = kt1;
2145 0 : surfFD.specHeatNodeReport(i) = Cp1;
2146 0 : surfFD.condNodeReport(i + 1) = kt2;
2147 0 : surfFD.specHeatNodeReport(i + 1) = Cp2;
2148 :
2149 : // R layer first, then PCM or regular layer
2150 0 : Real64 const Delt_Delx2(Delt * Delx2);
2151 0 : Real64 const Cp2_fac(Cp2 * pow_2(Delx2) * RhoS2 * Rlayer);
2152 0 : Real64 const Delt_kt2_Rlayer(Delt * kt2 * Rlayer);
2153 0 : if (s_hbfd->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
2154 0 : TDT_i = (2.0 * Delt_Delx2 * QSSFlux * Rlayer + (Cp2_fac - Delt_Delx2 - Delt_kt2_Rlayer) * TD_i +
2155 0 : Delt_Delx2 * (TD(i - 1) + TDT_m) + Delt_kt2_Rlayer * (TD(i + 1) + TDT_p)) /
2156 0 : (Delt_Delx2 + Delt_kt2_Rlayer + Cp2_fac);
2157 0 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
2158 0 : Real64 const Two_Delt_Delx2(2.0 * Delt_Delx2);
2159 0 : Real64 const Two_Delt_kt2_Rlayer(2.0 * Delt_kt2_Rlayer);
2160 0 : TDT_i = (Two_Delt_Delx2 * (QSSFlux * Rlayer + TDT_m) + Cp2_fac * TD_i + Two_Delt_kt2_Rlayer * TDT_p) /
2161 0 : (Two_Delt_Delx2 + Two_Delt_kt2_Rlayer + Cp2_fac);
2162 : }
2163 :
2164 0 : CheckFDNodeTempLimits(state, SurfNum, i, TDT_i);
2165 :
2166 0 : surfFD.CpDelXRhoS1(i) = 0.0; // - rlayer has no capacitance, so this is zero
2167 0 : surfFD.CpDelXRhoS2(i) = (Cp2 * Delx2 * RhoS2) / 2.0; // Save this for computing node flux values
2168 :
2169 4175014 : } else if (!RLayerPresent && RLayer2Present) { // R-layer second
2170 :
2171 : // Check for PCM layer before R layer
2172 0 : if (mat->hasPCM) {
2173 0 : auto *matPC = dynamic_cast<Material::MaterialPhaseChange *>(mat);
2174 0 : adjustPropertiesForPhaseChange(state, i, SurfNum, matPC, TD_i, TDT_i, Cp1, RhoS1, kt1);
2175 0 : } else if ((matFD_sum > 0.0) && (matFD2_sum < 0.0)) { // Phase change material Layer1, Use TempEnth Data
2176 0 : Real64 const Enth1Old(terpld(matFD_TempEnth, TD_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2177 0 : Real64 const Enth1New(terpld(matFD_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2178 0 : EnthNew(i) = Enth1New; // This node really doesn't have an enthalpy, this gives it a value
2179 0 : if ((std::abs(Enth1New - Enth1Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2180 0 : Cp1 = max(Cpo1, (Enth1New - Enth1Old) / (TDT_i - TD_i));
2181 : }
2182 : }
2183 :
2184 : // EMS Conductivity 1 Override
2185 0 : if (condActuator1.isActuated) {
2186 0 : kt1 = condActuator1.actuatedValue;
2187 : }
2188 :
2189 : // EMS Specific Heat 1 Override
2190 0 : if (specHeatActuator1.isActuated) {
2191 0 : Cp1 = specHeatActuator1.actuatedValue;
2192 : }
2193 :
2194 : // Update EMS internal variables
2195 0 : surfFD.condNodeReport(i) = kt1;
2196 0 : surfFD.specHeatNodeReport(i) = Cp1;
2197 0 : surfFD.condNodeReport(i + 1) = kt2;
2198 0 : surfFD.specHeatNodeReport(i + 1) = Cp2;
2199 :
2200 0 : Real64 const Delt_Delx1(Delt * Delx1);
2201 0 : Real64 const Cp1_fac(Cp1 * pow_2(Delx1) * RhoS1 * Rlayer2);
2202 0 : Real64 const Delt_kt1_Rlayer2(Delt * kt1 * Rlayer2);
2203 0 : if (s_hbfd->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
2204 0 : TDT_i = (2.0 * Delt_Delx1 * QSSFlux * Rlayer2 + (Cp1_fac - Delt_Delx1 - Delt_kt1_Rlayer2) * TD_i +
2205 0 : Delt_Delx1 * (TD(i + 1) + TDT_p) + Delt_kt1_Rlayer2 * (TD(i - 1) + TDT_m)) /
2206 0 : (Delt_Delx1 + Delt_kt1_Rlayer2 + Cp1_fac);
2207 0 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
2208 0 : Real64 const Two_Delt_Delx1(2.0 * Delt_Delx1);
2209 0 : Real64 const Two_Delt_kt1_Rlayer2(2.0 * Delt_kt1_Rlayer2);
2210 0 : TDT_i = (Two_Delt_Delx1 * (QSSFlux * Rlayer2 + TDT_p) + Cp1_fac * TD_i + Two_Delt_kt1_Rlayer2 * TDT_m) /
2211 0 : (Two_Delt_Delx1 + Two_Delt_kt1_Rlayer2 + Cp1_fac);
2212 : }
2213 :
2214 0 : CheckFDNodeTempLimits(state, SurfNum, i, TDT_i);
2215 :
2216 0 : surfFD.CpDelXRhoS1(i) = (Cp1 * Delx1 * RhoS1) / 2.0; // Save this for computing node flux values
2217 0 : surfFD.CpDelXRhoS2(i) = 0.0; // - rlayer has no capacitance, so this is zero
2218 :
2219 0 : } else { // Regular or Phase Change on both sides of interface
2220 :
2221 : // Consider the various PCM material location cases
2222 4175014 : if ((matFD_sum > 0.0) && (matFD2_sum > 0.0)) { // Phase change material both layers, Use TempEnth Data
2223 :
2224 0 : Real64 const Enth1Old(terpld(matFD_TempEnth, TD_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2225 0 : Real64 const Enth2Old(terpld(matFD2_TempEnth, TD_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2226 0 : Real64 const Enth1New(terpld(matFD_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2227 0 : Real64 const Enth2New(terpld(matFD2_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2228 :
2229 0 : EnthNew(i) = Enth1New; // This node really doesn't have an enthalpy, this gives it a value
2230 :
2231 0 : if ((std::abs(Enth1New - Enth1Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2232 0 : Cp1 = max(Cpo1, (Enth1New - Enth1Old) / (TDT_i - TD_i));
2233 : }
2234 :
2235 0 : if ((std::abs(Enth2New - Enth2Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2236 0 : Cp2 = max(Cpo2, (Enth2New - Enth2Old) / (TDT_i - TD_i));
2237 : }
2238 :
2239 : // if
2240 :
2241 4175014 : } else if ((matFD_sum > 0.0) && (matFD2_sum < 0.0)) { // Phase change material Layer1, Use TempEnth Data
2242 :
2243 0 : Real64 const Enth1Old(terpld(matFD_TempEnth, TD_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2244 0 : Real64 const Enth1New(terpld(matFD_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2245 0 : EnthNew(i) = Enth1New; // This node really doesn't have an enthalpy, this gives it a value
2246 :
2247 0 : if ((std::abs(Enth1New - Enth1Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2248 0 : Cp1 = max(Cpo1, (Enth1New - Enth1Old) / (TDT_i - TD_i));
2249 : }
2250 :
2251 4175014 : } else if ((matFD_sum < 0.0) && (matFD2_sum > 0.0)) { // Phase change material Layer2, Use TempEnth Data
2252 :
2253 0 : Real64 const Enth2Old(terpld(matFD2_TempEnth, TD_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2254 0 : Real64 const Enth2New(terpld(matFD2_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
2255 0 : EnthNew(i) = Enth2New; // This node really doesn't have an enthalpy, this gives it a value
2256 :
2257 0 : if ((std::abs(Enth2New - Enth2Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2258 0 : Cp2 = max(Cpo2, (Enth2New - Enth2Old) / (TDT_i - TD_i));
2259 : }
2260 :
2261 : } // Phase change material check
2262 :
2263 4175014 : if (mat->hasPCM) {
2264 0 : auto *matPC = dynamic_cast<Material::MaterialPhaseChange *>(mat);
2265 0 : adjustPropertiesForPhaseChange(state, i, SurfNum, matPC, TD_i, TDT_i, Cp1, RhoS1, kt1);
2266 : }
2267 4175014 : if (mat2->hasPCM) {
2268 0 : auto *matPC2 = dynamic_cast<Material::MaterialPhaseChange *>(mat2);
2269 0 : adjustPropertiesForPhaseChange(state, i, SurfNum, matPC2, TD_i, TDT_i, Cp2, RhoS2, kt2);
2270 : }
2271 :
2272 : // EMS Conductivity 1 Override
2273 4175014 : if (condActuator1.isActuated) {
2274 955830 : kt1 = condActuator1.actuatedValue;
2275 : }
2276 :
2277 : // EMS Conductivity 2 Override
2278 4175014 : if (condActuator2.isActuated) {
2279 955830 : kt2 = condActuator2.actuatedValue;
2280 : }
2281 :
2282 : // EMS Specific Heat 1 Override
2283 4175014 : if (specHeatActuator1.isActuated) {
2284 955830 : Cp1 = specHeatActuator1.actuatedValue;
2285 : }
2286 :
2287 : // EMS Specific Heat 2 Override
2288 4175014 : if (specHeatActuator2.isActuated) {
2289 955830 : Cp2 = specHeatActuator2.actuatedValue;
2290 : }
2291 :
2292 : // Update EMS internal variables
2293 4175014 : surfFD.condNodeReport(i) = kt1;
2294 4175014 : surfFD.specHeatNodeReport(i) = Cp1;
2295 4175014 : surfFD.condNodeReport(i + 1) = kt2;
2296 4175014 : surfFD.specHeatNodeReport(i + 1) = Cp2;
2297 :
2298 4175014 : Real64 const Delt_Delx1(Delt * Delx1);
2299 4175014 : Real64 const Delt_Delx2(Delt * Delx2);
2300 4175014 : Real64 const Delt_Delx1_kt2(Delt_Delx1 * kt2);
2301 4175014 : Real64 const Delt_Delx2_kt1(Delt_Delx2 * kt1);
2302 4175014 : Real64 const Delt_sum(Delt_Delx1_kt2 + Delt_Delx2_kt1);
2303 4175014 : Real64 const Cp1_fac(Cp1 * pow_2(Delx1) * Delx2 * RhoS1);
2304 4175014 : Real64 const Cp2_fac(Cp2 * Delx1 * pow_2(Delx2) * RhoS2);
2305 4175014 : Real64 const Cp_fac(Cp1_fac + Cp2_fac);
2306 4175014 : if (s_hbfd->CondFDSchemeType ==
2307 : CondFDScheme::CrankNicholsonSecondOrder) { // Regular Internal Interface Node with Source/sink using Adams Moulton second
2308 : // order
2309 0 : TDT_i = (2.0 * Delt_Delx1 * Delx2 * QSSFlux + (Cp_fac - Delt_sum) * TD_i + Delt_Delx1_kt2 * (TD(i + 1) + TDT_p) +
2310 0 : Delt_Delx2_kt1 * (TD(i - 1) + TDT_m)) /
2311 0 : (Delt_sum + Cp_fac);
2312 4175014 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) { // First order adams moulton
2313 4175014 : TDT_i = (2.0 * (Delt_Delx1 * Delx2 * QSSFlux + Delt_Delx2_kt1 * TDT_m + Delt_Delx1_kt2 * TDT_p) + Cp_fac * TD_i) /
2314 4175014 : (2.0 * (Delt_Delx2_kt1 + Delt_Delx1_kt2) + Cp_fac);
2315 : }
2316 :
2317 4175014 : CheckFDNodeTempLimits(state, SurfNum, i, TDT_i);
2318 :
2319 4175014 : surfFD.CpDelXRhoS1(i) = (Cp1 * Delx1 * RhoS1) / 2.0; // Save this for computing node flux values
2320 4175014 : surfFD.CpDelXRhoS2(i) = (Cp2 * Delx2 * RhoS2) / 2.0; // Save this for computing node flux values
2321 :
2322 4175014 : if (construct.SourceSinkPresent && (Lay == construct.SourceAfterLayer)) {
2323 0 : state.dataHeatBalFanSys->TCondFDSourceNode(SurfNum) = TDT_i; // Transfer node temp to Radiant System
2324 0 : state.dataHeatBalSurf->SurfTempSource(SurfNum) = TDT_i; // Transfer node temp to DataHeatBalSurface module
2325 0 : surfFD.QSource = QSSFlux;
2326 0 : surfFD.SourceNodeNum = i;
2327 : }
2328 :
2329 4175014 : if (construct.SourceSinkPresent && (Lay == construct.TempAfterLayer)) {
2330 0 : state.dataHeatBalSurf->SurfTempUserLoc(SurfNum) = TDT_i; // Transfer node temp to DataHeatBalSurface module
2331 : }
2332 :
2333 : } // End of R-layer and Regular check
2334 :
2335 4175014 : TDT(i) = TDT_i;
2336 : }
2337 :
2338 : } // End of the CondFD if block
2339 4175014 : }
2340 :
2341 2201492 : void InteriorBCEqns(EnergyPlusData &state,
2342 : int const Delt, // Time Increment
2343 : int const i, // Node Index
2344 : int const Lay, // Layer Number for Construction
2345 : int const SurfNum, // Surface number
2346 : [[maybe_unused]] Array1D<Real64> const &T, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF (Old).
2347 : [[maybe_unused]] Array1D<Real64> &TT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF (New).
2348 : [[maybe_unused]] Array1D<Real64> const &Rhov, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
2349 : [[maybe_unused]] Array1D<Real64> &RhoT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
2350 : [[maybe_unused]] Array1D<Real64> &RH, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
2351 : Array1D<Real64> const &TD, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
2352 : Array1D<Real64> &TDT, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
2353 : Array1D<Real64> &EnthOld, // Old Nodal enthalpy
2354 : Array1D<Real64> &EnthNew, // New Nodal enthalpy
2355 : Array1D<Real64> &TDreport // Temperature value from previous HeatSurfaceHeatManager iteration's value
2356 : )
2357 : {
2358 : // SUBROUTINE INFORMATION:
2359 : // AUTHOR Richard Liesen
2360 : // DATE WRITTEN November, 2003
2361 : // MODIFIED B. Griffith, P. Tabares, May 2011, add first order fully implicit, bug fixes, cleanup
2362 : // November 2011 P. Tabares fixed problems with adiabatic walls/massless walls
2363 : // November 2011 P. Tabares fixed problems PCM stability problems
2364 : // RE-ENGINEERED C. O. Pedersen 2006
2365 :
2366 : // PURPOSE OF THIS SUBROUTINE:
2367 : // Calculate the heat transfer at the node on the surfaces inside face (facing zone)
2368 :
2369 2201492 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
2370 :
2371 2201492 : auto const &surface(state.dataSurface->Surface(SurfNum));
2372 2201492 : auto &surfFD = s_hbfd->SurfaceFD(SurfNum);
2373 2201492 : int const ConstrNum(surface.Construction);
2374 :
2375 : // Set the internal conditions to local variables
2376 : Real64 const NetLWRadToSurfFD(
2377 2201492 : state.dataHeatBalSurf->SurfQdotRadNetLWInPerArea(SurfNum)); // Net interior long wavelength radiation to surface from other surfaces
2378 2201492 : Real64 const QRadSWInFD(state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum)); // Short wave radiation absorbed on inside of opaque surface
2379 : Real64 const SurfQdotRadHVACInPerAreaFD(
2380 2201492 : state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(SurfNum)); // Total current radiant heat flux at a surface
2381 2201492 : Real64 const QRadThermInFD(state.dataHeatBal->SurfQdotRadIntGainsInPerArea(SurfNum)); // Thermal radiation absorbed on inside surfaces
2382 :
2383 : // Boundary Conditions from Simulation for Interior
2384 2201492 : Real64 hconvi(state.dataMstBal->HConvInFD(SurfNum));
2385 :
2386 2201492 : Real64 const Tia(state.dataZoneTempPredictorCorrector->zoneHeatBalance(surface.Zone).MAT);
2387 :
2388 : //++++++++++++++++++++++++++++++++++++++++++++++++++++++
2389 : // Do all the nodes in the surface Else will switch to SigmaR,SigmaC
2390 2201492 : Real64 TDT_i(TDT(i));
2391 2201492 : Real64 const QFac(NetLWRadToSurfFD + QRadSWInFD + QRadThermInFD + SurfQdotRadHVACInPerAreaFD);
2392 2201492 : if (surface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
2393 2201492 : int const MatLay(state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay));
2394 2201492 : auto *mat = state.dataMaterial->materials(MatLay);
2395 2201492 : auto const &matFD(s_hbfd->MaterialFD(MatLay));
2396 2201492 : auto const &condActuator = surfFD.condMaterialActuators(Lay);
2397 2201492 : auto const &specHeatActuator = surfFD.specHeatMaterialActuators(Lay);
2398 :
2399 : // Calculate the Dry Heat Conduction Equation
2400 :
2401 2201492 : if (mat->ROnly || mat->group == Material::Group::AirGap) { // R Layer or Air Layer
2402 : // Use algebraic equation for TDT based on R
2403 299648 : Real64 constexpr IterDampConst(
2404 : 5.0); // Damping constant for inside surface temperature iterations. Only used for massless (R-value only) Walls
2405 299648 : Real64 const Rlayer(mat->Resistance);
2406 299648 : if ((i == 1) && (surface.ExtBoundCond > 0)) { // this is for an adiabatic partition
2407 0 : TDT_i = (TDT(i + 1) + (QFac + hconvi * Tia + TDreport(i) * IterDampConst) * Rlayer) / (1.0 + (hconvi + IterDampConst) * Rlayer);
2408 : } else { // regular wall
2409 299648 : TDT_i = (TDT(i - 1) + (QFac + hconvi * Tia + TDreport(i) * IterDampConst) * Rlayer) / (1.0 + (hconvi + IterDampConst) * Rlayer);
2410 : }
2411 299648 : surfFD.CpDelXRhoS1(i) = 0.0; // Save this for computing node flux values - rlayer has no capacitance
2412 299648 : surfFD.CpDelXRhoS2(i) = 0.0; // Inside face does not have an inner half node
2413 :
2414 299648 : } else { // Regular or PCM
2415 1901844 : Real64 const TDT_m(TDT(i - 1));
2416 :
2417 : // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
2418 1901844 : auto const &matFD_TempCond(matFD.TempCond);
2419 1901844 : assert(matFD_TempCond.u2() >= 3);
2420 1901844 : Real64 const lTC(matFD_TempCond.index(2, 1));
2421 : Real64 kt;
2422 1901844 : if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
2423 : // Use average of surface and first node temp for determining k
2424 0 : kt = terpld(matFD_TempCond, (TDT_i + TDT_m) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
2425 : } else {
2426 1901844 : kt = mat->Conductivity; // 20C base conductivity
2427 1901844 : Real64 const kt1(matFD.tk1); // linear coefficient (normally zero)
2428 1901844 : if (kt1 != 0.0) {
2429 0 : kt = +kt1 * ((TDT_i + TDT_m) / 2.0 - 20.0);
2430 : }
2431 : }
2432 :
2433 1901844 : Real64 RhoS(mat->Density);
2434 1901844 : Real64 const TD_i(TD(i));
2435 1901844 : Real64 const Cpo(mat->SpecHeat);
2436 1901844 : Real64 Cp(Cpo); // Will be changed if PCM
2437 1901844 : auto const &matFD_TempEnth(matFD.TempEnth);
2438 1901844 : assert(matFD_TempEnth.u2() >= 3);
2439 1901844 : Real64 const lTE(matFD_TempEnth.index(2, 1));
2440 1901844 : if (mat->hasPCM) {
2441 0 : auto *matPC = dynamic_cast<Material::MaterialPhaseChange *>(mat);
2442 0 : adjustPropertiesForPhaseChange(state, i, SurfNum, matPC, TD_i, TDT_i, Cp, RhoS, kt);
2443 1901844 : } else if (matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2] >=
2444 : 0.0) { // Phase change material: Use TempEnth data
2445 0 : EnthOld(i) = terpld(matFD_TempEnth, TD_i, 1, 2); // 1: Temperature, 2: Enthalpy
2446 0 : EnthNew(i) = terpld(matFD_TempEnth, TDT_i, 1, 2); // 1: Temperature, 2: Enthalpy
2447 0 : if ((std::abs(EnthNew(i) - EnthOld(i)) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
2448 0 : Cp = max(Cpo, (EnthNew(i) - EnthOld(i)) / (TDT_i - TD_i));
2449 : }
2450 : } // Phase change material check
2451 :
2452 : // EMS Conductivity Override
2453 1901844 : if (condActuator.isActuated) {
2454 0 : kt = condActuator.actuatedValue;
2455 : }
2456 :
2457 : // EMS Specific Heat Override
2458 1901844 : if (specHeatActuator.isActuated) {
2459 0 : Cp = specHeatActuator.actuatedValue;
2460 : }
2461 :
2462 : // Update EMS internal variables
2463 1901844 : surfFD.condNodeReport(i) = kt;
2464 1901844 : surfFD.specHeatNodeReport(i) = Cp;
2465 :
2466 1901844 : Real64 const DelX(s_hbfd->ConstructFD(ConstrNum).DelX(Lay));
2467 1901844 : Real64 const Delt_DelX(Delt * DelX);
2468 1901844 : Real64 const Two_Delt_DelX(2.0 * Delt_DelX);
2469 1901844 : Real64 const Delt_kt(Delt * kt);
2470 1901844 : Real64 const Cp_DelX2_RhoS(Cp * pow_2(DelX) * RhoS);
2471 1901844 : if ((surface.ExtBoundCond > 0) && (i == 1)) { // this is for an adiabatic or interzone partition
2472 0 : if (s_hbfd->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) { // Adams-Moulton second order
2473 0 : TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + (Cp_DelX2_RhoS - Delt_DelX * hconvi - Delt_kt) * TD_i +
2474 0 : Delt_kt * (TD(i + 1) + TDT(i + 1))) /
2475 0 : (Delt_DelX * hconvi + Delt_kt + Cp_DelX2_RhoS);
2476 0 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) { // Adams-Moulton First order
2477 0 : Real64 const Two_Delt_kt(2.0 * Delt_kt);
2478 0 : TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT(i + 1)) /
2479 0 : (Two_Delt_DelX * hconvi + Two_Delt_kt + Cp_DelX2_RhoS);
2480 : }
2481 0 : } else { // for regular or interzone walls
2482 1901844 : if (s_hbfd->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
2483 0 : TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + (Cp_DelX2_RhoS - Delt_DelX * hconvi - Delt_kt) * TD_i +
2484 0 : Delt_kt * (TD(i - 1) + TDT_m)) /
2485 0 : (Delt_DelX * hconvi + Delt_kt + Cp_DelX2_RhoS);
2486 1901844 : } else if (s_hbfd->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
2487 1901844 : Real64 const Two_Delt_kt(2.0 * Delt_kt);
2488 1901844 : TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT_m) /
2489 1901844 : (Two_Delt_DelX * hconvi + Two_Delt_kt + Cp_DelX2_RhoS);
2490 : }
2491 : }
2492 1901844 : surfFD.CpDelXRhoS1(i) = (Cp * DelX * RhoS) / 2.0; // Save this for computing node flux values
2493 1901844 : surfFD.CpDelXRhoS2(i) = 0.0; // Inside face does not have an inner half node
2494 :
2495 : } // Regular or R layer
2496 :
2497 2201492 : CheckFDNodeTempLimits(state, SurfNum, i, TDT_i);
2498 :
2499 2201492 : TDT(i) = TDT_i;
2500 :
2501 : } // End of Regular node or SigmaR SigmaC option
2502 :
2503 2201492 : Real64 const QNetSurfInside(-(QFac + hconvi * (-TDT_i + Tia)));
2504 : // Pass inside conduction Flux [W/m2] to DataHeatBalanceSurface array
2505 2201492 : state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(SurfNum) = QNetSurfInside;
2506 2201492 : }
2507 :
2508 : // todo - function not used
2509 0 : void CheckFDSurfaceTempLimits(EnergyPlusData &state,
2510 : int const SurfNum, // surface number
2511 : Real64 const CheckTemperature // calculated temperature, not reset
2512 : )
2513 : {
2514 :
2515 : // SUBROUTINE INFORMATION:
2516 : // AUTHOR Linda Lawrie
2517 : // DATE WRITTEN August 2012
2518 :
2519 : // PURPOSE OF THIS SUBROUTINE:
2520 : // Provides a single entry point for checking surface temperature limits as well as
2521 : // setting up for recurring errors if too low or too high.
2522 :
2523 : // METHODOLOGY EMPLOYED:
2524 : // Use methodology similar to HBSurfaceManager
2525 0 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
2526 :
2527 0 : int ZoneNum = state.dataSurface->Surface(SurfNum).Zone;
2528 :
2529 0 : if (state.dataGlobal->WarmupFlag) {
2530 0 : ++s_hbfd->WarmupSurfTemp;
2531 : }
2532 0 : if (!state.dataGlobal->WarmupFlag || s_hbfd->WarmupSurfTemp > 10 || state.dataGlobal->DisplayExtraWarnings) {
2533 0 : if (CheckTemperature < DataHeatBalSurface::MinSurfaceTempLimit) {
2534 0 : if (state.dataSurface->SurfLowTempErrCount(SurfNum) == 0) {
2535 0 : ShowSevereMessage(state,
2536 0 : format("Temperature (low) out of bounds [{:.2R}] for zone=\"{}\", for surface=\"{}\"",
2537 : CheckTemperature,
2538 0 : state.dataHeatBal->Zone(ZoneNum).Name,
2539 0 : state.dataSurface->Surface(SurfNum).Name));
2540 0 : ShowContinueErrorTimeStamp(state, "");
2541 0 : if (!state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported) {
2542 0 : ShowContinueError(state, format("Zone=\"{}\", Diagnostic Details:", state.dataHeatBal->Zone(ZoneNum).Name));
2543 0 : if (state.dataHeatBal->Zone(ZoneNum).FloorArea > 0.0) {
2544 0 : ShowContinueError(
2545 : state,
2546 0 : format("...Internal Heat Gain [{:.3R}] W/m2",
2547 0 : state.dataHeatBal->Zone(ZoneNum).InternalHeatGains / state.dataHeatBal->Zone(ZoneNum).FloorArea));
2548 : } else {
2549 0 : ShowContinueError(
2550 0 : state, format("...Internal Heat Gain (no floor) [{:.3R}] W", state.dataHeatBal->Zone(ZoneNum).InternalHeatGains));
2551 : }
2552 0 : if (state.afn->simulation_control.type == AirflowNetwork::ControlType::NoMultizoneOrDistribution) {
2553 0 : ShowContinueError(state,
2554 0 : format("...Infiltration/Ventilation [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalInfilVent));
2555 0 : ShowContinueError(state, format("...Mixing/Cross Mixing [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalMixing));
2556 : } else {
2557 0 : ShowContinueError(state, "...Airflow Network Simulation: Nominal Infiltration/Ventilation/Mixing not available.");
2558 : }
2559 0 : if (state.dataHeatBal->Zone(ZoneNum).IsControlled) {
2560 0 : ShowContinueError(state, "...Zone is part of HVAC controlled system.");
2561 : } else {
2562 0 : ShowContinueError(state, "...Zone is not part of HVAC controlled system.");
2563 : }
2564 0 : state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported = true;
2565 : }
2566 0 : ShowRecurringSevereErrorAtEnd(state,
2567 0 : "Temperature (low) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
2568 0 : " for surface=" + state.dataSurface->Surface(SurfNum).Name,
2569 0 : state.dataSurface->SurfLowTempErrCount(SurfNum),
2570 : CheckTemperature,
2571 : CheckTemperature,
2572 : _,
2573 : "C",
2574 : "C");
2575 : } else {
2576 0 : ShowRecurringSevereErrorAtEnd(state,
2577 0 : "Temperature (low) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
2578 0 : " for surface=" + state.dataSurface->Surface(SurfNum).Name,
2579 0 : state.dataSurface->SurfLowTempErrCount(SurfNum),
2580 : CheckTemperature,
2581 : CheckTemperature,
2582 : _,
2583 : "C",
2584 : "C");
2585 : }
2586 : } else {
2587 0 : if (state.dataSurface->SurfHighTempErrCount(SurfNum) == 0) {
2588 0 : ShowSevereMessage(state,
2589 0 : format("Temperature (high) out of bounds ({:.2R}] for zone=\"{}\", for surface=\"{}\"",
2590 : CheckTemperature,
2591 0 : state.dataHeatBal->Zone(ZoneNum).Name,
2592 0 : state.dataSurface->Surface(SurfNum).Name));
2593 0 : ShowContinueErrorTimeStamp(state, "");
2594 0 : if (!state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported) {
2595 0 : ShowContinueError(state, format("Zone=\"{}\", Diagnostic Details:", state.dataHeatBal->Zone(ZoneNum).Name));
2596 0 : if (state.dataHeatBal->Zone(ZoneNum).FloorArea > 0.0) {
2597 0 : ShowContinueError(
2598 : state,
2599 0 : format("...Internal Heat Gain [{:.3R}] W/m2",
2600 0 : state.dataHeatBal->Zone(ZoneNum).InternalHeatGains / state.dataHeatBal->Zone(ZoneNum).FloorArea));
2601 : } else {
2602 0 : ShowContinueError(
2603 0 : state, format("...Internal Heat Gain (no floor) [{:.3R}] W", state.dataHeatBal->Zone(ZoneNum).InternalHeatGains));
2604 : }
2605 0 : if (state.afn->simulation_control.type == AirflowNetwork::ControlType::NoMultizoneOrDistribution) {
2606 0 : ShowContinueError(state,
2607 0 : format("...Infiltration/Ventilation [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalInfilVent));
2608 0 : ShowContinueError(state, format("...Mixing/Cross Mixing [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalMixing));
2609 : } else {
2610 0 : ShowContinueError(state, "...Airflow Network Simulation: Nominal Infiltration/Ventilation/Mixing not available.");
2611 : }
2612 0 : if (state.dataHeatBal->Zone(ZoneNum).IsControlled) {
2613 0 : ShowContinueError(state, "...Zone is part of HVAC controlled system.");
2614 : } else {
2615 0 : ShowContinueError(state, "...Zone is not part of HVAC controlled system.");
2616 : }
2617 0 : state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported = true;
2618 : }
2619 0 : ShowRecurringSevereErrorAtEnd(state,
2620 0 : "Temperature (high) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
2621 0 : " for surface=" + state.dataSurface->Surface(SurfNum).Name,
2622 0 : state.dataSurface->SurfHighTempErrCount(SurfNum),
2623 : CheckTemperature,
2624 : CheckTemperature,
2625 : _,
2626 : "C",
2627 : "C");
2628 : } else {
2629 0 : ShowRecurringSevereErrorAtEnd(state,
2630 0 : "Temperature (high) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
2631 0 : " for surface=" + state.dataSurface->Surface(SurfNum).Name,
2632 0 : state.dataSurface->SurfHighTempErrCount(SurfNum),
2633 : CheckTemperature,
2634 : CheckTemperature,
2635 : _,
2636 : "C",
2637 : "C");
2638 : }
2639 : }
2640 : }
2641 0 : }
2642 :
2643 11097680 : void CheckFDNodeTempLimits(EnergyPlusData &state,
2644 : int surfNum, // surface number
2645 : int nodeNum, // node number
2646 : Real64 &nodeTemp // calculated temperature, not reset
2647 : )
2648 : {
2649 11097680 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
2650 :
2651 11097680 : auto &surfFD = s_hbfd->SurfaceFD(surfNum);
2652 11097680 : auto &surfName = state.dataSurface->Surface(surfNum).Name;
2653 11097680 : auto &minTempLimit = DataHeatBalSurface::MinSurfaceTempLimit;
2654 11097680 : auto &maxTempLimit = state.dataHeatBalSurf->MaxSurfaceTempLimit;
2655 11097680 : if (nodeTemp < minTempLimit) {
2656 4 : if (surfFD.indexNodeMinTempLimit == 0) {
2657 4 : ShowSevereMessage(state,
2658 4 : format("Node temperature (low) out of bounds [{:.2R}] for surface={}, node={}", nodeTemp, surfName, nodeNum));
2659 4 : ShowContinueErrorTimeStamp(state, "");
2660 2 : ShowContinueError(state, format("Value has been reset to the lower limit value of {:.2R}.", minTempLimit));
2661 : }
2662 28 : ShowRecurringSevereErrorAtEnd(
2663 8 : state, "Node temperature (low) out of bounds for surface=" + surfName, surfFD.indexNodeMinTempLimit, nodeTemp, nodeTemp, _, "C", "C");
2664 4 : nodeTemp = minTempLimit;
2665 11097676 : } else if (nodeTemp > maxTempLimit) {
2666 4 : if (surfFD.indexNodeMaxTempLimit == 0) {
2667 4 : ShowSevereMessage(state,
2668 4 : format("Node temperature (high) out of bounds [{:.2R}] for surface={}, node={}", nodeTemp, surfName, nodeNum));
2669 4 : ShowContinueErrorTimeStamp(state, "");
2670 2 : ShowContinueError(state, format("Value has been reset to the upper limit value of {:.2R}.", maxTempLimit));
2671 : }
2672 28 : ShowRecurringSevereErrorAtEnd(state,
2673 4 : "Node temperature (high) out of bounds for surface=" + surfName,
2674 4 : surfFD.indexNodeMaxTempLimit,
2675 : nodeTemp,
2676 : nodeTemp,
2677 : _,
2678 : "C",
2679 : "C");
2680 4 : nodeTemp = maxTempLimit;
2681 : }
2682 11097680 : }
2683 :
2684 599176 : void CalcNodeHeatFlux(EnergyPlusData &state,
2685 : int const SurfNum, // surface number
2686 : int const TotNodes // number of nodes in surface
2687 : )
2688 : {
2689 :
2690 : // SUBROUTINE INFORMATION:
2691 : // AUTHOR M.J. Witte
2692 : // DATE WRITTEN Sept-Nov 2015
2693 : // PURPOSE OF THIS SUBROUTINE:
2694 : // Calculate flux at each condFD node
2695 :
2696 599176 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
2697 599176 : auto &surfFD = s_hbfd->SurfaceFD(SurfNum);
2698 :
2699 : // SurfaceFD.QDreport( n ) is the flux at node n
2700 : // When this is called TDT( NodeNum ) is the new node temp and TDpriortimestep( NodeNum ) holds the previous node temp
2701 : // For the TDT and TDpriortimestep arrays, Node 1 is the outside face, and Node TotNodes+1 is the inside face
2702 :
2703 : // Last node is always the surface inside face. Start calculations here because the outside face is not defined for all surfaces.
2704 : // Note that TotNodes is the number of nodes in the surface including the outside face node, but not the inside face node
2705 : // so the arrays are all allocated to Totodes+1
2706 :
2707 : // Heat flux at the inside face node (TotNodes+1)
2708 599176 : surfFD.QDreport(TotNodes + 1) = state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(SurfNum);
2709 :
2710 : // Heat flux for remaining nodes.
2711 3495190 : for (int node = TotNodes; node >= 1; --node) {
2712 : // Start with inside face (above) and work outward, positive value is flowing towards the inside face
2713 : // CpDelXRhoS1 is outer half-node heat capacity, CpDelXRhoS2 is inner half node heat capacity
2714 : Real64 interNodeFlux; // heat flux at the plane between node and node+1 [W/m2]
2715 : Real64 sourceFlux; // Internal source flux [W/m2]
2716 2896014 : if (surfFD.SourceNodeNum == node) {
2717 0 : sourceFlux = surfFD.QSource;
2718 : } else {
2719 2896014 : sourceFlux = 0.0;
2720 : }
2721 2896014 : interNodeFlux = surfFD.QDreport(node + 1) + surfFD.CpDelXRhoS1(node + 1) * (surfFD.TDT(node + 1) - surfFD.TDpriortimestep(node + 1)) /
2722 2896014 : state.dataGlobal->TimeStepZoneSec;
2723 2896014 : surfFD.QDreport(node) = interNodeFlux - sourceFlux +
2724 2896014 : surfFD.CpDelXRhoS2(node) * (surfFD.TDT(node) - surfFD.TDpriortimestep(node)) / state.dataGlobal->TimeStepZoneSec;
2725 : }
2726 599176 : if (state.dataEnvrn->IsRain) {
2727 2 : state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(SurfNum) = -surfFD.QDreport(1); // Update the outside flux if it is raining
2728 : }
2729 599176 : }
2730 :
2731 1 : void adjustPropertiesForPhaseChange(EnergyPlusData &state,
2732 : int finiteDifferenceLayerIndex,
2733 : int surfNum,
2734 : Material::MaterialPhaseChange *mat,
2735 : Real64 temperaturePrevious,
2736 : Real64 temperatureUpdated,
2737 : Real64 &updatedSpecificHeat,
2738 : Real64 &updatedDensity,
2739 : Real64 &updatedThermalConductivity)
2740 : {
2741 1 : auto &s_hbfd = state.dataHeatBalFiniteDiffMgr;
2742 1 : auto &surfFD = s_hbfd->SurfaceFD(surfNum);
2743 :
2744 1 : updatedSpecificHeat = mat->getCurrentSpecificHeat(temperaturePrevious,
2745 : temperatureUpdated,
2746 1 : surfFD.PhaseChangeTemperatureReverse(finiteDifferenceLayerIndex),
2747 1 : surfFD.PhaseChangeStateOld(finiteDifferenceLayerIndex),
2748 : surfFD.PhaseChangeState(finiteDifferenceLayerIndex));
2749 :
2750 1 : surfFD.PhaseChangeStateRep(finiteDifferenceLayerIndex) = Material::phaseInts[(int)surfFD.PhaseChangeState(finiteDifferenceLayerIndex)];
2751 1 : updatedDensity = mat->getDensity(temperaturePrevious);
2752 1 : updatedThermalConductivity = mat->getConductivity(temperatureUpdated);
2753 1 : }
2754 :
2755 20 : bool findAnySurfacesUsingConstructionAndCondFD(EnergyPlusData const &state, int const constructionNum)
2756 : {
2757 71 : for (auto const &thisSurface : state.dataSurface->Surface) {
2758 70 : if (thisSurface.Construction == constructionNum) {
2759 20 : if (thisSurface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
2760 19 : return true;
2761 : }
2762 : }
2763 39 : }
2764 1 : return false;
2765 : }
2766 :
2767 : } // namespace HeatBalFiniteDiffManager
2768 :
2769 : } // namespace EnergyPlus
|