Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
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 970159 : Real64 SpaceData::sumHATsurf(EnergyPlusData &state)
102 : {
103 : // PURPOSE OF THIS FUNCTION:
104 : // This function calculates the space sum of Hc*Area*Tsurf.
105 :
106 970159 : Real64 sumHATsurf = 0.0;
107 :
108 8005943 : for (int surfNum = this->HTSurfaceFirst; surfNum <= this->HTSurfaceLast; ++surfNum) {
109 7035784 : Real64 Area = state.dataSurface->Surface(surfNum).Area;
110 :
111 7035784 : if (state.dataSurface->Surface(surfNum).Class == DataSurfaces::SurfaceClass::Window) {
112 545959 : 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 545959 : 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 7035784 : sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(surfNum);
131 : }
132 :
133 970159 : return sumHATsurf;
134 : }
135 :
136 970159 : Real64 ZoneData::sumHATsurf(EnergyPlusData &state)
137 : {
138 970159 : Real64 sumHATsurf = 0.0;
139 1940318 : for (int spaceNum : this->spaceIndexes) {
140 970159 : sumHATsurf += state.dataHeatBal->space(spaceNum).sumHATsurf(state);
141 970159 : }
142 970159 : return sumHATsurf;
143 : }
144 19871224 : 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 19871224 : 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 19871224 : Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
161 19871224 : Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
162 :
163 19871224 : Real64 const Z(Centroid.z); // Centroid value
164 19871224 : if (Z <= 0.0) {
165 214705 : OutDryBulbTemp = BaseDryTemp;
166 214705 : OutWetBulbTemp = BaseWetTemp;
167 : } else {
168 19656519 : OutDryBulbTemp = BaseDryTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
169 19656519 : OutWetBulbTemp = BaseWetTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
170 : }
171 : }
172 19871224 : }
173 :
174 19866168 : 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 19866168 : if (state.dataEnvrn->SiteWindExp == 0.0) {
186 0 : WindSpeed = state.dataEnvrn->WindSpeed;
187 : } else {
188 19866168 : Real64 const Z(Centroid.z); // Centroid value
189 19866168 : if (Z <= 0.0) {
190 214650 : 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 19651518 : WindSpeed = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
196 : }
197 : }
198 19866168 : }
199 :
200 19866168 : void ZoneData::SetWindDirAt(Real64 const fac)
201 : {
202 19866168 : WindDir = fac;
203 19866168 : }
204 :
205 5072 : void AirReportVars::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string const &name)
206 : {
207 15216 : SetupOutputVariable(state,
208 10144 : format("{} Mean Air Temperature", prefix),
209 : Constant::Units::C,
210 5072 : this->MeanAirTemp,
211 : OutputProcessor::TimeStepType::Zone,
212 : OutputProcessor::StoreType::Average,
213 : name);
214 15216 : SetupOutputVariable(state,
215 10144 : format("{} Wetbulb Globe Temperature", prefix),
216 : Constant::Units::C,
217 5072 : this->WetbulbGlobeTemp,
218 : OutputProcessor::TimeStepType::Zone,
219 : OutputProcessor::StoreType::Average,
220 : name);
221 15216 : SetupOutputVariable(state,
222 10144 : format("{} Operative Temperature", prefix),
223 : Constant::Units::C,
224 5072 : this->OperativeTemp,
225 : OutputProcessor::TimeStepType::Zone,
226 : OutputProcessor::StoreType::Average,
227 : name);
228 15216 : SetupOutputVariable(state,
229 10144 : format("{} Mean Air Dewpoint Temperature", prefix),
230 : Constant::Units::C,
231 5072 : this->MeanAirDewPointTemp,
232 : OutputProcessor::TimeStepType::Zone,
233 : OutputProcessor::StoreType::Average,
234 : name);
235 15216 : SetupOutputVariable(state,
236 10144 : format("{} Mean Air Humidity Ratio", prefix),
237 : Constant::Units::kgWater_kgDryAir,
238 5072 : this->MeanAirHumRat,
239 : OutputProcessor::TimeStepType::Zone,
240 : OutputProcessor::StoreType::Average,
241 : name);
242 15216 : SetupOutputVariable(state,
243 10144 : format("{} Air Heat Balance Internal Convective Heat Gain Rate", prefix),
244 : Constant::Units::W,
245 5072 : this->SumIntGains,
246 : OutputProcessor::TimeStepType::System,
247 : OutputProcessor::StoreType::Average,
248 : name);
249 15216 : SetupOutputVariable(state,
250 10144 : format("{} Air Heat Balance Surface Convection Rate", prefix),
251 : Constant::Units::W,
252 5072 : this->SumHADTsurfs,
253 : OutputProcessor::TimeStepType::System,
254 : OutputProcessor::StoreType::Average,
255 : name);
256 15216 : SetupOutputVariable(state,
257 10144 : format("{} Air Heat Balance Interzone Air Transfer Rate", prefix),
258 : Constant::Units::W,
259 5072 : this->SumMCpDTzones,
260 : OutputProcessor::TimeStepType::System,
261 : OutputProcessor::StoreType::Average,
262 : name);
263 15216 : SetupOutputVariable(state,
264 10144 : format("{} Air Heat Balance Outdoor Air Transfer Rate", prefix),
265 : Constant::Units::W,
266 5072 : this->SumMCpDtInfil,
267 : OutputProcessor::TimeStepType::System,
268 : OutputProcessor::StoreType::Average,
269 : name);
270 15216 : SetupOutputVariable(state,
271 10144 : format("{} Air Heat Balance System Air Transfer Rate", prefix),
272 : Constant::Units::W,
273 5072 : this->SumMCpDTsystem,
274 : OutputProcessor::TimeStepType::System,
275 : OutputProcessor::StoreType::Average,
276 : name);
277 15216 : SetupOutputVariable(state,
278 10144 : format("{} Air Heat Balance System Convective Heat Gain Rate", prefix),
279 : Constant::Units::W,
280 5072 : this->SumNonAirSystem,
281 : OutputProcessor::TimeStepType::System,
282 : OutputProcessor::StoreType::Average,
283 : name);
284 15216 : SetupOutputVariable(state,
285 10144 : format("{} Air Heat Balance Air Energy Storage Rate", prefix),
286 : Constant::Units::W,
287 5072 : this->CzdTdt,
288 : OutputProcessor::TimeStepType::System,
289 : OutputProcessor::StoreType::Average,
290 : name);
291 5072 : 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 5072 : }
301 :
302 2805278 : void SetZoneOutBulbTempAt(EnergyPlusData &state)
303 : {
304 22676502 : for (auto &zone : state.dataHeatBal->Zone) {
305 19871224 : zone.SetOutBulbTempAt(state);
306 2805278 : }
307 2805278 : }
308 :
309 2805278 : void CheckZoneOutBulbTempAt(EnergyPlusData &state)
310 : {
311 : // Using/Aliasing
312 : using DataEnvironment::SetOutBulbTempAt_error;
313 :
314 2805278 : Real64 minBulb = 0.0;
315 22676502 : for (auto &zone : state.dataHeatBal->Zone) {
316 19871224 : minBulb = min(minBulb, zone.OutDryBulbTemp, zone.OutWetBulbTemp);
317 19871224 : if (minBulb < -100.0) SetOutBulbTempAt_error(state, "Zone", zone.Centroid.z, zone.Name);
318 2805278 : }
319 2805278 : }
320 :
321 2804482 : void SetZoneWindSpeedAt(EnergyPlusData &state)
322 : {
323 2804482 : Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
324 2804482 : std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
325 22670650 : for (auto &zone : state.dataHeatBal->Zone) {
326 19866168 : zone.SetWindSpeedAt(state, fac);
327 2804482 : }
328 2804482 : }
329 :
330 2804482 : void SetZoneWindDirAt(EnergyPlusData &state)
331 : {
332 : // Using/Aliasing
333 2804482 : Real64 const fac(state.dataEnvrn->WindDir);
334 22670650 : for (auto &zone : state.dataHeatBal->Zone) {
335 19866168 : zone.SetWindDirAt(fac);
336 2804482 : }
337 2804482 : }
338 :
339 6004 : 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 :
352 6004 : auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
353 6004 : int TotLayers = thisConstruct.TotLayers; // Number of layers in a construction
354 6004 : if (TotLayers == 0) return; // error condition, hopefully caught elsewhere
355 6004 : int InsideLayer = TotLayers; // Inside Layer of Construct; for window construct, layer no. of inside glass
356 6004 : if (thisConstruct.LayerPoint(InsideLayer) <= 0) return; // Error condition
357 :
358 : // window screen is not allowed on inside layer
359 :
360 6004 : thisConstruct.DayltPropPtr = 0;
361 6004 : int InsideMaterNum = thisConstruct.LayerPoint(InsideLayer); // Material "number" of the Inside layer
362 6004 : if (InsideMaterNum != 0) {
363 6004 : auto const *mat = state.dataMaterial->Material(InsideMaterNum);
364 6004 : thisConstruct.InsideAbsorpVis = mat->AbsorpVisible;
365 6004 : thisConstruct.InsideAbsorpSolar = mat->AbsorpSolar;
366 :
367 : // Following line applies only to opaque surfaces; it is recalculated later for windows.
368 6004 : thisConstruct.ReflectVisDiffBack = 1.0 - mat->AbsorpVisible;
369 : }
370 :
371 6004 : int OutsideMaterNum = thisConstruct.LayerPoint(1); // Material "number" of the Outside layer
372 6004 : if (OutsideMaterNum != 0) {
373 6004 : auto const *mat = state.dataMaterial->Material(OutsideMaterNum);
374 6004 : thisConstruct.OutsideAbsorpVis = mat->AbsorpVisible;
375 6004 : thisConstruct.OutsideAbsorpSolar = mat->AbsorpSolar;
376 : }
377 :
378 6004 : thisConstruct.TotSolidLayers = 0;
379 6004 : thisConstruct.TotGlassLayers = 0;
380 6004 : thisConstruct.AbsDiffShade = 0.0;
381 :
382 : // Check if any layer is glass, gas, shade, screen or blind; if so it is considered a window construction for
383 : // purposes of error checking.
384 :
385 6004 : thisConstruct.TypeIsWindow = false;
386 20087 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
387 14083 : int const MaterNum = thisConstruct.LayerPoint(Layer);
388 14083 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
389 14083 : auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
390 14083 : thisConstruct.TypeIsWindow =
391 12249 : (thisMaterial->group == Material::Group::WindowGlass || thisMaterial->group == Material::Group::WindowGas ||
392 11643 : thisMaterial->group == Material::Group::WindowGasMixture || thisMaterial->group == Material::Group::Shade ||
393 11616 : thisMaterial->group == Material::Group::WindowBlind || thisMaterial->group == Material::Group::Screen ||
394 11596 : thisMaterial->group == Material::Group::WindowSimpleGlazing || thisMaterial->group == Material::Group::ComplexWindowShade ||
395 11462 : thisMaterial->group == Material::Group::ComplexWindowGap || thisMaterial->group == Material::Group::GlassEquivalentLayer ||
396 11430 : thisMaterial->group == Material::Group::ShadeEquivalentLayer || thisMaterial->group == Material::Group::DrapeEquivalentLayer ||
397 37759 : thisMaterial->group == Material::Group::ScreenEquivalentLayer || thisMaterial->group == Material::Group::BlindEquivalentLayer ||
398 11427 : thisMaterial->group == Material::Group::GapEquivalentLayer);
399 14083 : bool TypeIsNotWindow = (thisMaterial->group == Material::Group::Invalid || thisMaterial->group == Material::Group::Air ||
400 30832 : thisMaterial->group == Material::Group::Regular || thisMaterial->group == Material::Group::EcoRoof ||
401 2666 : thisMaterial->group == Material::Group::IRTransparent);
402 14083 : if (!thisConstruct.TypeIsWindow && !TypeIsNotWindow) assert(false);
403 : }
404 :
405 6004 : if (InsideMaterNum == 0) return;
406 6004 : auto const *thisMaterialInside = state.dataMaterial->Material(InsideMaterNum);
407 6004 : if (OutsideMaterNum == 0) return;
408 6004 : auto const *thisMaterialOutside = state.dataMaterial->Material(OutsideMaterNum);
409 :
410 6004 : if (thisConstruct.TypeIsWindow) {
411 :
412 1331 : bool WrongMaterialsMix = false;
413 1331 : thisConstruct.NumCTFTerms = 0;
414 1331 : thisConstruct.NumHistories = 0;
415 3995 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
416 2664 : int const MaterNum = thisConstruct.LayerPoint(Layer);
417 2664 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
418 2664 : auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
419 2664 : WrongMaterialsMix =
420 2888 : !((thisMaterial->group == Material::Group::WindowGlass) || (thisMaterial->group == Material::Group::WindowGas) ||
421 224 : (thisMaterial->group == Material::Group::WindowGasMixture) || (thisMaterial->group == Material::Group::Shade) ||
422 197 : (thisMaterial->group == Material::Group::WindowBlind) || (thisMaterial->group == Material::Group::Screen) ||
423 177 : (thisMaterial->group == Material::Group::WindowSimpleGlazing) || (thisMaterial->group == Material::Group::ComplexWindowShade) ||
424 43 : (thisMaterial->group == Material::Group::ComplexWindowGap) || (thisMaterial->group == Material::Group::GlassEquivalentLayer) ||
425 11 : (thisMaterial->group == Material::Group::ShadeEquivalentLayer) || (thisMaterial->group == Material::Group::DrapeEquivalentLayer) ||
426 9 : (thisMaterial->group == Material::Group::ScreenEquivalentLayer) || (thisMaterial->group == Material::Group::BlindEquivalentLayer) ||
427 8 : (thisMaterial->group == Material::Group::GapEquivalentLayer));
428 : }
429 :
430 1331 : if (WrongMaterialsMix) { // Illegal material for a window construction
431 0 : ShowSevereError(state,
432 0 : format("Error: Window construction={} has materials other than glass, gas, shade, screen, blind, complex shading, "
433 : "complex gap, or simple system.",
434 0 : thisConstruct.Name));
435 0 : ErrorsFound = true;
436 : // Do not check number of layers for BSDF type of window since that can be handled
437 1331 : } else if ((TotLayers > 8) && (!thisConstruct.WindowTypeBSDF) &&
438 1 : (!thisConstruct.WindowTypeEQL)) { // Too many layers for a window construction
439 0 : ShowSevereError(state,
440 0 : format("CheckAndSetConstructionProperties: Window construction={} has too many layers (max of 8 allowed -- 4 glass + 3 "
441 : "gap + 1 shading device).",
442 0 : thisConstruct.Name));
443 0 : ErrorsFound = true;
444 :
445 1331 : } else if (TotLayers == 1) {
446 738 : auto const *thisMaterial = state.dataMaterial->Material(thisConstruct.LayerPoint(1));
447 738 : Material::Group thisMaterialGroup = thisMaterial->group;
448 738 : if ((thisMaterialGroup == Material::Group::Shade) || (thisMaterialGroup == Material::Group::WindowGas) ||
449 738 : (thisMaterialGroup == Material::Group::WindowGasMixture) || (thisMaterialGroup == Material::Group::WindowBlind) ||
450 738 : (thisMaterialGroup == Material::Group::Screen) || (thisMaterialGroup == Material::Group::ComplexWindowShade) ||
451 : (thisMaterialGroup == Material::Group::ComplexWindowGap)) {
452 0 : ShowSevereError(state,
453 0 : format("CheckAndSetConstructionProperties: The single-layer window construction={} has a gas, complex gap, shade, "
454 : "complex shade, screen or blind material; it should be glass of simple glazing system.",
455 0 : thisConstruct.Name));
456 0 : ErrorsFound = true;
457 : }
458 : }
459 :
460 : // Find total glass layers, total shade/blind layers and total gas layers in a window construction
461 :
462 1331 : bool WrongWindowLayering = false;
463 1331 : int TotGlassLayers = 0;
464 1331 : int TotShadeLayers = 0; // Includes shades, blinds, and screens
465 1331 : int TotGasLayers = 0;
466 3995 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
467 2664 : int const MaterNum = thisConstruct.LayerPoint(Layer);
468 2664 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
469 2664 : auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
470 2664 : if (thisMaterial->group == Material::Group::WindowGlass) ++TotGlassLayers;
471 2664 : if (thisMaterial->group == Material::Group::WindowSimpleGlazing) ++TotGlassLayers;
472 2664 : if (thisMaterial->group == Material::Group::Shade || thisMaterial->group == Material::Group::WindowBlind ||
473 2634 : thisMaterial->group == Material::Group::Screen || thisMaterial->group == Material::Group::ComplexWindowShade)
474 41 : ++TotShadeLayers;
475 2664 : if (thisMaterial->group == Material::Group::WindowGas || thisMaterial->group == Material::Group::WindowGasMixture ||
476 2043 : thisMaterial->group == Material::Group::ComplexWindowGap)
477 645 : ++TotGasLayers;
478 2664 : if (Layer < TotLayers) {
479 1333 : int const MaterNumNext = thisConstruct.LayerPoint(Layer + 1);
480 : // Adjacent layers of same type not allowed
481 1333 : if (MaterNumNext == 0) continue;
482 1333 : if (thisMaterial->group == state.dataMaterial->Material(MaterNumNext)->group) WrongWindowLayering = true;
483 : }
484 : }
485 :
486 : // It is not necessary to check rest of BSDF window structure since that is performed inside TARCOG90 routine.
487 : // That routine also allow structures which are not allowed in rest of this routine
488 1331 : if (thisConstruct.WindowTypeBSDF) {
489 14 : thisConstruct.TotGlassLayers = TotGlassLayers;
490 14 : thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
491 14 : thisConstruct.InsideAbsorpThermal = thisMaterialInside->AbsorpThermalBack;
492 14 : thisConstruct.OutsideAbsorpThermal = thisMaterialOutside->AbsorpThermalFront;
493 14 : return;
494 : }
495 :
496 1317 : if (thisConstruct.WindowTypeEQL) {
497 3 : thisConstruct.InsideAbsorpThermal = thisMaterialInside->AbsorpThermalBack;
498 3 : thisConstruct.OutsideAbsorpThermal = thisMaterialOutside->AbsorpThermalFront;
499 3 : return;
500 : }
501 :
502 1314 : if (thisMaterialOutside->group == Material::Group::WindowGas || thisMaterialOutside->group == Material::Group::WindowGasMixture ||
503 1314 : thisMaterialInside->group == Material::Group::WindowGas || thisMaterialInside->group == Material::Group::WindowGasMixture)
504 0 : WrongWindowLayering = true; // Gas cannot be first or last layer
505 1314 : if (TotShadeLayers > 1) WrongWindowLayering = true; // At most one shade, screen or blind allowed
506 :
507 : // If there is a diffusing glass layer no shade, screen or blind is allowed
508 3897 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
509 2583 : int const MatNum = thisConstruct.LayerPoint(Layer);
510 2583 : if (MatNum == 0) continue; // error -- has been caught will stop program later
511 2583 : auto const *mat = state.dataMaterial->Material(MatNum);
512 2583 : if (mat->group != Material::Group::WindowGlass && mat->group != Material::Group::WindowSimpleGlazing &&
513 653 : mat->group != Material::Group::GlassEquivalentLayer)
514 653 : continue;
515 :
516 1930 : auto const *matGlass = dynamic_cast<Material::MaterialChild const *>(mat);
517 1930 : assert(matGlass != nullptr);
518 1930 : if (matGlass->SolarDiffusing && TotShadeLayers > 0) {
519 0 : ErrorsFound = true;
520 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
521 0 : ShowContinueError(state, format("has diffusing glass={} and a shade, screen or blind layer.", matGlass->Name));
522 0 : break;
523 : }
524 : }
525 :
526 : // If there is a diffusing glass layer it must be the innermost layer
527 1314 : if (TotGlassLayers > 1) {
528 556 : int GlassLayNum = 0;
529 2361 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
530 1805 : int const MatNum = thisConstruct.LayerPoint(Layer);
531 1805 : if (MatNum == 0) continue; // error -- has been caught will stop program later
532 1805 : auto const *mat = state.dataMaterial->Material(MatNum);
533 1805 : if (mat->group != Material::Group::WindowGlass) continue;
534 :
535 1172 : auto const *matGlass = dynamic_cast<Material::MaterialChild const *>(mat);
536 1172 : assert(matGlass != nullptr);
537 1172 : ++GlassLayNum;
538 1172 : if (GlassLayNum < TotGlassLayers && matGlass->SolarDiffusing) {
539 0 : ErrorsFound = true;
540 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
541 0 : ShowContinueError(state, format("has diffusing glass={} that is not the innermost glass layer.", matGlass->Name));
542 : }
543 : }
544 : }
545 :
546 : // interior window screen is not allowed. Check for invalid between-glass screen is checked below.
547 1314 : if (TotShadeLayers == 1 && thisMaterialInside->group == Material::Group::Screen && TotLayers != 1) {
548 0 : WrongWindowLayering = true;
549 : }
550 :
551 : // Consistency checks for a construction with a between-glass shade or blind
552 :
553 1314 : if (TotShadeLayers == 1 && thisMaterialOutside->group != Material::Group::Shade &&
554 29 : thisMaterialOutside->group != Material::Group::WindowBlind && thisMaterialOutside->group != Material::Group::Screen &&
555 25 : thisMaterialInside->group != Material::Group::Shade && thisMaterialInside->group != Material::Group::WindowBlind &&
556 5 : thisMaterialInside->group != Material::Group::ComplexWindowShade && !WrongWindowLayering) {
557 :
558 : // This is a construction with a between-glass shade or blind
559 :
560 5 : if (TotGlassLayers >= 4) {
561 : // Quadruple pane not allowed.
562 0 : WrongWindowLayering = true;
563 5 : } else if (TotGlassLayers == 2 || TotGlassLayers == 3) {
564 5 : bool ValidBGShadeBlindConst = false;
565 5 : if (TotGlassLayers == 2) {
566 3 : if (TotLayers != 5) {
567 0 : WrongWindowLayering = true;
568 : } else {
569 9 : if (thisMaterialOutside->group == Material::Group::WindowGlass &&
570 3 : (state.dataMaterial->Material(thisConstruct.LayerPoint(2))->group == Material::Group::WindowGas ||
571 0 : state.dataMaterial->Material(thisConstruct.LayerPoint(2))->group == Material::Group::WindowGasMixture) &&
572 3 : ((state.dataMaterial->Material(thisConstruct.LayerPoint(3))->group == Material::Group::Shade ||
573 2 : state.dataMaterial->Material(thisConstruct.LayerPoint(3))->group == Material::Group::WindowBlind) &&
574 3 : state.dataMaterial->Material(thisConstruct.LayerPoint(3))->group != Material::Group::Screen) &&
575 3 : (state.dataMaterial->Material(thisConstruct.LayerPoint(4))->group == Material::Group::WindowGas ||
576 6 : state.dataMaterial->Material(thisConstruct.LayerPoint(4))->group == Material::Group::WindowGasMixture) &&
577 3 : state.dataMaterial->Material(thisConstruct.LayerPoint(5))->group == Material::Group::WindowGlass)
578 3 : ValidBGShadeBlindConst = true;
579 : }
580 : } else { // TotGlassLayers = 3
581 2 : if (TotLayers != 7) {
582 0 : WrongWindowLayering = true;
583 : } else {
584 6 : if (thisMaterialOutside->group == Material::Group::WindowGlass &&
585 2 : (state.dataMaterial->Material(thisConstruct.LayerPoint(2))->group == Material::Group::WindowGas ||
586 0 : state.dataMaterial->Material(thisConstruct.LayerPoint(2))->group == Material::Group::WindowGasMixture) &&
587 2 : state.dataMaterial->Material(thisConstruct.LayerPoint(3))->group == Material::Group::WindowGlass &&
588 2 : (state.dataMaterial->Material(thisConstruct.LayerPoint(4))->group == Material::Group::WindowGas ||
589 0 : state.dataMaterial->Material(thisConstruct.LayerPoint(4))->group == Material::Group::WindowGasMixture) &&
590 2 : ((state.dataMaterial->Material(thisConstruct.LayerPoint(5))->group == Material::Group::Shade ||
591 1 : state.dataMaterial->Material(thisConstruct.LayerPoint(5))->group == Material::Group::WindowBlind) &&
592 2 : state.dataMaterial->Material(thisConstruct.LayerPoint(5))->group != Material::Group::Screen) &&
593 2 : (state.dataMaterial->Material(thisConstruct.LayerPoint(6))->group == Material::Group::WindowGas ||
594 4 : state.dataMaterial->Material(thisConstruct.LayerPoint(6))->group == Material::Group::WindowGasMixture) &&
595 2 : state.dataMaterial->Material(thisConstruct.LayerPoint(7))->group == Material::Group::WindowGlass)
596 2 : ValidBGShadeBlindConst = true;
597 : }
598 : } // End of check if TotGlassLayers = 2 or 3
599 5 : if (!ValidBGShadeBlindConst) WrongWindowLayering = true;
600 5 : if (!WrongWindowLayering) {
601 5 : int const LayNumSh = 2 * TotGlassLayers - 1;
602 5 : int const MatSh = thisConstruct.LayerPoint(LayNumSh);
603 5 : auto const *thisMaterialSh = dynamic_cast<Material::MaterialChild *>(state.dataMaterial->Material(MatSh));
604 5 : assert(thisMaterialSh != nullptr);
605 : // For double pane, shade/blind must be layer #3.
606 : // For triple pane, it must be layer #5 (i.e., between two inner panes).
607 5 : if (thisMaterialSh->group != Material::Group::Shade && thisMaterialSh->group != Material::Group::WindowBlind)
608 0 : WrongWindowLayering = true;
609 5 : if (TotLayers != 2 * TotGlassLayers + 1) WrongWindowLayering = true;
610 5 : if (!WrongWindowLayering) {
611 : // Gas on either side of a between-glass shade/blind must be the same
612 5 : int const MatGapL = thisConstruct.LayerPoint(LayNumSh - 1);
613 5 : int const MatGapR = thisConstruct.LayerPoint(LayNumSh + 1);
614 5 : auto const *thisMaterialGapL = dynamic_cast<const Material::MaterialGasMix *>(state.dataMaterial->Material(MatGapL));
615 5 : auto const *thisMaterialGapR = dynamic_cast<const Material::MaterialGasMix *>(state.dataMaterial->Material(MatGapR));
616 30 : for (int IGas = 0; IGas < Material::maxMixGases; ++IGas) {
617 50 : if ((thisMaterialGapL->gases[IGas].type != thisMaterialGapR->gases[IGas].type) ||
618 25 : (thisMaterialGapL->gasFracts[IGas] != thisMaterialGapR->gasFracts[IGas]))
619 0 : WrongWindowLayering = true;
620 : }
621 : // Gap width on either side of a between-glass shade/blind must be the same
622 5 : if (std::abs(thisMaterialGapL->Thickness - thisMaterialGapR->Thickness) > 0.0005) WrongWindowLayering = true;
623 5 : if (thisMaterialSh->group == Material::Group::WindowBlind) {
624 3 : int const BlNum = thisMaterialSh->BlindDataPtr;
625 3 : if (BlNum > 0) {
626 3 : if ((thisMaterialGapL->Thickness + thisMaterialGapR->Thickness) < state.dataMaterial->Blind(BlNum).SlatWidth) {
627 0 : ErrorsFound = true;
628 0 : ShowSevereError(state,
629 0 : format("CheckAndSetConstructionProperties: For window construction {}", thisConstruct.Name));
630 0 : ShowContinueError(state, "the slat width of the between-glass blind is greater than");
631 0 : ShowContinueError(state, "the sum of the widths of the gas layers adjacent to the blind.");
632 : }
633 : } // End of check if BlNum > 0
634 : } // End of check if material is window blind
635 : } // End of check if WrongWindowLayering
636 : } // End of check if WrongWindowLayering
637 : } // End of check on total glass layers
638 : } // End of check if construction has between-glass shade/blind
639 :
640 : // Check Simple Windows,
641 1314 : if (state.dataMaterial->Material(thisConstruct.LayerPoint(1))->group == Material::Group::WindowSimpleGlazing) {
642 124 : if (TotLayers > 1) {
643 : // check that none of the other layers are glazing or gas
644 15 : for (int Layer = 1; Layer <= TotLayers; ++Layer) {
645 10 : int const MaterNum = thisConstruct.LayerPoint(Layer);
646 10 : if (MaterNum == 0) continue; // error -- has been caught will stop program later
647 10 : auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
648 10 : if (thisMaterial->group == Material::Group::WindowGlass) {
649 0 : ErrorsFound = true;
650 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
651 0 : ShowContinueError(state, "For simple window constructions, no other glazing layers are allowed.");
652 : }
653 10 : if (thisMaterial->group == Material::Group::WindowGas) {
654 0 : ErrorsFound = true;
655 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
656 0 : ShowContinueError(state, "For simple window constructions, no other gas layers are allowed.");
657 : }
658 : }
659 : }
660 : }
661 :
662 1314 : if (WrongWindowLayering) {
663 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
664 0 : ShowContinueError(state, " For multi-layer window constructions the following rules apply:");
665 0 : ShowContinueError(state, " --The first and last layer must be a solid layer (glass or shade/screen/blind),");
666 0 : ShowContinueError(state, " --Adjacent glass layers must be separated by one and only one gas layer,");
667 0 : ShowContinueError(state, " --Adjacent layers must not be of the same type,");
668 0 : ShowContinueError(state, " --Only one shade/screen/blind layer is allowed,");
669 0 : ShowContinueError(state, " --An exterior shade/screen/blind must be the first layer,");
670 0 : ShowContinueError(state, " --An interior shade/blind must be the last layer,");
671 0 : ShowContinueError(state, " --An interior screen is not allowed,");
672 0 : ShowContinueError(state, " --For an exterior shade/screen/blind or interior shade/blind, there should not be a gas layer");
673 0 : ShowContinueError(state, " ----between the shade/screen/blind and adjacent glass,");
674 0 : ShowContinueError(state, " --A between-glass screen is not allowed,");
675 0 : ShowContinueError(state, " --A between-glass shade/blind is allowed only for double and triple glazing,");
676 0 : ShowContinueError(state, " --A between-glass shade/blind must have adjacent gas layers of the same type and width,");
677 0 : ShowContinueError(state, " --For triple glazing the between-glass shade/blind must be between the two inner glass layers,");
678 0 : ShowContinueError(state, " --The slat width of a between-glass blind must be less than the sum of the widths");
679 0 : ShowContinueError(state, " ----of the gas layers adjacent to the blind.");
680 0 : ErrorsFound = true;
681 : }
682 :
683 1314 : thisConstruct.TotGlassLayers = TotGlassLayers;
684 1314 : thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
685 :
686 : // In following, InsideLayer is layer number of inside glass and InsideAbsorpThermal applies
687 : // only to inside glass; it is corrected later in InitGlassOpticalCalculations
688 : // if construction has inside shade or blind.
689 1314 : if (thisMaterialInside->group == Material::Group::Shade || thisMaterialInside->group == Material::Group::WindowBlind) {
690 20 : --InsideLayer;
691 : }
692 1314 : if (InsideLayer > 0) {
693 1314 : InsideMaterNum = thisConstruct.LayerPoint(InsideLayer);
694 1314 : thisConstruct.InsideAbsorpThermal = thisMaterialInside->AbsorpThermalBack;
695 : }
696 1314 : if (InsideMaterNum != 0) {
697 1314 : auto const *thisInsideMaterial = dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(InsideMaterNum));
698 1314 : thisConstruct.InsideAbsorpVis = thisInsideMaterial->AbsorpVisible;
699 1314 : thisConstruct.InsideAbsorpSolar = thisInsideMaterial->AbsorpSolar;
700 : }
701 :
702 1314 : if ((thisMaterialOutside->group == Material::Group::WindowGlass) ||
703 131 : (thisMaterialOutside->group == Material::Group::WindowSimpleGlazing)) { // Glass
704 1307 : thisConstruct.OutsideAbsorpThermal = thisMaterialOutside->AbsorpThermalFront;
705 : } else { // Exterior shade, blind or screen
706 7 : thisConstruct.OutsideAbsorpThermal = thisMaterialOutside->AbsorpThermal;
707 : }
708 :
709 : } else { // Opaque surface
710 4673 : thisConstruct.InsideAbsorpThermal = thisMaterialInside->AbsorpThermal;
711 4673 : thisConstruct.OutsideAbsorpThermal = thisMaterialOutside->AbsorpThermal;
712 : }
713 :
714 5987 : thisConstruct.OutsideRoughness = thisMaterialOutside->Roughness;
715 :
716 5987 : if (thisMaterialOutside->group == Material::Group::Air) {
717 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Outside Layer is Air for construction {}", thisConstruct.Name));
718 0 : ShowContinueError(state, format(" Error in material {}", thisMaterialOutside->Name));
719 0 : ErrorsFound = true;
720 : }
721 5987 : if (InsideLayer > 0) {
722 5987 : if (thisMaterialInside->group == Material::Group::Air) {
723 0 : ShowSevereError(state, format("CheckAndSetConstructionProperties: Inside Layer is Air for construction {}", thisConstruct.Name));
724 0 : ShowContinueError(state, format(" Error in material {}", thisMaterialInside->Name));
725 0 : ErrorsFound = true;
726 : }
727 : }
728 :
729 5987 : if (thisMaterialOutside->group == Material::Group::EcoRoof) {
730 4 : thisConstruct.TypeIsEcoRoof = true;
731 : // need to check EcoRoof is not non-outside layer
732 12 : for (int Layer = 2; Layer <= TotLayers; ++Layer) {
733 8 : if (state.dataMaterial->Material(thisConstruct.LayerPoint(Layer))->group == Material::Group::EcoRoof) {
734 0 : ShowSevereError(state,
735 0 : format("CheckAndSetConstructionProperties: Interior Layer is EcoRoof for construction {}", thisConstruct.Name));
736 0 : ShowContinueError(state, format(" Error in material {}", state.dataMaterial->Material(thisConstruct.LayerPoint(Layer))->Name));
737 0 : ErrorsFound = true;
738 : }
739 : }
740 : }
741 :
742 5987 : if (thisMaterialOutside->group == Material::Group::IRTransparent) {
743 2 : thisConstruct.TypeIsIRT = true;
744 2 : if (thisConstruct.TotLayers != 1) {
745 0 : ShowSevereError(
746 : state,
747 0 : format("CheckAndSetConstructionProperties: Infrared Transparent (IRT) Construction is limited to 1 layer {}", thisConstruct.Name));
748 0 : ShowContinueError(state, " Too many layers in referenced construction.");
749 0 : ErrorsFound = true;
750 : }
751 : }
752 : }
753 :
754 370 : int AssignReverseConstructionNumber(EnergyPlusData &state,
755 : int const ConstrNum, // Existing Construction number of first surface
756 : bool &ErrorsFound)
757 : {
758 :
759 : // FUNCTION INFORMATION:
760 : // AUTHOR Linda Lawrie
761 : // DATE WRITTEN December 2006
762 :
763 : // PURPOSE OF THIS FUNCTION:
764 : // For interzone, unentered surfaces, we need to have "reverse" constructions
765 : // assigned to the created surfaces. These need to be the reverse (outside to inside layer)
766 : // of existing surfaces. Plus, there may be one already in the data structure so this is looked for as well.
767 :
768 : // METHODOLOGY EMPLOYED:
769 : // Create reverse layers. Look in current constructions to see if match. If no match, create a new one.
770 :
771 : // Return value
772 : int NewConstrNum; // Reverse Construction Number
773 :
774 370 : if (ConstrNum == 0) {
775 : // error caught elsewhere
776 0 : NewConstrNum = 0;
777 0 : return NewConstrNum;
778 : }
779 :
780 370 : auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
781 370 : thisConstruct.IsUsed = true;
782 370 : int nLayer = 0;
783 370 : state.dataConstruction->LayerPoint = 0;
784 1115 : for (int Loop = thisConstruct.TotLayers; Loop >= 1; --Loop) {
785 745 : ++nLayer;
786 745 : state.dataConstruction->LayerPoint(nLayer) = thisConstruct.LayerPoint(Loop);
787 : }
788 :
789 : // now, got thru and see if there is a match already....
790 370 : NewConstrNum = 0;
791 2232 : for (int Loop = 1; Loop <= state.dataHeatBal->TotConstructs; ++Loop) {
792 2227 : bool Found = true;
793 6249 : for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
794 5884 : if (state.dataConstruction->Construct(Loop).LayerPoint(nLayer) != state.dataConstruction->LayerPoint(nLayer)) {
795 1862 : Found = false;
796 1862 : break;
797 : }
798 : }
799 2227 : if (Found) {
800 365 : NewConstrNum = Loop;
801 365 : state.dataConstruction->Construct(Loop).IsUsed = true;
802 365 : break;
803 : }
804 : }
805 :
806 : // if need new one, bunch o stuff
807 370 : if (NewConstrNum == 0) {
808 5 : ++state.dataHeatBal->TotConstructs;
809 5 : state.dataConstruction->Construct.redimension(state.dataHeatBal->TotConstructs);
810 5 : state.dataHeatBal->NominalRforNominalUCalculation.redimension(state.dataHeatBal->TotConstructs);
811 5 : state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) = 0.0;
812 5 : state.dataHeatBal->NominalU.redimension(state.dataHeatBal->TotConstructs);
813 5 : state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) = 0.0;
814 5 : state.dataHeatBal->NominalUBeforeAdjusted.redimension(state.dataHeatBal->TotConstructs);
815 5 : state.dataHeatBal->NominalUBeforeAdjusted(state.dataHeatBal->TotConstructs) = 0.0;
816 5 : state.dataHeatBal->CoeffAdjRatio.redimension(state.dataHeatBal->TotConstructs) = 1.0;
817 : // Put in new attributes
818 5 : NewConstrNum = state.dataHeatBal->TotConstructs;
819 5 : state.dataConstruction->Construct(NewConstrNum).IsUsed = true;
820 5 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs) =
821 10 : state.dataConstruction->Construct(ConstrNum); // preserve some of the attributes.
822 : // replace others...
823 5 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).Name = "iz-" + state.dataConstruction->Construct(ConstrNum).Name;
824 5 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
825 60 : for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
826 55 : state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).LayerPoint(nLayer) = state.dataConstruction->LayerPoint(nLayer);
827 55 : if (state.dataConstruction->LayerPoint(nLayer) != 0) {
828 16 : state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) +=
829 16 : state.dataHeatBal->NominalR(state.dataConstruction->LayerPoint(nLayer));
830 : }
831 : }
832 :
833 : // no error if zero -- that will have been caught with earlier construction
834 : // the following line was changed to fix CR7601
835 5 : if (state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) != 0.0) {
836 5 : state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) =
837 5 : 1.0 / state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs);
838 : }
839 :
840 5 : CheckAndSetConstructionProperties(state, state.dataHeatBal->TotConstructs, ErrorsFound);
841 : }
842 :
843 370 : return NewConstrNum;
844 : }
845 :
846 3 : void AddVariableSlatBlind(EnergyPlusData &state,
847 : int const inBlindNumber, // current Blind Number/pointer to name
848 : int &outBlindNumber, // resultant Blind Number to pass back
849 : bool &errFlag // error flag should one be needed
850 : )
851 : {
852 :
853 : // SUBROUTINE INFORMATION:
854 : // AUTHOR Linda Lawrie
855 : // DATE WRITTEN September 2009
856 : // MODIFIED na
857 : // RE-ENGINEERED na
858 :
859 : // PURPOSE OF THIS SUBROUTINE:
860 : // Window Blinds are presented as "fixed" slat blinds. However for certain Window Shading Controls,
861 : // the program needs to set the property to "variable"/movable slats. Since a blind could be in use
862 : // elsewhere with "fixed", a material needs to be added with variable properties -- having most of the
863 : // "fixed" properties in tact.
864 :
865 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
866 : int Found;
867 : Real64 MinSlatAngGeom;
868 : Real64 MaxSlatAngGeom;
869 :
870 : // Object Data
871 :
872 : // maybe it's already there
873 3 : errFlag = false;
874 3 : Found = Util::FindItemInList("~" + state.dataMaterial->Blind(inBlindNumber).Name, state.dataMaterial->Blind);
875 3 : if (Found == 0) {
876 : // Add a new blind
877 3 : state.dataHeatBal->TotBlinds += 1;
878 3 : state.dataMaterial->Blind.push_back(Material::WindowBlindProperties());
879 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds) = state.dataMaterial->Blind(inBlindNumber);
880 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).Name = "~" + state.dataMaterial->Blind(inBlindNumber).Name;
881 3 : outBlindNumber = state.dataHeatBal->TotBlinds;
882 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatAngleType = DataWindowEquivalentLayer::AngleType::Variable;
883 :
884 : // Minimum and maximum slat angles allowed by slat geometry
885 3 : if (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatWidth >
886 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatSeparation) {
887 3 : MinSlatAngGeom = std::asin(state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatThickness /
888 3 : (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatThickness +
889 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatSeparation)) /
890 : Constant::DegToRadians;
891 : } else {
892 0 : MinSlatAngGeom = 0.0;
893 : }
894 3 : MaxSlatAngGeom = 180.0 - MinSlatAngGeom;
895 :
896 : // Error if maximum slat angle less than minimum
897 :
898 3 : if (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle <
899 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle) {
900 0 : errFlag = true;
901 0 : ShowSevereError(state, format("WindowMaterial:Blind=\"{}\", Illegal value combination.", state.dataMaterial->Blind(inBlindNumber).Name));
902 0 : ShowContinueError(state,
903 0 : format("Minimum Slat Angle=[{:.1R}], is greater than Maximum Slat Angle=[{:.1R}] deg.",
904 0 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle,
905 0 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle));
906 : }
907 :
908 : // Error if input slat angle not in input min/max range
909 :
910 3 : if (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle >
911 6 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle &&
912 3 : (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatAngle <
913 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle ||
914 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatAngle >
915 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle)) {
916 0 : errFlag = true;
917 0 : ShowSevereError(state, format("WindowMaterial:Blind=\"{}\", Illegal value combination.", state.dataMaterial->Blind(inBlindNumber).Name));
918 0 : ShowContinueError(state,
919 0 : format("Slat Angle=[{:.1R}] is outside of the input min/max range, min=[{:.1R}], max=[{:.1R}] deg.",
920 0 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).SlatAngle,
921 0 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle,
922 0 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle));
923 : }
924 :
925 : // Warning if input minimum slat angle is less than that allowed by slat geometry
926 :
927 3 : if (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle < MinSlatAngGeom) {
928 3 : ShowWarningError(state, format("WindowMaterial:Blind=\"{}\", Illegal value combination.", state.dataMaterial->Blind(inBlindNumber).Name));
929 6 : ShowContinueError(
930 : state,
931 6 : format("Minimum Slat Angle=[{:.1R}] is less than the smallest allowed by slat dimensions and spacing, min=[{:.1R}] deg.",
932 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle,
933 : MinSlatAngGeom));
934 3 : ShowContinueError(state, format("Minimum Slat Angle will be set to {:.1R} deg.", MinSlatAngGeom));
935 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle = MinSlatAngGeom;
936 : }
937 :
938 : // Warning if input maximum slat angle is greater than that allowed by slat geometry
939 :
940 3 : if (state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle > MaxSlatAngGeom) {
941 3 : ShowWarningError(state, format("WindowMaterial:Blind=\"{}\", Illegal value combination.", state.dataMaterial->Blind(inBlindNumber).Name));
942 6 : ShowContinueError(state,
943 6 : format("Maximum Slat Angle=[{:.1R}] is greater than the largest allowed by slat dimensions and spacing, [{:.1R}] deg.",
944 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle,
945 : MaxSlatAngGeom));
946 3 : ShowContinueError(state, format("Maximum Slat Angle will be set to {:.1R} deg.", MaxSlatAngGeom));
947 3 : state.dataMaterial->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle = MaxSlatAngGeom;
948 : }
949 : } else {
950 0 : outBlindNumber = Found;
951 : }
952 3 : }
953 :
954 44408 : Real64 ComputeNominalUwithConvCoeffs(EnergyPlusData &state,
955 : int const numSurf, // index for Surface array.
956 : bool &isValid // returns true if result is valid
957 : )
958 : {
959 :
960 : // SUBROUTINE INFORMATION:
961 : // AUTHOR Jason Glazer
962 : // DATE WRITTEN September 2013
963 : // MODIFIED na
964 : // RE-ENGINEERED na
965 :
966 : // PURPOSE OF THIS SUBROUTINE:
967 : // Calculate Nominal U-value with convection/film coefficients for reporting by
968 : // adding on prescribed R-values for interior and exterior convection coefficients
969 : // as found in ASHRAE 90.1-2004, Appendix A. Used in EIO and tabular reports.
970 : // ASHRAE 90.1-2004 Section A9.4.1 shows the following:
971 : // R-value Condition
972 : // All exterior conditions IP: 0.17 SI: 0.0299
973 : // All semi-exterior surfaces IP: 0.46 SI: 0.0810
974 : // Interior horizontal surfaces, heat flow up IP: 0.61 SI: 0.1074
975 : // Interior horizontal surfaces, heat flow down IP: 0.92 SI: 0.1620
976 : // Interior vertical surfaces IP: 0.68 SI: 0.1198
977 : // This section shows the same value in 90.1-2010 and 90.2-2010
978 : // Note that this report does not use the semi-exterior surface value because
979 : // EnergyPlus does not have a way to specifically tell whether or not a surface
980 : // is connected to a semi-exterior area of the building. Users can always use
981 : // the Nominal U-Value to manually calculated this. The values calculated here
982 : // are simply reported to the EIO file and not used for any calculations.
983 :
984 : // Return value
985 : Real64 NominalUwithConvCoeffs; // return value
986 :
987 : static constexpr std::array<Real64, static_cast<int>(DataSurfaces::SurfaceClass::Num)> filmCoefs = {
988 : 0.0, // None
989 : 0.1197548, // Wall
990 : 0.1620212, // Floor
991 : 0.1074271, // Roof
992 : 0.0, // IntMass
993 : 0.0, // Detached_B
994 : 0.0, // Detached_F
995 : 0.1197548, // Window
996 : 0.1197548, // GlassDoor
997 : 0.1197548, // Door
998 : 0.0, // Shading
999 : 0.0, // Overhang
1000 : 0.0, // Fin
1001 : 0.0, // TDD_Dome
1002 : 0.0 // TDD_Diffuser
1003 : }; // If anything added to the enum SurfaceClass, adjust this list appropriately
1004 :
1005 : Real64 insideFilm;
1006 : Real64 outsideFilm;
1007 :
1008 44408 : isValid = true;
1009 :
1010 44408 : auto &thisSurface = state.dataSurface->Surface(numSurf);
1011 :
1012 : // exterior conditions
1013 44408 : switch (thisSurface.ExtBoundCond) {
1014 17799 : case DataSurfaces::ExternalEnvironment: { // ExtBoundCond = 0
1015 17799 : outsideFilm = 0.0299387; // All exterior conditions
1016 17799 : } break;
1017 1 : case DataSurfaces::OtherSideCoefCalcExt: {
1018 1 : outsideFilm = state.dataSurface->OSC(thisSurface.OSCPtr).SurfFilmCoef;
1019 1 : } break;
1020 2510 : case DataSurfaces::Ground:
1021 : case DataSurfaces::OtherSideCoefNoCalcExt:
1022 : case DataSurfaces::OtherSideCondModeledExt:
1023 : case DataSurfaces::GroundFCfactorMethod:
1024 : case DataSurfaces::KivaFoundation: { // All these cases have a negative ExtBoundCond so don't use film coefficients
1025 2510 : outsideFilm = 0.0;
1026 2510 : } break;
1027 24098 : default: { // Interior Surface Attached to a Zone (ExtBoundCond is a surface)
1028 24098 : outsideFilm = filmCoefs[static_cast<int>(state.dataSurface->Surface(thisSurface.ExtBoundCond).Class)];
1029 24098 : } break;
1030 : }
1031 : // interior conditions and calculate the return value
1032 44408 : if (state.dataHeatBal->NominalU(thisSurface.Construction) > 0.0) {
1033 44390 : insideFilm = filmCoefs[static_cast<int>(thisSurface.Class)];
1034 44390 : if (insideFilm == 0.0) outsideFilm = 0.0;
1035 44390 : NominalUwithConvCoeffs =
1036 44390 : 1.0 / (insideFilm + (1.0 / state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction)) + outsideFilm);
1037 : } else {
1038 18 : isValid = false;
1039 18 : NominalUwithConvCoeffs = state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction);
1040 : }
1041 :
1042 44408 : return NominalUwithConvCoeffs;
1043 : }
1044 :
1045 796 : void SetFlagForWindowConstructionWithShadeOrBlindLayer(EnergyPlusData &state)
1046 : {
1047 :
1048 : // PURPOSE OF THIS SUBROUTINE:
1049 : // check fenestrations with shading control and set a flag to true if its construction has
1050 : // either shade or blind material layer
1051 :
1052 : // METHODOLOGY EMPLOYED:
1053 : // Loop through Surface and register any shading controls, and loop through the construction
1054 : // material layer
1055 :
1056 : // Using/Aliasing
1057 : using DataSurfaces::ExternalEnvironment;
1058 :
1059 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1060 796 : int loopSurfNum(0); // surface index
1061 796 : int ConstrNum(0); // construction index
1062 796 : int NumLayers(0); // number of material layers in a construction
1063 796 : int Layer(0); // construction material layer index
1064 796 : int MaterNum(0); // construction material index
1065 :
1066 46840 : for (loopSurfNum = 1; loopSurfNum <= state.dataSurface->TotSurfaces; ++loopSurfNum) {
1067 :
1068 46044 : if (state.dataSurface->Surface(loopSurfNum).Class != DataSurfaces::SurfaceClass::Window) continue;
1069 6217 : if (state.dataSurface->Surface(loopSurfNum).ExtBoundCond != ExternalEnvironment) continue;
1070 6203 : if (!state.dataSurface->Surface(loopSurfNum).HasShadeControl) continue;
1071 151 : if (state.dataSurface->Surface(loopSurfNum).activeShadedConstruction == 0) continue;
1072 :
1073 151 : ConstrNum = state.dataSurface->Surface(loopSurfNum).activeShadedConstruction;
1074 151 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
1075 151 : if (thisConstruct.TypeIsWindow) {
1076 151 : NumLayers = thisConstruct.TotLayers;
1077 610 : for (Layer = 1; Layer <= NumLayers; ++Layer) {
1078 459 : MaterNum = thisConstruct.LayerPoint(Layer);
1079 459 : if (MaterNum == 0) continue;
1080 459 : auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
1081 459 : if (thisMaterial->group == Material::Group::Shade || thisMaterial->group == Material::Group::WindowBlind)
1082 113 : state.dataSurface->SurfWinHasShadeOrBlindLayer(loopSurfNum) = true;
1083 : }
1084 : }
1085 : }
1086 796 : }
1087 :
1088 796 : void AllocateIntGains(EnergyPlusData &state)
1089 : {
1090 796 : state.dataHeatBal->ZoneIntGain.allocate(state.dataGlobal->NumOfZones);
1091 796 : state.dataHeatBal->spaceIntGain.allocate(state.dataGlobal->numSpaces);
1092 796 : state.dataHeatBal->spaceIntGainDevices.allocate(state.dataGlobal->numSpaces);
1093 796 : state.dataDayltg->spacePowerReductionFactor.dimension(state.dataGlobal->numSpaces, 1.0);
1094 796 : }
1095 :
1096 : } // namespace EnergyPlus::DataHeatBalance
|