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 971135 : Real64 SpaceData::sumHATsurf(EnergyPlusData &state)
102 : {
103 : // PURPOSE OF THIS FUNCTION:
104 : // This function calculates the space sum of Hc*Area*Tsurf.
105 :
106 971135 : Real64 sumHATsurf = 0.0;
107 :
108 8013751 : for (int surfNum = this->HTSurfaceFirst; surfNum <= this->HTSurfaceLast; ++surfNum) {
109 7042616 : Real64 Area = state.dataSurface->Surface(surfNum).Area;
110 :
111 7042616 : if (state.dataSurface->Surface(surfNum).Class == DataSurfaces::SurfaceClass::Window) {
112 546925 : 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 546925 : 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 7042616 : sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(surfNum);
131 : }
132 :
133 971135 : return sumHATsurf;
134 : }
135 :
136 971135 : Real64 ZoneData::sumHATsurf(EnergyPlusData &state)
137 : {
138 971135 : Real64 sumHATsurf = 0.0;
139 1942270 : for (int spaceNum : this->spaceIndexes) {
140 971135 : sumHATsurf += state.dataHeatBal->space(spaceNum).sumHATsurf(state);
141 971135 : }
142 971135 : return sumHATsurf;
143 : }
144 20539436 : 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 20539436 : 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 20539436 : Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
161 20539436 : Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
162 :
163 20539436 : Real64 const Z(Centroid.z); // Centroid value
164 20539436 : if (Z <= 0.0) {
165 212881 : OutDryBulbTemp = BaseDryTemp;
166 212881 : OutWetBulbTemp = BaseWetTemp;
167 : } else {
168 20326555 : OutDryBulbTemp = BaseDryTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
169 20326555 : OutWetBulbTemp = BaseWetTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
170 : }
171 : }
172 20539436 : }
173 :
174 20534235 : 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 20534235 : if (state.dataEnvrn->SiteWindExp == 0.0) {
186 0 : WindSpeed = state.dataEnvrn->WindSpeed;
187 : } else {
188 20534235 : Real64 const Z(Centroid.z); // Centroid value
189 20534235 : if (Z <= 0.0) {
190 212826 : 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 20321409 : WindSpeed = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
196 : }
197 : }
198 20534235 : }
199 :
200 20534235 : void ZoneData::SetWindDirAt(Real64 const fac)
201 : {
202 20534235 : WindDir = fac;
203 20534235 : }
204 :
205 5217 : void AirReportVars::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string const &name)
206 : {
207 15651 : SetupOutputVariable(state,
208 10434 : format("{} Mean Air Temperature", prefix),
209 : Constant::Units::C,
210 5217 : this->MeanAirTemp,
211 : OutputProcessor::TimeStepType::Zone,
212 : OutputProcessor::StoreType::Average,
213 : name);
214 15651 : SetupOutputVariable(state,
215 10434 : format("{} Wetbulb Globe Temperature", prefix),
216 : Constant::Units::C,
217 5217 : this->WetbulbGlobeTemp,
218 : OutputProcessor::TimeStepType::Zone,
219 : OutputProcessor::StoreType::Average,
220 : name);
221 15651 : SetupOutputVariable(state,
222 10434 : format("{} Operative Temperature", prefix),
223 : Constant::Units::C,
224 5217 : this->OperativeTemp,
225 : OutputProcessor::TimeStepType::Zone,
226 : OutputProcessor::StoreType::Average,
227 : name);
228 15651 : SetupOutputVariable(state,
229 10434 : format("{} Mean Air Dewpoint Temperature", prefix),
230 : Constant::Units::C,
231 5217 : this->MeanAirDewPointTemp,
232 : OutputProcessor::TimeStepType::Zone,
233 : OutputProcessor::StoreType::Average,
234 : name);
235 15651 : SetupOutputVariable(state,
236 10434 : format("{} Mean Air Humidity Ratio", prefix),
237 : Constant::Units::kgWater_kgDryAir,
238 5217 : this->MeanAirHumRat,
239 : OutputProcessor::TimeStepType::Zone,
240 : OutputProcessor::StoreType::Average,
241 : name);
242 15651 : SetupOutputVariable(state,
243 10434 : format("{} Air Heat Balance Internal Convective Heat Gain Rate", prefix),
244 : Constant::Units::W,
245 5217 : this->SumIntGains,
246 : OutputProcessor::TimeStepType::System,
247 : OutputProcessor::StoreType::Average,
248 : name);
249 15651 : SetupOutputVariable(state,
250 10434 : format("{} Air Heat Balance Surface Convection Rate", prefix),
251 : Constant::Units::W,
252 5217 : this->SumHADTsurfs,
253 : OutputProcessor::TimeStepType::System,
254 : OutputProcessor::StoreType::Average,
255 : name);
256 15651 : SetupOutputVariable(state,
257 10434 : format("{} Air Heat Balance Interzone Air Transfer Rate", prefix),
258 : Constant::Units::W,
259 5217 : this->SumMCpDTzones,
260 : OutputProcessor::TimeStepType::System,
261 : OutputProcessor::StoreType::Average,
262 : name);
263 15651 : SetupOutputVariable(state,
264 10434 : format("{} Air Heat Balance Outdoor Air Transfer Rate", prefix),
265 : Constant::Units::W,
266 5217 : this->SumMCpDtInfil,
267 : OutputProcessor::TimeStepType::System,
268 : OutputProcessor::StoreType::Average,
269 : name);
270 15651 : SetupOutputVariable(state,
271 10434 : format("{} Air Heat Balance System Air Transfer Rate", prefix),
272 : Constant::Units::W,
273 5217 : this->SumMCpDTsystem,
274 : OutputProcessor::TimeStepType::System,
275 : OutputProcessor::StoreType::Average,
276 : name);
277 15651 : SetupOutputVariable(state,
278 10434 : format("{} Air Heat Balance System Convective Heat Gain Rate", prefix),
279 : Constant::Units::W,
280 5217 : this->SumNonAirSystem,
281 : OutputProcessor::TimeStepType::System,
282 : OutputProcessor::StoreType::Average,
283 : name);
284 15651 : SetupOutputVariable(state,
285 10434 : format("{} Air Heat Balance Air Energy Storage Rate", prefix),
286 : Constant::Units::W,
287 5217 : this->CzdTdt,
288 : OutputProcessor::TimeStepType::System,
289 : OutputProcessor::StoreType::Average,
290 : name);
291 5217 : if (state.dataGlobal->DisplayAdvancedReportVariables) {
292 180 : SetupOutputVariable(state,
293 120 : format("{} Air Heat Balance Deviation Rate", prefix),
294 : Constant::Units::W,
295 60 : this->imBalance,
296 : OutputProcessor::TimeStepType::System,
297 : OutputProcessor::StoreType::Average,
298 : name);
299 : }
300 5217 : }
301 :
302 2829013 : void SetZoneOutBulbTempAt(EnergyPlusData &state)
303 : {
304 23368449 : for (auto &zone : state.dataHeatBal->Zone) {
305 20539436 : zone.SetOutBulbTempAt(state);
306 2829013 : }
307 2829013 : }
308 :
309 2829013 : void CheckZoneOutBulbTempAt(EnergyPlusData &state)
310 : {
311 : // Using/Aliasing
312 : using DataEnvironment::SetOutBulbTempAt_error;
313 :
314 2829013 : Real64 minBulb = 0.0;
315 23368449 : for (auto &zone : state.dataHeatBal->Zone) {
316 20539436 : minBulb = min(minBulb, zone.OutDryBulbTemp, zone.OutWetBulbTemp);
317 20539436 : if (minBulb < -100.0) {
318 0 : SetOutBulbTempAt_error(state, "Zone", zone.Centroid.z, zone.Name);
319 : }
320 2829013 : }
321 2829013 : }
322 :
323 2828212 : void SetZoneWindSpeedAt(EnergyPlusData &state)
324 : {
325 2828212 : Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
326 2828212 : std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
327 23362447 : for (auto &zone : state.dataHeatBal->Zone) {
328 20534235 : zone.SetWindSpeedAt(state, fac);
329 2828212 : }
330 2828212 : }
331 :
332 2828212 : void SetZoneWindDirAt(EnergyPlusData &state)
333 : {
334 : // Using/Aliasing
335 2828212 : Real64 const fac(state.dataEnvrn->WindDir);
336 23362447 : for (auto &zone : state.dataHeatBal->Zone) {
337 20534235 : zone.SetWindDirAt(fac);
338 2828212 : }
339 2828212 : }
340 :
341 6062 : void CheckAndSetConstructionProperties(EnergyPlusData &state,
342 : int const ConstrNum, // Construction number to be set/checked
343 : bool &ErrorsFound // error flag that is set when certain errors have occurred
344 : )
345 : {
346 :
347 : // SUBROUTINE INFORMATION:
348 : // AUTHOR Linda Lawrie
349 : // DATE WRITTEN December 2006
350 :
351 : // This routine checks some properties of entered constructions; sets some properties; and sets
352 : // an error flag for certain error conditions.
353 6062 : auto &s_mat = state.dataMaterial;
354 :
355 6062 : auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
356 6062 : int TotLayers = thisConstruct.TotLayers; // Number of layers in a construction
357 6062 : if (TotLayers == 0) {
358 0 : return; // error condition, hopefully caught elsewhere
359 : }
360 6062 : int InsideLayer = TotLayers; // Inside Layer of Construct; for window construct, layer no. of inside glass
361 6062 : if (thisConstruct.LayerPoint(InsideLayer) <= 0) {
362 0 : return; // Error condition
363 : }
364 :
365 : // window screen is not allowed on inside layer
366 :
367 6062 : thisConstruct.DayltPropPtr = 0;
368 6062 : int InsideMaterNum = thisConstruct.LayerPoint(InsideLayer); // Material "number" of the Inside layer
369 6062 : if (InsideMaterNum != 0) {
370 6062 : auto const *mat = s_mat->materials(InsideMaterNum);
371 6062 : thisConstruct.InsideAbsorpVis = mat->AbsorpVisible;
372 6062 : thisConstruct.InsideAbsorpSolar = mat->AbsorpSolar;
373 :
374 : // Following line applies only to opaque surfaces; it is recalculated later for windows.
375 6062 : thisConstruct.ReflectVisDiffBack = 1.0 - mat->AbsorpVisible;
376 : }
377 :
378 6062 : int OutsideMaterNum = thisConstruct.LayerPoint(1); // Material "number" of the Outside layer
379 6062 : if (OutsideMaterNum != 0) {
380 6062 : auto const *mat = s_mat->materials(OutsideMaterNum);
381 6062 : thisConstruct.OutsideAbsorpVis = mat->AbsorpVisible;
382 6062 : thisConstruct.OutsideAbsorpSolar = mat->AbsorpSolar;
383 : }
384 :
385 6062 : thisConstruct.TotSolidLayers = 0;
386 6062 : thisConstruct.TotGlassLayers = 0;
387 6062 : thisConstruct.AbsDiffShade = 0.0;
388 :
389 : // Check if any layer is glass, gas, shade, screen or blind; if so it is considered a window construction for
390 : // purposes of error checking.
391 :
392 6062 : thisConstruct.TypeIsWindow = false;
393 20279 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
394 14217 : int const MaterNum = thisConstruct.LayerPoint(Layer);
395 14217 : if (MaterNum == 0) {
396 0 : continue; // error -- has been caught will stop program later
397 : }
398 14217 : auto const *mat = s_mat->materials(MaterNum);
399 14217 : thisConstruct.TypeIsWindow =
400 12368 : (mat->group == Material::Group::Glass || mat->group == Material::Group::Gas || mat->group == Material::Group::GasMixture ||
401 11742 : mat->group == Material::Group::Shade || mat->group == Material::Group::Blind || mat->group == Material::Group::Screen ||
402 11710 : mat->group == Material::Group::GlassSimple || mat->group == Material::Group::ComplexShade ||
403 11569 : mat->group == Material::Group::ComplexWindowGap || mat->group == Material::Group::GlassEQL || mat->group == Material::Group::ShadeEQL ||
404 38119 : mat->group == Material::Group::DrapeEQL || mat->group == Material::Group::ScreenEQL || mat->group == Material::Group::BlindEQL ||
405 11534 : mat->group == Material::Group::WindowGapEQL);
406 14217 : bool TypeIsNotWindow =
407 14217 : (mat->group == Material::Group::Invalid || mat->group == Material::Group::AirGap || mat->group == Material::Group::Regular ||
408 28434 : mat->group == Material::Group::EcoRoof || mat->group == Material::Group::IRTransparent);
409 14217 : if (!thisConstruct.TypeIsWindow && !TypeIsNotWindow) {
410 0 : assert(false);
411 : }
412 : }
413 :
414 6062 : if (InsideMaterNum == 0) {
415 0 : return;
416 : }
417 6062 : auto const *matInside = s_mat->materials(InsideMaterNum);
418 6062 : if (OutsideMaterNum == 0) {
419 0 : return;
420 : }
421 6062 : auto const *matOutside = s_mat->materials(OutsideMaterNum);
422 :
423 6062 : if (thisConstruct.TypeIsWindow) {
424 :
425 1348 : bool WrongMaterialsMix = false;
426 1348 : thisConstruct.NumCTFTerms = 0;
427 1348 : thisConstruct.NumHistories = 0;
428 4039 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
429 2691 : int const MaterNum = thisConstruct.LayerPoint(Layer);
430 2691 : if (MaterNum == 0) {
431 0 : continue; // error -- has been caught will stop program later
432 : }
433 2691 : auto const *mat = s_mat->materials(MaterNum);
434 2691 : WrongMaterialsMix =
435 2907 : !((mat->group == Material::Group::Glass) || (mat->group == Material::Group::Gas) || (mat->group == Material::Group::GasMixture) ||
436 216 : (mat->group == Material::Group::Shade) || (mat->group == Material::Group::Blind) || (mat->group == Material::Group::Screen) ||
437 184 : (mat->group == Material::Group::GlassSimple) || (mat->group == Material::Group::ComplexShade) ||
438 43 : (mat->group == Material::Group::ComplexWindowGap) || (mat->group == Material::Group::GlassEQL) ||
439 11 : (mat->group == Material::Group::ShadeEQL) || (mat->group == Material::Group::DrapeEQL) ||
440 9 : (mat->group == Material::Group::ScreenEQL) || (mat->group == Material::Group::BlindEQL) ||
441 8 : (mat->group == Material::Group::WindowGapEQL));
442 : }
443 :
444 1348 : if (WrongMaterialsMix) { // Illegal material for a window construction
445 0 : ShowSevereError(state,
446 0 : format("Error: Window construction={} has materials other than glass, gas, shade, screen, blind, complex shading, "
447 : "complex gap, or simple system.",
448 0 : thisConstruct.Name));
449 0 : ErrorsFound = true;
450 : // Do not check number of layers for BSDF type of window since that can be handled
451 1348 : } else if ((TotLayers > 8) && (!thisConstruct.WindowTypeBSDF) &&
452 1 : (!thisConstruct.WindowTypeEQL)) { // Too many layers for a window construction
453 0 : ShowSevereError(state,
454 0 : format("CheckAndSetConstructionProperties: Window construction={} has too many layers (max of 8 allowed -- 4 glass + 3 "
455 : "gap + 1 shading device).",
456 0 : thisConstruct.Name));
457 0 : ErrorsFound = true;
458 :
459 1348 : } else if (TotLayers == 1) {
460 750 : auto const *mat = s_mat->materials(thisConstruct.LayerPoint(1));
461 750 : Material::Group matGroup = mat->group;
462 750 : if ((matGroup == Material::Group::Shade) || (matGroup == Material::Group::Gas) || (matGroup == Material::Group::GasMixture) ||
463 750 : (matGroup == Material::Group::Blind) || (matGroup == Material::Group::Screen) || (matGroup == Material::Group::ComplexShade) ||
464 : (matGroup == Material::Group::ComplexWindowGap)) {
465 0 : ShowSevereError(state,
466 0 : format("CheckAndSetConstructionProperties: The single-layer window construction={} has a gas, complex gap, shade, "
467 : "complex shade, screen or blind material; it should be glass of simple glazing system.",
468 0 : thisConstruct.Name));
469 0 : ErrorsFound = true;
470 : }
471 : }
472 :
473 : // Find total glass layers, total shade/blind layers and total gas layers in a window construction
474 :
475 1348 : bool WrongWindowLayering = false;
476 1348 : int TotGlassLayers = 0;
477 1348 : int TotShadeLayers = 0; // Includes shades, blinds, and screens
478 1348 : int TotGasLayers = 0;
479 4039 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
480 2691 : int const MaterNum = thisConstruct.LayerPoint(Layer);
481 2691 : if (MaterNum == 0) {
482 0 : continue; // error -- has been caught will stop program later
483 : }
484 2691 : auto const *mat = s_mat->materials(MaterNum);
485 2691 : if (mat->group == Material::Group::Glass) {
486 1849 : ++TotGlassLayers;
487 : }
488 2691 : if (mat->group == Material::Group::GlassSimple) {
489 132 : ++TotGlassLayers;
490 : }
491 2691 : if (mat->group == Material::Group::Shade || mat->group == Material::Group::Blind || mat->group == Material::Group::Screen ||
492 2659 : mat->group == Material::Group::ComplexShade) {
493 41 : ++TotShadeLayers;
494 : }
495 2691 : if (mat->group == Material::Group::Gas || mat->group == Material::Group::GasMixture || mat->group == Material::Group::ComplexWindowGap) {
496 650 : ++TotGasLayers;
497 : }
498 2691 : if (Layer < TotLayers) {
499 1343 : int const MaterNumNext = thisConstruct.LayerPoint(Layer + 1);
500 : // Adjacent layers of same type not allowed
501 1343 : if (MaterNumNext == 0) {
502 0 : continue;
503 : }
504 1343 : if (mat->group == s_mat->materials(MaterNumNext)->group) {
505 0 : WrongWindowLayering = true;
506 : }
507 : }
508 : }
509 :
510 : // It is not necessary to check rest of BSDF window structure since that is performed inside TARCOG90 routine.
511 : // That routine also allow structures which are not allowed in rest of this routine
512 1348 : if (thisConstruct.WindowTypeBSDF) {
513 14 : thisConstruct.TotGlassLayers = TotGlassLayers;
514 14 : thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
515 14 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
516 14 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
517 14 : return;
518 : }
519 :
520 1334 : if (thisConstruct.WindowTypeEQL) {
521 3 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
522 3 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
523 3 : return;
524 : }
525 :
526 1331 : if (matOutside->group == Material::Group::Gas || matOutside->group == Material::Group::GasMixture ||
527 1331 : matInside->group == Material::Group::Gas || matInside->group == Material::Group::GasMixture) {
528 0 : WrongWindowLayering = true; // Gas cannot be first or last layer
529 : }
530 1331 : if (TotShadeLayers > 1) {
531 0 : WrongWindowLayering = true; // At most one shade, screen or blind allowed
532 : }
533 :
534 : // If there is a diffusing glass layer no shade, screen or blind is allowed
535 3941 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
536 2610 : int const MatNum = thisConstruct.LayerPoint(Layer);
537 2610 : if (MatNum == 0) {
538 0 : continue; // error -- has been caught will stop program later
539 : }
540 2610 : auto const *mat = s_mat->materials(MatNum);
541 2610 : if (mat->group != Material::Group::Glass) {
542 790 : continue;
543 : }
544 1820 : auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
545 1820 : assert(matGlass != nullptr);
546 1820 : if (matGlass->SolarDiffusing && TotShadeLayers > 0) {
547 0 : ErrorsFound = true;
548 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
549 0 : ShowContinueError(state, format("has diffusing glass={} and a shade, screen or blind layer.", matGlass->Name));
550 0 : break;
551 : }
552 : }
553 :
554 : // If there is a diffusing glass layer it must be the innermost layer
555 1331 : if (TotGlassLayers > 1) {
556 561 : int GlassLayNum = 0;
557 2381 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
558 1820 : int const MatNum = thisConstruct.LayerPoint(Layer);
559 1820 : if (MatNum == 0) {
560 0 : continue; // error -- has been caught will stop program later
561 : }
562 1820 : auto const *mat = s_mat->materials(MatNum);
563 1820 : if (mat->group != Material::Group::Glass) {
564 638 : continue;
565 : }
566 :
567 1182 : auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
568 1182 : assert(matGlass != nullptr);
569 1182 : ++GlassLayNum;
570 1182 : if (GlassLayNum < TotGlassLayers && matGlass->SolarDiffusing) {
571 0 : ErrorsFound = true;
572 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
573 0 : ShowContinueError(state, format("has diffusing glass={} that is not the innermost glass layer.", matGlass->Name));
574 : }
575 : }
576 : }
577 :
578 : // interior window screen is not allowed. Check for invalid between-glass screen is checked below.
579 1331 : if (TotShadeLayers == 1 && matInside->group == Material::Group::Screen && TotLayers != 1) {
580 0 : WrongWindowLayering = true;
581 : }
582 :
583 : // Consistency checks for a construction with a between-glass shade or blind
584 :
585 1331 : if (TotShadeLayers == 1 && matOutside->group != Material::Group::Shade && matOutside->group != Material::Group::Blind &&
586 27 : matOutside->group != Material::Group::Screen && matInside->group != Material::Group::Shade &&
587 18 : matInside->group != Material::Group::Blind && matInside->group != Material::Group::ComplexShade && !WrongWindowLayering) {
588 :
589 : // This is a construction with a between-glass shade or blind
590 :
591 5 : if (TotGlassLayers >= 4) {
592 : // Quadruple pane not allowed.
593 0 : WrongWindowLayering = true;
594 5 : } else if (TotGlassLayers == 2 || TotGlassLayers == 3) {
595 5 : bool ValidBGShadeBlindConst = false;
596 5 : if (TotGlassLayers == 2) {
597 3 : if (TotLayers != 5) {
598 0 : WrongWindowLayering = true;
599 : } else {
600 9 : if (matOutside->group == Material::Group::Glass &&
601 3 : (s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::Gas ||
602 0 : s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::GasMixture) &&
603 3 : ((s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Shade ||
604 2 : s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Blind) &&
605 3 : s_mat->materials(thisConstruct.LayerPoint(3))->group != Material::Group::Screen) &&
606 3 : (s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::Gas ||
607 6 : s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::GasMixture) &&
608 3 : s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Glass) {
609 3 : ValidBGShadeBlindConst = true;
610 : }
611 : }
612 : } else { // TotGlassLayers = 3
613 2 : if (TotLayers != 7) {
614 0 : WrongWindowLayering = true;
615 : } else {
616 6 : if (matOutside->group == Material::Group::Glass &&
617 2 : (s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::Gas ||
618 0 : s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::GasMixture) &&
619 2 : s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Glass &&
620 2 : (s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::Gas ||
621 0 : s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::GasMixture) &&
622 2 : ((s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Shade ||
623 1 : s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Blind) &&
624 2 : s_mat->materials(thisConstruct.LayerPoint(5))->group != Material::Group::Screen) &&
625 2 : (s_mat->materials(thisConstruct.LayerPoint(6))->group == Material::Group::Gas ||
626 4 : s_mat->materials(thisConstruct.LayerPoint(6))->group == Material::Group::GasMixture) &&
627 2 : s_mat->materials(thisConstruct.LayerPoint(7))->group == Material::Group::Glass) {
628 2 : ValidBGShadeBlindConst = true;
629 : }
630 : }
631 : } // End of check if TotGlassLayers = 2 or 3
632 5 : if (!ValidBGShadeBlindConst) {
633 0 : WrongWindowLayering = true;
634 : }
635 5 : if (!WrongWindowLayering) {
636 5 : int const LayNumSh = 2 * TotGlassLayers - 1;
637 5 : int const MatSh = thisConstruct.LayerPoint(LayNumSh);
638 5 : auto const *matSh = s_mat->materials(MatSh);
639 : // For double pane, shade/blind must be layer #3.
640 : // For triple pane, it must be layer #5 (i.e., between two inner panes).
641 5 : if (matSh->group != Material::Group::Shade && matSh->group != Material::Group::Blind) {
642 0 : WrongWindowLayering = true;
643 : }
644 5 : if (TotLayers != 2 * TotGlassLayers + 1) {
645 0 : WrongWindowLayering = true;
646 : }
647 5 : if (!WrongWindowLayering) {
648 : // Gas on either side of a between-glass shade/blind must be the same
649 5 : int const MatGapL = thisConstruct.LayerPoint(LayNumSh - 1);
650 5 : int const MatGapR = thisConstruct.LayerPoint(LayNumSh + 1);
651 5 : auto const *matGapL = dynamic_cast<const Material::MaterialGasMix *>(s_mat->materials(MatGapL));
652 5 : auto const *matGapR = dynamic_cast<const Material::MaterialGasMix *>(s_mat->materials(MatGapR));
653 30 : for (int IGas = 0; IGas < Material::maxMixGases; ++IGas) {
654 25 : if ((matGapL->gases[IGas].type != matGapR->gases[IGas].type) || (matGapL->gasFracts[IGas] != matGapR->gasFracts[IGas])) {
655 0 : WrongWindowLayering = true;
656 : }
657 : }
658 : // Gap width on either side of a between-glass shade/blind must be the same
659 5 : if (std::abs(matGapL->Thickness - matGapR->Thickness) > 0.0005) {
660 0 : WrongWindowLayering = true;
661 : }
662 5 : if (matSh->group == Material::Group::Blind) {
663 3 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(matSh);
664 3 : assert(matBlind != nullptr);
665 3 : if ((matGapL->Thickness + matGapR->Thickness) < matBlind->SlatWidth) {
666 0 : ErrorsFound = true;
667 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: For window construction {}", thisConstruct.Name));
668 0 : ShowContinueError(state, "the slat width of the between-glass blind is greater than");
669 0 : ShowContinueError(state, "the sum of the widths of the gas layers adjacent to the blind.");
670 : }
671 : } // End of check if material is window blind
672 : } // End of check if WrongWindowLayering
673 : } // End of check if WrongWindowLayering
674 : } // End of check on total glass layers
675 : } // End of check if construction has between-glass shade/blind
676 :
677 : // Check Simple Windows,
678 1331 : if (s_mat->materials(thisConstruct.LayerPoint(1))->group == Material::Group::GlassSimple) {
679 131 : if (TotLayers > 1) {
680 : // check that none of the other layers are glazing or gas
681 15 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
682 10 : int const MaterNum = thisConstruct.LayerPoint(Layer);
683 10 : if (MaterNum == 0) {
684 0 : continue; // error -- has been caught will stop program later
685 : }
686 10 : auto const *mat = s_mat->materials(MaterNum);
687 10 : if (mat->group == Material::Group::Glass) {
688 0 : ErrorsFound = true;
689 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
690 0 : ShowContinueError(state, "For simple window constructions, no other glazing layers are allowed.");
691 : }
692 10 : if (mat->group == Material::Group::Gas) {
693 0 : ErrorsFound = true;
694 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
695 0 : ShowContinueError(state, "For simple window constructions, no other gas layers are allowed.");
696 : }
697 : }
698 : }
699 : }
700 :
701 1331 : if (WrongWindowLayering) {
702 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
703 0 : ShowContinueError(state, " For multi-layer window constructions the following rules apply:");
704 0 : ShowContinueError(state, " --The first and last layer must be a solid layer (glass or shade/screen/blind),");
705 0 : ShowContinueError(state, " --Adjacent glass layers must be separated by one and only one gas layer,");
706 0 : ShowContinueError(state, " --Adjacent layers must not be of the same type,");
707 0 : ShowContinueError(state, " --Only one shade/screen/blind layer is allowed,");
708 0 : ShowContinueError(state, " --An exterior shade/screen/blind must be the first layer,");
709 0 : ShowContinueError(state, " --An interior shade/blind must be the last layer,");
710 0 : ShowContinueError(state, " --An interior screen is not allowed,");
711 0 : ShowContinueError(state, " --For an exterior shade/screen/blind or interior shade/blind, there should not be a gas layer");
712 0 : ShowContinueError(state, " ----between the shade/screen/blind and adjacent glass,");
713 0 : ShowContinueError(state, " --A between-glass screen is not allowed,");
714 0 : ShowContinueError(state, " --A between-glass shade/blind is allowed only for double and triple glazing,");
715 0 : ShowContinueError(state, " --A between-glass shade/blind must have adjacent gas layers of the same type and width,");
716 0 : ShowContinueError(state, " --For triple glazing the between-glass shade/blind must be between the two inner glass layers,");
717 0 : ShowContinueError(state, " --The slat width of a between-glass blind must be less than the sum of the widths");
718 0 : ShowContinueError(state, " ----of the gas layers adjacent to the blind.");
719 0 : ErrorsFound = true;
720 : }
721 :
722 1331 : thisConstruct.TotGlassLayers = TotGlassLayers;
723 1331 : thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
724 :
725 : // In following, InsideLayer is layer number of inside glass and InsideAbsorpThermal applies
726 : // only to inside glass; it is corrected later in InitGlassOpticalCalculations
727 : // if construction has inside shade or blind.
728 1331 : if (matInside->group == Material::Group::Shade || matInside->group == Material::Group::Blind) {
729 20 : --InsideLayer;
730 : }
731 1331 : if (InsideLayer > 0) {
732 1331 : InsideMaterNum = thisConstruct.LayerPoint(InsideLayer);
733 1331 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
734 : }
735 1331 : if (InsideMaterNum != 0) {
736 1331 : auto const *thisInsideMaterial = s_mat->materials(InsideMaterNum);
737 1331 : thisConstruct.InsideAbsorpVis = thisInsideMaterial->AbsorpVisible;
738 1331 : thisConstruct.InsideAbsorpSolar = thisInsideMaterial->AbsorpSolar;
739 : }
740 :
741 1331 : if ((matOutside->group == Material::Group::Glass) || (matOutside->group == Material::Group::GlassSimple)) { // Glass
742 1324 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
743 : } else { // Exterior shade, blind or screen
744 7 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermal;
745 : }
746 :
747 : } else { // Opaque surface
748 4714 : thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermal;
749 4714 : thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermal;
750 : }
751 :
752 6045 : thisConstruct.OutsideRoughness = matOutside->Roughness;
753 :
754 6045 : if (matOutside->group == Material::Group::AirGap) {
755 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Outside Layer is Air for construction {}", thisConstruct.Name));
756 0 : ShowContinueError(state, format(" Error in material {}", matOutside->Name));
757 0 : ErrorsFound = true;
758 : }
759 6045 : if (InsideLayer > 0) {
760 6045 : if (matInside->group == Material::Group::AirGap) {
761 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Inside Layer is Air for construction {}", thisConstruct.Name));
762 0 : ShowContinueError(state, format(" Error in material {}", matInside->Name));
763 0 : ErrorsFound = true;
764 : }
765 : }
766 :
767 6045 : if (matOutside->group == Material::Group::EcoRoof) {
768 4 : thisConstruct.TypeIsEcoRoof = true;
769 : // need to check EcoRoof is not non-outside layer
770 12 : for (int Layer = 2; Layer <= TotLayers; ++Layer) {
771 8 : if (s_mat->materials(thisConstruct.LayerPoint(Layer))->group == Material::Group::EcoRoof) {
772 0 : ShowSevereError(state,
773 0 : format("CheckAndSetConstructionProperties: Interior Layer is EcoRoof for construction {}", thisConstruct.Name));
774 0 : ShowContinueError(state, format(" Error in material {}", s_mat->materials(thisConstruct.LayerPoint(Layer))->Name));
775 0 : ErrorsFound = true;
776 : }
777 : }
778 : }
779 :
780 6045 : if (matOutside->group == Material::Group::IRTransparent) {
781 2 : thisConstruct.TypeIsIRT = true;
782 2 : if (thisConstruct.TotLayers != 1) {
783 0 : ShowSevereError(
784 : state,
785 0 : format("CheckAndSetConstructionProperties: Infrared Transparent (IRT) Construction is limited to 1 layer {}", thisConstruct.Name));
786 0 : ShowContinueError(state, " Too many layers in referenced construction.");
787 0 : ErrorsFound = true;
788 : }
789 : }
790 : }
791 :
792 370 : int AssignReverseConstructionNumber(EnergyPlusData &state,
793 : int const ConstrNum, // Existing Construction number of first surface
794 : bool &ErrorsFound)
795 : {
796 :
797 : // FUNCTION INFORMATION:
798 : // AUTHOR Linda Lawrie
799 : // DATE WRITTEN December 2006
800 :
801 : // PURPOSE OF THIS FUNCTION:
802 : // For interzone, unentered surfaces, we need to have "reverse" constructions
803 : // assigned to the created surfaces. These need to be the reverse (outside to inside layer)
804 : // of existing surfaces. Plus, there may be one already in the data structure so this is looked for as well.
805 :
806 : // METHODOLOGY EMPLOYED:
807 : // Create reverse layers. Look in current constructions to see if match. If no match, create a new one.
808 :
809 370 : auto &s_mat = state.dataMaterial;
810 : // Return value
811 : int NewConstrNum; // Reverse Construction Number
812 :
813 370 : if (ConstrNum == 0) {
814 : // error caught elsewhere
815 0 : NewConstrNum = 0;
816 0 : return NewConstrNum;
817 : }
818 :
819 370 : auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
820 370 : thisConstruct.IsUsed = true;
821 370 : int nLayer = 0;
822 370 : state.dataConstruction->LayerPoint = 0;
823 1115 : for (int Loop = thisConstruct.TotLayers; Loop >= 1; --Loop) {
824 745 : ++nLayer;
825 745 : state.dataConstruction->LayerPoint(nLayer) = thisConstruct.LayerPoint(Loop);
826 : }
827 :
828 : // now, got thru and see if there is a match already....
829 370 : NewConstrNum = 0;
830 2232 : for (int Loop = 1; Loop <= state.dataHeatBal->TotConstructs; ++Loop) {
831 2227 : bool Found = true;
832 6249 : for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
833 5884 : if (state.dataConstruction->Construct(Loop).LayerPoint(nLayer) != state.dataConstruction->LayerPoint(nLayer)) {
834 1862 : Found = false;
835 1862 : break;
836 : }
837 : }
838 2227 : if (Found) {
839 365 : NewConstrNum = Loop;
840 365 : state.dataConstruction->Construct(Loop).IsUsed = true;
841 365 : break;
842 : }
843 : }
844 :
845 : // if need new one, bunch o stuff
846 370 : if (NewConstrNum == 0) {
847 5 : ++state.dataHeatBal->TotConstructs;
848 5 : state.dataConstruction->Construct.redimension(state.dataHeatBal->TotConstructs);
849 5 : state.dataHeatBal->NominalRforNominalUCalculation.redimension(state.dataHeatBal->TotConstructs);
850 5 : state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) = 0.0;
851 5 : state.dataHeatBal->NominalU.redimension(state.dataHeatBal->TotConstructs);
852 5 : state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) = 0.0;
853 5 : state.dataHeatBal->NominalUBeforeAdjusted.redimension(state.dataHeatBal->TotConstructs);
854 5 : state.dataHeatBal->NominalUBeforeAdjusted(state.dataHeatBal->TotConstructs) = 0.0;
855 5 : state.dataHeatBal->CoeffAdjRatio.redimension(state.dataHeatBal->TotConstructs) = 1.0;
856 : // Put in new attributes
857 5 : NewConstrNum = state.dataHeatBal->TotConstructs;
858 5 : state.dataConstruction->Construct(NewConstrNum).IsUsed = true;
859 5 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs) =
860 10 : state.dataConstruction->Construct(ConstrNum); // preserve some of the attributes.
861 : // replace others...
862 5 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).Name = "iz-" + state.dataConstruction->Construct(ConstrNum).Name;
863 5 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
864 60 : for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
865 55 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).LayerPoint(nLayer) = state.dataConstruction->LayerPoint(nLayer);
866 55 : if (state.dataConstruction->LayerPoint(nLayer) != 0) {
867 16 : state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) +=
868 16 : s_mat->materials(state.dataConstruction->LayerPoint(nLayer))->NominalR;
869 : }
870 : }
871 :
872 : // no error if zero -- that will have been caught with earlier construction
873 : // the following line was changed to fix CR7601
874 5 : if (state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) != 0.0) {
875 5 : state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) =
876 5 : 1.0 / state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs);
877 : }
878 :
879 5 : CheckAndSetConstructionProperties(state, state.dataHeatBal->TotConstructs, ErrorsFound);
880 : }
881 :
882 370 : return NewConstrNum;
883 : }
884 :
885 45600 : Real64 ComputeNominalUwithConvCoeffs(EnergyPlusData &state,
886 : int const numSurf, // index for Surface array.
887 : bool &isValid // returns true if result is valid
888 : )
889 : {
890 :
891 : // SUBROUTINE INFORMATION:
892 : // AUTHOR Jason Glazer
893 : // DATE WRITTEN September 2013
894 : // MODIFIED na
895 : // RE-ENGINEERED na
896 :
897 : // PURPOSE OF THIS SUBROUTINE:
898 : // Calculate Nominal U-value with convection/film coefficients for reporting by
899 : // adding on prescribed R-values for interior and exterior convection coefficients
900 : // as found in ASHRAE 90.1-2004, Appendix A. Used in EIO and tabular reports.
901 : // ASHRAE 90.1-2004 Section A9.4.1 shows the following:
902 : // R-value Condition
903 : // All exterior conditions IP: 0.17 SI: 0.0299
904 : // All semi-exterior surfaces IP: 0.46 SI: 0.0810
905 : // Interior horizontal surfaces, heat flow up IP: 0.61 SI: 0.1074
906 : // Interior horizontal surfaces, heat flow down IP: 0.92 SI: 0.1620
907 : // Interior vertical surfaces IP: 0.68 SI: 0.1198
908 : // This section shows the same value in 90.1-2010 and 90.2-2010
909 : // Note that this report does not use the semi-exterior surface value because
910 : // EnergyPlus does not have a way to specifically tell whether or not a surface
911 : // is connected to a semi-exterior area of the building. Users can always use
912 : // the Nominal U-Value to manually calculated this. The values calculated here
913 : // are simply reported to the EIO file and not used for any calculations.
914 :
915 : // Return value
916 : Real64 NominalUwithConvCoeffs; // return value
917 :
918 : static constexpr std::array<Real64, static_cast<int>(DataSurfaces::SurfaceClass::Num)> filmCoefs = {
919 : 0.0, // None
920 : 0.1197548, // Wall
921 : 0.1620212, // Floor
922 : 0.1074271, // Roof
923 : 0.0, // IntMass
924 : 0.0, // Detached_B
925 : 0.0, // Detached_F
926 : 0.1197548, // Window
927 : 0.1197548, // GlassDoor
928 : 0.1197548, // Door
929 : 0.0, // Shading
930 : 0.0, // Overhang
931 : 0.0, // Fin
932 : 0.0, // TDD_Dome
933 : 0.0 // TDD_Diffuser
934 : }; // If anything added to the enum SurfaceClass, adjust this list appropriately
935 :
936 : Real64 insideFilm;
937 : Real64 outsideFilm;
938 :
939 45600 : isValid = true;
940 :
941 45600 : auto &thisSurface = state.dataSurface->Surface(numSurf);
942 :
943 : // exterior conditions
944 45600 : switch (thisSurface.ExtBoundCond) {
945 18171 : case DataSurfaces::ExternalEnvironment: { // ExtBoundCond = 0
946 18171 : outsideFilm = 0.0299387; // All exterior conditions
947 18171 : } break;
948 1 : case DataSurfaces::OtherSideCoefCalcExt: {
949 1 : outsideFilm = state.dataSurface->OSC(thisSurface.OSCPtr).SurfFilmCoef;
950 1 : } break;
951 2557 : case DataSurfaces::Ground:
952 : case DataSurfaces::OtherSideCoefNoCalcExt:
953 : case DataSurfaces::OtherSideCondModeledExt:
954 : case DataSurfaces::GroundFCfactorMethod:
955 : case DataSurfaces::KivaFoundation: { // All these cases have a negative ExtBoundCond so don't use film coefficients
956 2557 : outsideFilm = 0.0;
957 2557 : } break;
958 24871 : default: { // Interior Surface Attached to a Zone (ExtBoundCond is a surface)
959 24871 : outsideFilm = filmCoefs[static_cast<int>(state.dataSurface->Surface(thisSurface.ExtBoundCond).Class)];
960 24871 : } break;
961 : }
962 : // interior conditions and calculate the return value
963 45600 : if (state.dataHeatBal->NominalU(thisSurface.Construction) > 0.0) {
964 45582 : insideFilm = filmCoefs[static_cast<int>(thisSurface.Class)];
965 45582 : if (insideFilm == 0.0) {
966 2539 : outsideFilm = 0.0;
967 : }
968 45582 : NominalUwithConvCoeffs =
969 45582 : 1.0 / (insideFilm + (1.0 / state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction)) + outsideFilm);
970 : } else {
971 18 : isValid = false;
972 18 : NominalUwithConvCoeffs = state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction);
973 : }
974 :
975 45600 : return NominalUwithConvCoeffs;
976 : }
977 :
978 801 : void SetFlagForWindowConstructionWithShadeOrBlindLayer(EnergyPlusData &state)
979 : {
980 :
981 : // PURPOSE OF THIS SUBROUTINE:
982 : // check fenestrations with shading control and set a flag to true if its construction has
983 : // either shade or blind material layer
984 :
985 : // METHODOLOGY EMPLOYED:
986 : // Loop through Surface and register any shading controls, and loop through the construction
987 : // material layer
988 :
989 : // Using/Aliasing
990 : using DataSurfaces::ExternalEnvironment;
991 :
992 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
993 801 : int loopSurfNum(0); // surface index
994 801 : int ConstrNum(0); // construction index
995 801 : int NumLayers(0); // number of material layers in a construction
996 801 : int Layer(0); // construction material layer index
997 801 : int MaterNum(0); // construction material index
998 :
999 801 : auto &s_mat = state.dataMaterial;
1000 :
1001 48091 : for (loopSurfNum = 1; loopSurfNum <= state.dataSurface->TotSurfaces; ++loopSurfNum) {
1002 :
1003 47290 : if (state.dataSurface->Surface(loopSurfNum).Class != DataSurfaces::SurfaceClass::Window) {
1004 40931 : continue;
1005 : }
1006 6359 : if (state.dataSurface->Surface(loopSurfNum).ExtBoundCond != ExternalEnvironment) {
1007 14 : continue;
1008 : }
1009 6345 : if (!state.dataSurface->Surface(loopSurfNum).HasShadeControl) {
1010 6194 : continue;
1011 : }
1012 151 : if (state.dataSurface->Surface(loopSurfNum).activeShadedConstruction == 0) {
1013 0 : continue;
1014 : }
1015 :
1016 151 : ConstrNum = state.dataSurface->Surface(loopSurfNum).activeShadedConstruction;
1017 151 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
1018 151 : if (thisConstruct.TypeIsWindow) {
1019 151 : NumLayers = thisConstruct.TotLayers;
1020 610 : for (Layer = 1; Layer <= NumLayers; ++Layer) {
1021 459 : MaterNum = thisConstruct.LayerPoint(Layer);
1022 459 : if (MaterNum == 0) {
1023 0 : continue;
1024 : }
1025 459 : auto const *mat = s_mat->materials(MaterNum);
1026 459 : if (mat->group == Material::Group::Shade || mat->group == Material::Group::Blind) {
1027 113 : state.dataSurface->SurfWinHasShadeOrBlindLayer(loopSurfNum) = true;
1028 : }
1029 : }
1030 : }
1031 : }
1032 801 : }
1033 :
1034 801 : void AllocateIntGains(EnergyPlusData &state)
1035 : {
1036 801 : state.dataHeatBal->ZoneIntGain.allocate(state.dataGlobal->NumOfZones);
1037 801 : state.dataHeatBal->spaceIntGain.allocate(state.dataGlobal->numSpaces);
1038 801 : state.dataHeatBal->spaceIntGainDevices.allocate(state.dataGlobal->numSpaces);
1039 801 : state.dataDayltg->spacePowerReductionFactor.dimension(state.dataGlobal->numSpaces, 1.0);
1040 801 : }
1041 :
1042 : } // namespace EnergyPlus::DataHeatBalance
|