Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Construction.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataEnvironment.hh>
59 : #include <EnergyPlus/DataHeatBalSurface.hh>
60 : #include <EnergyPlus/DataHeatBalance.hh>
61 : #include <EnergyPlus/DataSurfaces.hh>
62 : #include <EnergyPlus/DaylightingManager.hh>
63 : #include <EnergyPlus/Material.hh>
64 : #include <EnergyPlus/OutputProcessor.hh>
65 : #include <EnergyPlus/UtilityRoutines.hh>
66 :
67 : namespace EnergyPlus::DataHeatBalance {
68 :
69 : // MODULE INFORMATION:
70 : // AUTHOR Rick Strand
71 : // DATE WRITTEN August 1997 (rewritten)
72 : // MODIFIED Aug-Oct 1997 RKS (added derived types)
73 : // MODIFIED Feb 1999 (FW) Added visible radiation parameters,
74 : // WindowShadingControl type and SurfaceWindowCalc type
75 : // Sep 1999 (FW) Added/modified Window4 gas variables
76 : // Jul 2003 (CC) Added reference temperature variable for air models
77 : // Aug 2003 (FW) Added FractionReturnAirPlenTempCoeff1 and
78 : // FractionReturnAirPlenTempCoeff2 to Type LightsData
79 : // Nov 2003 (FW) Add FullExteriorWithRefl and FullInteriorExteriorWithRefl
80 : // as SolarDistribution values
81 : // Dec 2003 (PGE) Added Zone List and Zone Group; added SNLoad variables
82 : // August 2006 (COP) Added variable k coefficient and PCM enthalpy.
83 : // Dec 2006 (DJS-PSU) Added ecoroof material
84 : // Dec 2008 TH added new properties to MaterialProperties and
85 : // ConstructionData for thermochromic windows
86 : // RE-ENGINEERED na
87 :
88 : // PURPOSE OF THIS MODULE:
89 : // This module should contain the information that is needed to pass
90 : // from the Heat Balance Module and all of the Zone Initializations
91 : // such as ConductionTransferFunction, GlassCalculation,
92 : // SolarShading, etc. Modules.
93 :
94 : // Using/Aliasing
95 : using namespace DataVectorTypes;
96 : using DataBSDFWindow::BSDFLayerAbsorpStruct;
97 : using DataBSDFWindow::BSDFWindowInputStruct;
98 :
99 : // Functions
100 :
101 72 : Real64 SpaceData::sumHATsurf(EnergyPlusData &state)
102 : {
103 : // PURPOSE OF THIS FUNCTION:
104 : // This function calculates the space sum of Hc*Area*Tsurf.
105 :
106 72 : Real64 sumHATsurf = 0.0;
107 :
108 168 : for (int surfNum = this->HTSurfaceFirst; surfNum <= this->HTSurfaceLast; ++surfNum) {
109 96 : Real64 Area = state.dataSurface->Surface(surfNum).Area;
110 :
111 96 : if (state.dataSurface->Surface(surfNum).Class == DataSurfaces::SurfaceClass::Window) {
112 0 : if (state.dataSurface->SurfWinDividerArea(surfNum) > 0.0) {
113 0 : if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(surfNum))) {
114 : // The area is the shade or blind area = sum of the glazing area and the divider area (which is zero if no divider)
115 0 : Area += state.dataSurface->SurfWinDividerArea(surfNum);
116 : } else {
117 : // Window divider contribution (only for window with divider and no interior shade or blind)
118 0 : sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * state.dataSurface->SurfWinDividerArea(surfNum) *
119 0 : (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(surfNum)) * state.dataSurface->SurfWinDividerTempIn(surfNum);
120 : }
121 : }
122 :
123 0 : if (state.dataSurface->SurfWinFrameArea(surfNum) > 0.0) {
124 : // Window frame contribution
125 0 : sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * state.dataSurface->SurfWinFrameArea(surfNum) *
126 0 : (1.0 + state.dataSurface->SurfWinProjCorrFrIn(surfNum)) * state.dataSurface->SurfWinFrameTempIn(surfNum);
127 : }
128 : }
129 :
130 96 : sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(surfNum);
131 : }
132 :
133 72 : return sumHATsurf;
134 : }
135 :
136 24 : Real64 ZoneData::sumHATsurf(EnergyPlusData &state)
137 : {
138 24 : Real64 sumHATsurf = 0.0;
139 96 : for (int spaceNum : this->spaceIndexes) {
140 72 : sumHATsurf += state.dataHeatBal->space(spaceNum).sumHATsurf(state);
141 : }
142 24 : return sumHATsurf;
143 : }
144 336921 : void ZoneData::SetOutBulbTempAt(EnergyPlusData &state)
145 : {
146 : // SUBROUTINE INFORMATION:
147 : // AUTHOR Noel Keen (LBL)/Linda Lawrie
148 : // DATE WRITTEN August 2010
149 : // MODIFIED na
150 : // RE-ENGINEERED na
151 :
152 : // PURPOSE OF THIS SUBROUTINE:
153 : // Routine provides facility for doing bulk Set Temperature at Height.
154 :
155 336921 : if (state.dataEnvrn->SiteTempGradient == 0.0) {
156 0 : OutDryBulbTemp = state.dataEnvrn->OutDryBulbTemp;
157 0 : OutWetBulbTemp = state.dataEnvrn->OutWetBulbTemp;
158 : } else {
159 : // Base temperatures at Z = 0 (C)
160 336921 : Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
161 336921 : Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
162 :
163 336921 : Real64 const Z(Centroid.z); // Centroid value
164 336921 : if (Z <= 0.0) {
165 6371 : OutDryBulbTemp = BaseDryTemp;
166 6371 : OutWetBulbTemp = BaseWetTemp;
167 : } else {
168 330550 : OutDryBulbTemp = BaseDryTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
169 330550 : OutWetBulbTemp = BaseWetTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
170 : }
171 : }
172 336921 : }
173 :
174 336646 : void ZoneData::SetWindSpeedAt(EnergyPlusData const &state, Real64 const fac)
175 : {
176 : // SUBROUTINE INFORMATION:
177 : // AUTHOR Linda Lawrie
178 : // DATE WRITTEN June 2013
179 : // MODIFIED na
180 : // RE-ENGINEERED na
181 :
182 : // PURPOSE OF THIS SUBROUTINE:
183 : // Routine provides facility for doing bulk Set Windspeed at Height.
184 :
185 336646 : if (state.dataEnvrn->SiteWindExp == 0.0) {
186 0 : WindSpeed = state.dataEnvrn->WindSpeed;
187 : } else {
188 336646 : Real64 const Z(Centroid.z); // Centroid value
189 336646 : if (Z <= 0.0) {
190 6356 : WindSpeed = 0.0;
191 : } else {
192 : // [Met] - at meterological Station, Height of measurement is usually 10m above ground
193 : // LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
194 : // * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
195 330290 : WindSpeed = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
196 : }
197 : }
198 336646 : }
199 :
200 336646 : void ZoneData::SetWindDirAt(Real64 const fac)
201 : {
202 336646 : WindDir = fac;
203 336646 : }
204 :
205 166 : void AirReportVars::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string const &name)
206 : {
207 498 : SetupOutputVariable(state,
208 332 : format("{} Mean Air Temperature", prefix),
209 : Constant::Units::C,
210 166 : this->MeanAirTemp,
211 : OutputProcessor::TimeStepType::Zone,
212 : OutputProcessor::StoreType::Average,
213 : name);
214 498 : SetupOutputVariable(state,
215 332 : format("{} Wetbulb Globe Temperature", prefix),
216 : Constant::Units::C,
217 166 : this->WetbulbGlobeTemp,
218 : OutputProcessor::TimeStepType::Zone,
219 : OutputProcessor::StoreType::Average,
220 : name);
221 498 : SetupOutputVariable(state,
222 332 : format("{} Operative Temperature", prefix),
223 : Constant::Units::C,
224 166 : this->OperativeTemp,
225 : OutputProcessor::TimeStepType::Zone,
226 : OutputProcessor::StoreType::Average,
227 : name);
228 498 : SetupOutputVariable(state,
229 332 : format("{} Mean Air Dewpoint Temperature", prefix),
230 : Constant::Units::C,
231 166 : this->MeanAirDewPointTemp,
232 : OutputProcessor::TimeStepType::Zone,
233 : OutputProcessor::StoreType::Average,
234 : name);
235 498 : SetupOutputVariable(state,
236 332 : format("{} Mean Air Humidity Ratio", prefix),
237 : Constant::Units::kgWater_kgDryAir,
238 166 : this->MeanAirHumRat,
239 : OutputProcessor::TimeStepType::Zone,
240 : OutputProcessor::StoreType::Average,
241 : name);
242 498 : SetupOutputVariable(state,
243 332 : format("{} Air Heat Balance Internal Convective Heat Gain Rate", prefix),
244 : Constant::Units::W,
245 166 : this->SumIntGains,
246 : OutputProcessor::TimeStepType::System,
247 : OutputProcessor::StoreType::Average,
248 : name);
249 498 : SetupOutputVariable(state,
250 332 : format("{} Air Heat Balance Surface Convection Rate", prefix),
251 : Constant::Units::W,
252 166 : this->SumHADTsurfs,
253 : OutputProcessor::TimeStepType::System,
254 : OutputProcessor::StoreType::Average,
255 : name);
256 498 : SetupOutputVariable(state,
257 332 : format("{} Air Heat Balance Interzone Air Transfer Rate", prefix),
258 : Constant::Units::W,
259 166 : this->SumMCpDTzones,
260 : OutputProcessor::TimeStepType::System,
261 : OutputProcessor::StoreType::Average,
262 : name);
263 498 : SetupOutputVariable(state,
264 332 : format("{} Air Heat Balance Outdoor Air Transfer Rate", prefix),
265 : Constant::Units::W,
266 166 : this->SumMCpDtInfil,
267 : OutputProcessor::TimeStepType::System,
268 : OutputProcessor::StoreType::Average,
269 : name);
270 498 : SetupOutputVariable(state,
271 332 : format("{} Air Heat Balance System Air Transfer Rate", prefix),
272 : Constant::Units::W,
273 166 : this->SumMCpDTsystem,
274 : OutputProcessor::TimeStepType::System,
275 : OutputProcessor::StoreType::Average,
276 : name);
277 498 : SetupOutputVariable(state,
278 332 : format("{} Air Heat Balance System Convective Heat Gain Rate", prefix),
279 : Constant::Units::W,
280 166 : this->SumNonAirSystem,
281 : OutputProcessor::TimeStepType::System,
282 : OutputProcessor::StoreType::Average,
283 : name);
284 498 : SetupOutputVariable(state,
285 332 : format("{} Air Heat Balance Air Energy Storage Rate", prefix),
286 : Constant::Units::W,
287 166 : this->CzdTdt,
288 : OutputProcessor::TimeStepType::System,
289 : OutputProcessor::StoreType::Average,
290 : name);
291 166 : if (state.dataGlobal->DisplayAdvancedReportVariables) {
292 0 : SetupOutputVariable(state,
293 0 : format("{} Air Heat Balance Deviation Rate", prefix),
294 : Constant::Units::W,
295 0 : this->imBalance,
296 : OutputProcessor::TimeStepType::System,
297 : OutputProcessor::StoreType::Average,
298 : name);
299 : }
300 166 : }
301 :
302 250148 : void SetZoneOutBulbTempAt(EnergyPlusData &state)
303 : {
304 587069 : for (auto &zone : state.dataHeatBal->Zone) {
305 336921 : zone.SetOutBulbTempAt(state);
306 : }
307 250148 : }
308 :
309 250148 : void CheckZoneOutBulbTempAt(EnergyPlusData &state)
310 : {
311 : // Using/Aliasing
312 : using DataEnvironment::SetOutBulbTempAt_error;
313 :
314 250148 : Real64 minBulb = 0.0;
315 587069 : for (auto &zone : state.dataHeatBal->Zone) {
316 336921 : minBulb = min(minBulb, zone.OutDryBulbTemp, zone.OutWetBulbTemp);
317 336921 : if (minBulb < -100.0) SetOutBulbTempAt_error(state, "Zone", zone.Centroid.z, zone.Name);
318 : }
319 250148 : }
320 :
321 249956 : void SetZoneWindSpeedAt(EnergyPlusData &state)
322 : {
323 249956 : Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
324 249956 : std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
325 586602 : for (auto &zone : state.dataHeatBal->Zone) {
326 336646 : zone.SetWindSpeedAt(state, fac);
327 : }
328 249956 : }
329 :
330 249956 : void SetZoneWindDirAt(EnergyPlusData &state)
331 : {
332 : // Using/Aliasing
333 249956 : Real64 const fac(state.dataEnvrn->WindDir);
334 586602 : for (auto &zone : state.dataHeatBal->Zone) {
335 336646 : zone.SetWindDirAt(fac);
336 : }
337 249956 : }
338 :
339 787 : void CheckAndSetConstructionProperties(EnergyPlusData &state,
340 : int const ConstrNum, // Construction number to be set/checked
341 : bool &ErrorsFound // error flag that is set when certain errors have occurred
342 : )
343 : {
344 :
345 : // SUBROUTINE INFORMATION:
346 : // AUTHOR Linda Lawrie
347 : // DATE WRITTEN December 2006
348 :
349 : // This routine checks some properties of entered constructions; sets some properties; and sets
350 : // an error flag for certain error conditions.
351 787 : auto &s_mat = state.dataMaterial;
352 :
353 787 : auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
354 787 : int TotLayers = thisConstruct.TotLayers; // Number of layers in a construction
355 787 : if (TotLayers == 0) return; // error condition, hopefully caught elsewhere
356 787 : int InsideLayer = TotLayers; // Inside Layer of Construct; for window construct, layer no. of inside glass
357 787 : if (thisConstruct.LayerPoint(InsideLayer) <= 0) return; // Error condition
358 :
359 : // window screen is not allowed on inside layer
360 :
361 784 : thisConstruct.DayltPropPtr = 0;
362 784 : int InsideMaterNum = thisConstruct.LayerPoint(InsideLayer); // Material "number" of the Inside layer
363 784 : if (InsideMaterNum != 0) {
364 784 : auto const *mat = s_mat->materials(InsideMaterNum);
365 784 : thisConstruct.InsideAbsorpVis = mat->AbsorpVisible;
366 784 : thisConstruct.InsideAbsorpSolar = mat->AbsorpSolar;
367 :
368 : // Following line applies only to opaque surfaces; it is recalculated later for windows.
369 784 : thisConstruct.ReflectVisDiffBack = 1.0 - mat->AbsorpVisible;
370 : }
371 :
372 784 : int OutsideMaterNum = thisConstruct.LayerPoint(1); // Material "number" of the Outside layer
373 784 : if (OutsideMaterNum != 0) {
374 784 : auto const *mat = s_mat->materials(OutsideMaterNum);
375 784 : thisConstruct.OutsideAbsorpVis = mat->AbsorpVisible;
376 784 : thisConstruct.OutsideAbsorpSolar = mat->AbsorpSolar;
377 : }
378 :
379 784 : thisConstruct.TotSolidLayers = 0;
380 784 : thisConstruct.TotGlassLayers = 0;
381 784 : thisConstruct.AbsDiffShade = 0.0;
382 :
383 : // Check if any layer is glass, gas, shade, screen or blind; if so it is considered a window construction for
384 : // purposes of error checking.
385 :
386 784 : thisConstruct.TypeIsWindow = false;
387 2206 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
388 1422 : int const MaterNum = thisConstruct.LayerPoint(Layer);
389 1422 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
390 1422 : auto const *mat = s_mat->materials(MaterNum);
391 1422 : thisConstruct.TypeIsWindow =
392 1285 : (mat->group == Material::Group::Glass || mat->group == Material::Group::Gas || mat->group == Material::Group::GasMixture ||
393 1249 : mat->group == Material::Group::Shade || mat->group == Material::Group::Blind || mat->group == Material::Group::Screen ||
394 1245 : mat->group == Material::Group::GlassSimple || mat->group == Material::Group::ComplexShade ||
395 1220 : mat->group == Material::Group::ComplexWindowGap || mat->group == Material::Group::GlassEQL || mat->group == Material::Group::ShadeEQL ||
396 3907 : mat->group == Material::Group::DrapeEQL || mat->group == Material::Group::ScreenEQL || mat->group == Material::Group::BlindEQL ||
397 1200 : mat->group == Material::Group::WindowGapEQL);
398 1422 : bool TypeIsNotWindow =
399 1422 : (mat->group == Material::Group::Invalid || mat->group == Material::Group::AirGap || mat->group == Material::Group::Regular ||
400 2844 : mat->group == Material::Group::EcoRoof || mat->group == Material::Group::IRTransparent);
401 1422 : if (!thisConstruct.TypeIsWindow && !TypeIsNotWindow) assert(false);
402 : }
403 :
404 784 : if (InsideMaterNum == 0) return;
405 784 : auto const *matInside = s_mat->materials(InsideMaterNum);
406 784 : if (OutsideMaterNum == 0) return;
407 784 : auto const *matOutside = s_mat->materials(OutsideMaterNum);
408 :
409 784 : if (thisConstruct.TypeIsWindow) {
410 :
411 131 : bool WrongMaterialsMix = false;
412 131 : thisConstruct.NumCTFTerms = 0;
413 131 : thisConstruct.NumHistories = 0;
414 364 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
415 233 : int const MaterNum = thisConstruct.LayerPoint(Layer);
416 233 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
417 233 : auto const *mat = s_mat->materials(MaterNum);
418 233 : WrongMaterialsMix =
419 293 : !((mat->group == Material::Group::Glass) || (mat->group == Material::Group::Gas) || (mat->group == Material::Group::GasMixture) ||
420 60 : (mat->group == Material::Group::Shade) || (mat->group == Material::Group::Blind) || (mat->group == Material::Group::Screen) ||
421 56 : (mat->group == Material::Group::GlassSimple) || (mat->group == Material::Group::ComplexShade) ||
422 31 : (mat->group == Material::Group::ComplexWindowGap) || (mat->group == Material::Group::GlassEQL) ||
423 15 : (mat->group == Material::Group::ShadeEQL) || (mat->group == Material::Group::DrapeEQL) ||
424 15 : (mat->group == Material::Group::ScreenEQL) || (mat->group == Material::Group::BlindEQL) ||
425 11 : (mat->group == Material::Group::WindowGapEQL));
426 : }
427 :
428 131 : if (WrongMaterialsMix) { // Illegal material for a window construction
429 0 : ShowSevereError(state,
430 0 : format("Error: Window construction={} has materials other than glass, gas, shade, screen, blind, complex shading, "
431 : "complex gap, or simple system.",
432 0 : thisConstruct.Name));
433 0 : ErrorsFound = true;
434 : // Do not check number of layers for BSDF type of window since that can be handled
435 131 : } else if ((TotLayers > 8) && (!thisConstruct.WindowTypeBSDF) &&
436 0 : (!thisConstruct.WindowTypeEQL)) { // Too many layers for a window construction
437 0 : ShowSevereError(state,
438 0 : format("CheckAndSetConstructionProperties: Window construction={} has too many layers (max of 8 allowed -- 4 glass + 3 "
439 : "gap + 1 shading device).",
440 0 : thisConstruct.Name));
441 0 : ErrorsFound = true;
442 :
443 131 : } else if (TotLayers == 1) {
444 83 : auto const *mat = s_mat->materials(thisConstruct.LayerPoint(1));
445 83 : Material::Group matGroup = mat->group;
446 83 : if ((matGroup == Material::Group::Shade) || (matGroup == Material::Group::Gas) || (matGroup == Material::Group::GasMixture) ||
447 83 : (matGroup == Material::Group::Blind) || (matGroup == Material::Group::Screen) || (matGroup == Material::Group::ComplexShade) ||
448 : (matGroup == Material::Group::ComplexWindowGap)) {
449 0 : ShowSevereError(state,
450 0 : format("CheckAndSetConstructionProperties: The single-layer window construction={} has a gas, complex gap, shade, "
451 : "complex shade, screen or blind material; it should be glass of simple glazing system.",
452 0 : thisConstruct.Name));
453 0 : ErrorsFound = true;
454 : }
455 : }
456 :
457 : // Find total glass layers, total shade/blind layers and total gas layers in a window construction
458 :
459 131 : bool WrongWindowLayering = false;
460 131 : int TotGlassLayers = 0;
461 131 : int TotShadeLayers = 0; // Includes shades, blinds, and screens
462 131 : int TotGasLayers = 0;
463 364 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
464 233 : int const MaterNum = thisConstruct.LayerPoint(Layer);
465 233 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
466 233 : auto const *mat = s_mat->materials(MaterNum);
467 233 : if (mat->group == Material::Group::Glass) ++TotGlassLayers;
468 233 : if (mat->group == Material::Group::GlassSimple) ++TotGlassLayers;
469 233 : if (mat->group == Material::Group::Shade || mat->group == Material::Group::Blind || mat->group == Material::Group::Screen ||
470 229 : mat->group == Material::Group::ComplexShade)
471 5 : ++TotShadeLayers;
472 233 : if (mat->group == Material::Group::Gas || mat->group == Material::Group::GasMixture || mat->group == Material::Group::ComplexWindowGap)
473 38 : ++TotGasLayers;
474 233 : if (Layer < TotLayers) {
475 102 : int const MaterNumNext = thisConstruct.LayerPoint(Layer + 1);
476 : // Adjacent layers of same type not allowed
477 102 : if (MaterNumNext == 0) continue;
478 102 : if (mat->group == s_mat->materials(MaterNumNext)->group) WrongWindowLayering = true;
479 : }
480 : }
481 :
482 : // It is not necessary to check rest of BSDF window structure since that is performed inside TARCOG90 routine.
483 : // That routine also allow structures which are not allowed in rest of this routine
484 131 : if (thisConstruct.WindowTypeBSDF) {
485 1 : thisConstruct.TotGlassLayers = TotGlassLayers;
486 1 : thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
487 1 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
488 1 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
489 1 : return;
490 : }
491 :
492 130 : if (thisConstruct.WindowTypeEQL) {
493 8 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
494 8 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
495 8 : return;
496 : }
497 :
498 122 : if (matOutside->group == Material::Group::Gas || matOutside->group == Material::Group::GasMixture ||
499 122 : matInside->group == Material::Group::Gas || matInside->group == Material::Group::GasMixture)
500 0 : WrongWindowLayering = true; // Gas cannot be first or last layer
501 122 : if (TotShadeLayers > 1) WrongWindowLayering = true; // At most one shade, screen or blind allowed
502 :
503 : // If there is a diffusing glass layer no shade, screen or blind is allowed
504 320 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
505 198 : int const MatNum = thisConstruct.LayerPoint(Layer);
506 198 : if (MatNum == 0) continue; // error -- has been caught will stop program later
507 198 : auto const *mat = s_mat->materials(MatNum);
508 198 : if (mat->group != Material::Group::Glass) continue;
509 135 : auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
510 135 : assert(matGlass != nullptr);
511 135 : if (matGlass->SolarDiffusing && TotShadeLayers > 0) {
512 0 : ErrorsFound = true;
513 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
514 0 : ShowContinueError(state, format("has diffusing glass={} and a shade, screen or blind layer.", matGlass->Name));
515 0 : break;
516 : }
517 : }
518 :
519 : // If there is a diffusing glass layer it must be the innermost layer
520 122 : if (TotGlassLayers > 1) {
521 36 : int GlassLayNum = 0;
522 144 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
523 108 : int const MatNum = thisConstruct.LayerPoint(Layer);
524 108 : if (MatNum == 0) continue; // error -- has been caught will stop program later
525 108 : auto const *mat = s_mat->materials(MatNum);
526 108 : if (mat->group != Material::Group::Glass) continue;
527 :
528 72 : auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
529 72 : assert(matGlass != nullptr);
530 72 : ++GlassLayNum;
531 72 : if (GlassLayNum < TotGlassLayers && matGlass->SolarDiffusing) {
532 0 : ErrorsFound = true;
533 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
534 0 : ShowContinueError(state, format("has diffusing glass={} that is not the innermost glass layer.", matGlass->Name));
535 : }
536 : }
537 : }
538 :
539 : // interior window screen is not allowed. Check for invalid between-glass screen is checked below.
540 122 : if (TotShadeLayers == 1 && matInside->group == Material::Group::Screen && TotLayers != 1) {
541 0 : WrongWindowLayering = true;
542 : }
543 :
544 : // Consistency checks for a construction with a between-glass shade or blind
545 :
546 122 : if (TotShadeLayers == 1 && matOutside->group != Material::Group::Shade && matOutside->group != Material::Group::Blind &&
547 4 : matOutside->group != Material::Group::Screen && matInside->group != Material::Group::Shade &&
548 2 : matInside->group != Material::Group::Blind && matInside->group != Material::Group::ComplexShade && !WrongWindowLayering) {
549 :
550 : // This is a construction with a between-glass shade or blind
551 :
552 0 : if (TotGlassLayers >= 4) {
553 : // Quadruple pane not allowed.
554 0 : WrongWindowLayering = true;
555 0 : } else if (TotGlassLayers == 2 || TotGlassLayers == 3) {
556 0 : bool ValidBGShadeBlindConst = false;
557 0 : if (TotGlassLayers == 2) {
558 0 : if (TotLayers != 5) {
559 0 : WrongWindowLayering = true;
560 : } else {
561 0 : if (matOutside->group == Material::Group::Glass &&
562 0 : (s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::Gas ||
563 0 : s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::GasMixture) &&
564 0 : ((s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Shade ||
565 0 : s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Blind) &&
566 0 : s_mat->materials(thisConstruct.LayerPoint(3))->group != Material::Group::Screen) &&
567 0 : (s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::Gas ||
568 0 : s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::GasMixture) &&
569 0 : s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Glass)
570 0 : ValidBGShadeBlindConst = true;
571 : }
572 : } else { // TotGlassLayers = 3
573 0 : if (TotLayers != 7) {
574 0 : WrongWindowLayering = true;
575 : } else {
576 0 : if (matOutside->group == Material::Group::Glass &&
577 0 : (s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::Gas ||
578 0 : s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::GasMixture) &&
579 0 : s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Glass &&
580 0 : (s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::Gas ||
581 0 : s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::GasMixture) &&
582 0 : ((s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Shade ||
583 0 : s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Blind) &&
584 0 : s_mat->materials(thisConstruct.LayerPoint(5))->group != Material::Group::Screen) &&
585 0 : (s_mat->materials(thisConstruct.LayerPoint(6))->group == Material::Group::Gas ||
586 0 : s_mat->materials(thisConstruct.LayerPoint(6))->group == Material::Group::GasMixture) &&
587 0 : s_mat->materials(thisConstruct.LayerPoint(7))->group == Material::Group::Glass)
588 0 : ValidBGShadeBlindConst = true;
589 : }
590 : } // End of check if TotGlassLayers = 2 or 3
591 0 : if (!ValidBGShadeBlindConst) WrongWindowLayering = true;
592 0 : if (!WrongWindowLayering) {
593 0 : int const LayNumSh = 2 * TotGlassLayers - 1;
594 0 : int const MatSh = thisConstruct.LayerPoint(LayNumSh);
595 0 : auto const *matSh = s_mat->materials(MatSh);
596 : // For double pane, shade/blind must be layer #3.
597 : // For triple pane, it must be layer #5 (i.e., between two inner panes).
598 0 : if (matSh->group != Material::Group::Shade && matSh->group != Material::Group::Blind) WrongWindowLayering = true;
599 0 : if (TotLayers != 2 * TotGlassLayers + 1) WrongWindowLayering = true;
600 0 : if (!WrongWindowLayering) {
601 : // Gas on either side of a between-glass shade/blind must be the same
602 0 : int const MatGapL = thisConstruct.LayerPoint(LayNumSh - 1);
603 0 : int const MatGapR = thisConstruct.LayerPoint(LayNumSh + 1);
604 0 : auto const *matGapL = dynamic_cast<const Material::MaterialGasMix *>(s_mat->materials(MatGapL));
605 0 : auto const *matGapR = dynamic_cast<const Material::MaterialGasMix *>(s_mat->materials(MatGapR));
606 0 : for (int IGas = 0; IGas < Material::maxMixGases; ++IGas) {
607 0 : if ((matGapL->gases[IGas].type != matGapR->gases[IGas].type) || (matGapL->gasFracts[IGas] != matGapR->gasFracts[IGas]))
608 0 : WrongWindowLayering = true;
609 : }
610 : // Gap width on either side of a between-glass shade/blind must be the same
611 0 : if (std::abs(matGapL->Thickness - matGapR->Thickness) > 0.0005) WrongWindowLayering = true;
612 0 : if (matSh->group == Material::Group::Blind) {
613 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(matSh);
614 0 : assert(matBlind != nullptr);
615 0 : if ((matGapL->Thickness + matGapR->Thickness) < matBlind->SlatWidth) {
616 0 : ErrorsFound = true;
617 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: For window construction {}", thisConstruct.Name));
618 0 : ShowContinueError(state, "the slat width of the between-glass blind is greater than");
619 0 : ShowContinueError(state, "the sum of the widths of the gas layers adjacent to the blind.");
620 : }
621 : } // End of check if material is window blind
622 : } // End of check if WrongWindowLayering
623 : } // End of check if WrongWindowLayering
624 : } // End of check on total glass layers
625 : } // End of check if construction has between-glass shade/blind
626 :
627 : // Check Simple Windows,
628 122 : if (s_mat->materials(thisConstruct.LayerPoint(1))->group == Material::Group::GlassSimple) {
629 23 : if (TotLayers > 1) {
630 : // check that none of the other layers are glazing or gas
631 3 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
632 2 : int const MaterNum = thisConstruct.LayerPoint(Layer);
633 2 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
634 2 : auto const *mat = s_mat->materials(MaterNum);
635 2 : if (mat->group == Material::Group::Glass) {
636 0 : ErrorsFound = true;
637 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
638 0 : ShowContinueError(state, "For simple window constructions, no other glazing layers are allowed.");
639 : }
640 2 : if (mat->group == Material::Group::Gas) {
641 0 : ErrorsFound = true;
642 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
643 0 : ShowContinueError(state, "For simple window constructions, no other gas layers are allowed.");
644 : }
645 : }
646 : }
647 : }
648 :
649 122 : if (WrongWindowLayering) {
650 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
651 0 : ShowContinueError(state, " For multi-layer window constructions the following rules apply:");
652 0 : ShowContinueError(state, " --The first and last layer must be a solid layer (glass or shade/screen/blind),");
653 0 : ShowContinueError(state, " --Adjacent glass layers must be separated by one and only one gas layer,");
654 0 : ShowContinueError(state, " --Adjacent layers must not be of the same type,");
655 0 : ShowContinueError(state, " --Only one shade/screen/blind layer is allowed,");
656 0 : ShowContinueError(state, " --An exterior shade/screen/blind must be the first layer,");
657 0 : ShowContinueError(state, " --An interior shade/blind must be the last layer,");
658 0 : ShowContinueError(state, " --An interior screen is not allowed,");
659 0 : ShowContinueError(state, " --For an exterior shade/screen/blind or interior shade/blind, there should not be a gas layer");
660 0 : ShowContinueError(state, " ----between the shade/screen/blind and adjacent glass,");
661 0 : ShowContinueError(state, " --A between-glass screen is not allowed,");
662 0 : ShowContinueError(state, " --A between-glass shade/blind is allowed only for double and triple glazing,");
663 0 : ShowContinueError(state, " --A between-glass shade/blind must have adjacent gas layers of the same type and width,");
664 0 : ShowContinueError(state, " --For triple glazing the between-glass shade/blind must be between the two inner glass layers,");
665 0 : ShowContinueError(state, " --The slat width of a between-glass blind must be less than the sum of the widths");
666 0 : ShowContinueError(state, " ----of the gas layers adjacent to the blind.");
667 0 : ErrorsFound = true;
668 : }
669 :
670 122 : thisConstruct.TotGlassLayers = TotGlassLayers;
671 122 : thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
672 :
673 : // In following, InsideLayer is layer number of inside glass and InsideAbsorpThermal applies
674 : // only to inside glass; it is corrected later in InitGlassOpticalCalculations
675 : // if construction has inside shade or blind.
676 122 : if (matInside->group == Material::Group::Shade || matInside->group == Material::Group::Blind) {
677 4 : --InsideLayer;
678 : }
679 122 : if (InsideLayer > 0) {
680 122 : InsideMaterNum = thisConstruct.LayerPoint(InsideLayer);
681 122 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
682 : }
683 122 : if (InsideMaterNum != 0) {
684 122 : auto const *thisInsideMaterial = s_mat->materials(InsideMaterNum);
685 122 : thisConstruct.InsideAbsorpVis = thisInsideMaterial->AbsorpVisible;
686 122 : thisConstruct.InsideAbsorpSolar = thisInsideMaterial->AbsorpSolar;
687 : }
688 :
689 122 : if ((matOutside->group == Material::Group::Glass) || (matOutside->group == Material::Group::GlassSimple)) { // Glass
690 122 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
691 : } else { // Exterior shade, blind or screen
692 0 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermal;
693 : }
694 :
695 : } else { // Opaque surface
696 653 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermal;
697 653 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermal;
698 : }
699 :
700 775 : thisConstruct.OutsideRoughness = matOutside->Roughness;
701 :
702 775 : if (matOutside->group == Material::Group::AirGap) {
703 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Outside Layer is Air for construction {}", thisConstruct.Name));
704 0 : ShowContinueError(state, format(" Error in material {}", matOutside->Name));
705 0 : ErrorsFound = true;
706 : }
707 775 : if (InsideLayer > 0) {
708 775 : if (matInside->group == Material::Group::AirGap) {
709 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Inside Layer is Air for construction {}", thisConstruct.Name));
710 0 : ShowContinueError(state, format(" Error in material {}", matInside->Name));
711 0 : ErrorsFound = true;
712 : }
713 : }
714 :
715 775 : if (matOutside->group == Material::Group::EcoRoof) {
716 1 : thisConstruct.TypeIsEcoRoof = true;
717 : // need to check EcoRoof is not non-outside layer
718 3 : for (int Layer = 2; Layer <= TotLayers; ++Layer) {
719 2 : if (s_mat->materials(thisConstruct.LayerPoint(Layer))->group == Material::Group::EcoRoof) {
720 0 : ShowSevereError(state,
721 0 : format("CheckAndSetConstructionProperties: Interior Layer is EcoRoof for construction {}", thisConstruct.Name));
722 0 : ShowContinueError(state, format(" Error in material {}", s_mat->materials(thisConstruct.LayerPoint(Layer))->Name));
723 0 : ErrorsFound = true;
724 : }
725 : }
726 : }
727 :
728 775 : if (matOutside->group == Material::Group::IRTransparent) {
729 0 : thisConstruct.TypeIsIRT = true;
730 0 : if (thisConstruct.TotLayers != 1) {
731 0 : ShowSevereError(
732 : state,
733 0 : format("CheckAndSetConstructionProperties: Infrared Transparent (IRT) Construction is limited to 1 layer {}", thisConstruct.Name));
734 0 : ShowContinueError(state, " Too many layers in referenced construction.");
735 0 : ErrorsFound = true;
736 : }
737 : }
738 : }
739 :
740 17 : int AssignReverseConstructionNumber(EnergyPlusData &state,
741 : int const ConstrNum, // Existing Construction number of first surface
742 : bool &ErrorsFound)
743 : {
744 :
745 : // FUNCTION INFORMATION:
746 : // AUTHOR Linda Lawrie
747 : // DATE WRITTEN December 2006
748 :
749 : // PURPOSE OF THIS FUNCTION:
750 : // For interzone, unentered surfaces, we need to have "reverse" constructions
751 : // assigned to the created surfaces. These need to be the reverse (outside to inside layer)
752 : // of existing surfaces. Plus, there may be one already in the data structure so this is looked for as well.
753 :
754 : // METHODOLOGY EMPLOYED:
755 : // Create reverse layers. Look in current constructions to see if match. If no match, create a new one.
756 :
757 17 : auto &s_mat = state.dataMaterial;
758 : // Return value
759 : int NewConstrNum; // Reverse Construction Number
760 :
761 17 : if (ConstrNum == 0) {
762 : // error caught elsewhere
763 0 : NewConstrNum = 0;
764 0 : return NewConstrNum;
765 : }
766 :
767 17 : auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
768 17 : thisConstruct.IsUsed = true;
769 17 : int nLayer = 0;
770 17 : state.dataConstruction->LayerPoint = 0;
771 47 : for (int Loop = thisConstruct.TotLayers; Loop >= 1; --Loop) {
772 30 : ++nLayer;
773 30 : state.dataConstruction->LayerPoint(nLayer) = thisConstruct.LayerPoint(Loop);
774 : }
775 :
776 : // now, got thru and see if there is a match already....
777 17 : NewConstrNum = 0;
778 77 : for (int Loop = 1; Loop <= state.dataHeatBal->TotConstructs; ++Loop) {
779 73 : bool Found = true;
780 218 : for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
781 205 : if (state.dataConstruction->Construct(Loop).LayerPoint(nLayer) != state.dataConstruction->LayerPoint(nLayer)) {
782 60 : Found = false;
783 60 : break;
784 : }
785 : }
786 73 : if (Found) {
787 13 : NewConstrNum = Loop;
788 13 : state.dataConstruction->Construct(Loop).IsUsed = true;
789 13 : break;
790 : }
791 : }
792 :
793 : // if need new one, bunch o stuff
794 17 : if (NewConstrNum == 0) {
795 4 : ++state.dataHeatBal->TotConstructs;
796 4 : state.dataConstruction->Construct.redimension(state.dataHeatBal->TotConstructs);
797 4 : state.dataHeatBal->NominalRforNominalUCalculation.redimension(state.dataHeatBal->TotConstructs);
798 4 : state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) = 0.0;
799 4 : state.dataHeatBal->NominalU.redimension(state.dataHeatBal->TotConstructs);
800 4 : state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) = 0.0;
801 4 : state.dataHeatBal->NominalUBeforeAdjusted.redimension(state.dataHeatBal->TotConstructs);
802 4 : state.dataHeatBal->NominalUBeforeAdjusted(state.dataHeatBal->TotConstructs) = 0.0;
803 4 : state.dataHeatBal->CoeffAdjRatio.redimension(state.dataHeatBal->TotConstructs) = 1.0;
804 : // Put in new attributes
805 4 : NewConstrNum = state.dataHeatBal->TotConstructs;
806 4 : state.dataConstruction->Construct(NewConstrNum).IsUsed = true;
807 4 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs) =
808 8 : state.dataConstruction->Construct(ConstrNum); // preserve some of the attributes.
809 : // replace others...
810 4 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).Name = "iz-" + state.dataConstruction->Construct(ConstrNum).Name;
811 4 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
812 48 : for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
813 44 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).LayerPoint(nLayer) = state.dataConstruction->LayerPoint(nLayer);
814 44 : if (state.dataConstruction->LayerPoint(nLayer) != 0) {
815 9 : state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) +=
816 9 : s_mat->materials(state.dataConstruction->LayerPoint(nLayer))->NominalR;
817 : }
818 : }
819 :
820 : // no error if zero -- that will have been caught with earlier construction
821 : // the following line was changed to fix CR7601
822 4 : if (state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) != 0.0) {
823 4 : state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) =
824 4 : 1.0 / state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs);
825 : }
826 :
827 4 : CheckAndSetConstructionProperties(state, state.dataHeatBal->TotConstructs, ErrorsFound);
828 : }
829 :
830 17 : return NewConstrNum;
831 : }
832 :
833 1639 : Real64 ComputeNominalUwithConvCoeffs(EnergyPlusData &state,
834 : int const numSurf, // index for Surface array.
835 : bool &isValid // returns true if result is valid
836 : )
837 : {
838 :
839 : // SUBROUTINE INFORMATION:
840 : // AUTHOR Jason Glazer
841 : // DATE WRITTEN September 2013
842 : // MODIFIED na
843 : // RE-ENGINEERED na
844 :
845 : // PURPOSE OF THIS SUBROUTINE:
846 : // Calculate Nominal U-value with convection/film coefficients for reporting by
847 : // adding on prescribed R-values for interior and exterior convection coefficients
848 : // as found in ASHRAE 90.1-2004, Appendix A. Used in EIO and tabular reports.
849 : // ASHRAE 90.1-2004 Section A9.4.1 shows the following:
850 : // R-value Condition
851 : // All exterior conditions IP: 0.17 SI: 0.0299
852 : // All semi-exterior surfaces IP: 0.46 SI: 0.0810
853 : // Interior horizontal surfaces, heat flow up IP: 0.61 SI: 0.1074
854 : // Interior horizontal surfaces, heat flow down IP: 0.92 SI: 0.1620
855 : // Interior vertical surfaces IP: 0.68 SI: 0.1198
856 : // This section shows the same value in 90.1-2010 and 90.2-2010
857 : // Note that this report does not use the semi-exterior surface value because
858 : // EnergyPlus does not have a way to specifically tell whether or not a surface
859 : // is connected to a semi-exterior area of the building. Users can always use
860 : // the Nominal U-Value to manually calculated this. The values calculated here
861 : // are simply reported to the EIO file and not used for any calculations.
862 :
863 : // Return value
864 : Real64 NominalUwithConvCoeffs; // return value
865 :
866 : static constexpr std::array<Real64, static_cast<int>(DataSurfaces::SurfaceClass::Num)> filmCoefs = {
867 : 0.0, // None
868 : 0.1197548, // Wall
869 : 0.1620212, // Floor
870 : 0.1074271, // Roof
871 : 0.0, // IntMass
872 : 0.0, // Detached_B
873 : 0.0, // Detached_F
874 : 0.1197548, // Window
875 : 0.1197548, // GlassDoor
876 : 0.1197548, // Door
877 : 0.0, // Shading
878 : 0.0, // Overhang
879 : 0.0, // Fin
880 : 0.0, // TDD_Dome
881 : 0.0 // TDD_Diffuser
882 : }; // If anything added to the enum SurfaceClass, adjust this list appropriately
883 :
884 : Real64 insideFilm;
885 : Real64 outsideFilm;
886 :
887 1639 : isValid = true;
888 :
889 1639 : auto &thisSurface = state.dataSurface->Surface(numSurf);
890 :
891 : // exterior conditions
892 1639 : switch (thisSurface.ExtBoundCond) {
893 1093 : case DataSurfaces::ExternalEnvironment: { // ExtBoundCond = 0
894 1093 : outsideFilm = 0.0299387; // All exterior conditions
895 1093 : } break;
896 1 : case DataSurfaces::OtherSideCoefCalcExt: {
897 1 : outsideFilm = state.dataSurface->OSC(thisSurface.OSCPtr).SurfFilmCoef;
898 1 : } break;
899 112 : case DataSurfaces::Ground:
900 : case DataSurfaces::OtherSideCoefNoCalcExt:
901 : case DataSurfaces::OtherSideCondModeledExt:
902 : case DataSurfaces::GroundFCfactorMethod:
903 : case DataSurfaces::KivaFoundation: { // All these cases have a negative ExtBoundCond so don't use film coefficients
904 112 : outsideFilm = 0.0;
905 112 : } break;
906 433 : default: { // Interior Surface Attached to a Zone (ExtBoundCond is a surface)
907 433 : outsideFilm = filmCoefs[static_cast<int>(state.dataSurface->Surface(thisSurface.ExtBoundCond).Class)];
908 433 : } break;
909 : }
910 : // interior conditions and calculate the return value
911 1639 : if (state.dataHeatBal->NominalU(thisSurface.Construction) > 0.0) {
912 1596 : insideFilm = filmCoefs[static_cast<int>(thisSurface.Class)];
913 1596 : if (insideFilm == 0.0) outsideFilm = 0.0;
914 1596 : NominalUwithConvCoeffs =
915 1596 : 1.0 / (insideFilm + (1.0 / state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction)) + outsideFilm);
916 : } else {
917 43 : isValid = false;
918 43 : NominalUwithConvCoeffs = state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction);
919 : }
920 :
921 1639 : return NominalUwithConvCoeffs;
922 : }
923 :
924 194 : void SetFlagForWindowConstructionWithShadeOrBlindLayer(EnergyPlusData &state)
925 : {
926 :
927 : // PURPOSE OF THIS SUBROUTINE:
928 : // check fenestrations with shading control and set a flag to true if its construction has
929 : // either shade or blind material layer
930 :
931 : // METHODOLOGY EMPLOYED:
932 : // Loop through Surface and register any shading controls, and loop through the construction
933 : // material layer
934 :
935 : // Using/Aliasing
936 : using DataSurfaces::ExternalEnvironment;
937 :
938 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
939 194 : int loopSurfNum(0); // surface index
940 194 : int ConstrNum(0); // construction index
941 194 : int NumLayers(0); // number of material layers in a construction
942 194 : int Layer(0); // construction material layer index
943 194 : int MaterNum(0); // construction material index
944 :
945 194 : auto &s_mat = state.dataMaterial;
946 :
947 1899 : for (loopSurfNum = 1; loopSurfNum <= state.dataSurface->TotSurfaces; ++loopSurfNum) {
948 :
949 1705 : if (state.dataSurface->Surface(loopSurfNum).Class != DataSurfaces::SurfaceClass::Window) continue;
950 122 : if (state.dataSurface->Surface(loopSurfNum).ExtBoundCond != ExternalEnvironment) continue;
951 114 : if (!state.dataSurface->Surface(loopSurfNum).HasShadeControl) continue;
952 12 : if (state.dataSurface->Surface(loopSurfNum).activeShadedConstruction == 0) continue;
953 :
954 12 : ConstrNum = state.dataSurface->Surface(loopSurfNum).activeShadedConstruction;
955 12 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
956 12 : if (thisConstruct.TypeIsWindow) {
957 12 : NumLayers = thisConstruct.TotLayers;
958 41 : for (Layer = 1; Layer <= NumLayers; ++Layer) {
959 29 : MaterNum = thisConstruct.LayerPoint(Layer);
960 29 : if (MaterNum == 0) continue;
961 29 : auto const *mat = s_mat->materials(MaterNum);
962 29 : if (mat->group == Material::Group::Shade || mat->group == Material::Group::Blind)
963 10 : state.dataSurface->SurfWinHasShadeOrBlindLayer(loopSurfNum) = true;
964 : }
965 : }
966 : }
967 194 : }
968 :
969 209 : void AllocateIntGains(EnergyPlusData &state)
970 : {
971 209 : state.dataHeatBal->ZoneIntGain.allocate(state.dataGlobal->NumOfZones);
972 209 : state.dataHeatBal->spaceIntGain.allocate(state.dataGlobal->numSpaces);
973 209 : state.dataHeatBal->spaceIntGainDevices.allocate(state.dataGlobal->numSpaces);
974 209 : state.dataDayltg->spacePowerReductionFactor.dimension(state.dataGlobal->numSpaces, 1.0);
975 209 : }
976 :
977 : } // namespace EnergyPlus::DataHeatBalance
|