diff --git a/doc/engineering-reference/src/climate-sky-and-solar-shading-calculations/shading-module.tex b/doc/engineering-reference/src/climate-sky-and-solar-shading-calculations/shading-module.tex index c33e71e85b3..7aeb04899d8 100644 --- a/doc/engineering-reference/src/climate-sky-and-solar-shading-calculations/shading-module.tex +++ b/doc/engineering-reference/src/climate-sky-and-solar-shading-calculations/shading-module.tex @@ -406,39 +406,93 @@ \subsubsection{Overlapping Shadows}\label{overlapping-shadows} \caption{Complex Overlapping Condition \protect \label{fig:complex-overlapping-condition}} \end{figure} -If two shadows overlap the receiving surface, they may also overlap each other as in Figure~\ref{fig:multiple-shadow-overlaps}. The vertices of this overlap can be computed.~ The areas of all overlaps can be computed.~ The total sunlit area can be expressed as the sum of all polygon areas given a proper sign on each of the areas. +If two shadows overlap the receiving surface, they may also overlap each other as in Figure~\ref{fig:multiple-shadow-overlaps}. The vertices of this overlap can be computed, and the areas of all overlaps can be computed. For opaque shadows, the total sunlit area can be expressed as the sum of all polygon areas using the sign convention shown in Table \ref{table:surface-area-characteristic-convention}: -The following convention was adopted: +\begin{equation} +SunlitArea = {\sum\limits_{i = 1}^n {A_i}} +\end{equation} -% table 24 -\begin{longtable}[c]{@{}ll@{}} -\caption{Surface / Area Characteristic / Convention \label{table:surface-area-characteristic-convention}} \tabularnewline +Each shadow's area is subtracted from the receiving surface and so on through multiple overlaps where the sign of the overlap area is the product of the signs of the overlapping areas. For the shadows in Figure~\ref{fig:multiple-shadow-overlaps}, start with the receiving surface area A, subtract shadow area B, then subtract shadow area C. Because shadows B and C overlap, the overlap area D has been subtracted twice, so add back area D to get the final sunlit area on the receiving surface. + + +\begin{longtable}[c]{>{\raggedright}p{2.9in}p{1.5in}p{1.59in}} +\caption{Overlapping Shadow Surface Area Convention \label{table:surface-area-characteristic-convention}} \tabularnewline \toprule -Surface Characteristic & Area Convention \tabularnewline +Surface Characteristic & Area Convention & Sunlit Area \tabularnewline \midrule \endfirsthead -\caption[]{Surface / Area Characteristic / Convention} \tabularnewline +\caption[]{Overlapping Shadow Surface Area Convention} \tabularnewline \toprule -Surface Characteristic & Area Convention \tabularnewline +Surface Characteristic & Area Convention & Sunlit Area \tabularnewline \midrule \endhead -receiving surface & positive (A) \tabularnewline -overlap between shadow and receiving & negative (B \& C) \tabularnewline -overlap between two shadows & positive (D) \tabularnewline +receiving surface & positive A & A \tabularnewline +overlap between shadow and receiving & negative B & A-B \tabularnewline +overlap between shadow and receiving & negative C & A-B-C \tabularnewline +overlap between two shadows & positive D & A-B-C+D \tabularnewline \bottomrule \end{longtable} -and so on through multiple overlaps where the sign of the overlap area is the product of the signs of the overlapping areas. - \begin{figure}[hbtp] % fig 47 \centering \includegraphics[width=0.9\textwidth, height=0.9\textheight, keepaspectratio=true]{media/image642.png} \caption{Multiple Shadow Overlaps \protect \label{fig:multiple-shadow-overlaps}} \end{figure} -Partially transparent shadowing surfaces can also be modeled by giving a transparency ($\tau$) to every shadowing polygon. Let $\tau$ of the receiving polygon be one. Then the $\tau$ of every overlap of polygons i and j is the product of $\tau$\(_{i}\)and $\tau$\(_{j}\). The shaded area is then computed by summing A\(_{i}\)*(1 - $\tau$\(_{i}\)) for all overlap polygons. +Partially transparent shadowing surfaces can also be modeled by giving a transparency ($\tau$) to every shadowing polygon. The sunlit area is computed by: + +\begin{equation} +SunlitArea = A_1 + {\sum\limits_{i = 2}^n {A_i}*(1-\tau_i)} +\end{equation} + +The actual $\tau$ of overlapping polygons i and j is the product of $\tau_i$ and $\tau_j$. For example, if a wall is fully shaded by two partially transparent shades, one with a transmittance of 0.8 and one with a transmittance of 0.5, the resulting sunlit fraction would be $0.8 * 0.5 = 0.4$. Because each shadow is first applied with its own transmittance, the ``transmittance'' for the overlap correction ($\tau_k$) in the sunlit area summation is derived from the individual surface transmittance values: + +\begin{equation} +\tau_k = (\tau_{i} + \tau_{j}) - (\tau_{i} * \tau_{j}) +\end{equation} + +where $\tau_k$ is the correction ``transmittance'' for the shadow overlap area (D in Figure~\ref{fig:multiple-shadow-overlaps}). + +For example, using the overlapping shadows in Figure~\ref{fig:multiple-shadow-overlaps}, the sunlit area can be calculated two ways as shown in Table~\ref{table:sunlit-area-calculation-overlapping-shadows}. Method 1 avoids any overlaps by calculating adjusted areas. Method 2 uses the EnergyPlus summation approach and shows the transmittance correction calculation for the overlap area to arrive at the same result. Stepping through Method 2: start with the full surface area A, subtract the shaded area $B*(1-\tau_b)$, subtract the shaded area $C*(1-\tau_c)$. At this point, the overlapping area D has been subtracted twice, once using $\tau_b$ and once using $\tau_c$. To correct for this, area D is added back in with the adjusted ``transmittance'' using the equation shown above for $\tau_k$. Once the sunlit area is known for each surface, the sunlit fraction is calculated, which is $13.56/20=0.678$ for this example. + +\begin{longtable}[c]{>{\raggedright}p{1.2in}p{1.5in}p{2.0in}p{2.0in}} +\caption{Sunlit Area Calculations with Overlapping Partially Transmitting Shadows\label{table:sunlit-area-calculation-overlapping-shadows}} \tabularnewline +\toprule +Region & Area & Transmittance & Sunlit Area \tabularnewline +\midrule +\endfirsthead + +\caption[]{Sunlit Area Calculations with Overlapping Partially Transmitting Shadows} \tabularnewline +\toprule +Region & Area & Transmittance & Sunlit Area \tabularnewline +\midrule +\endhead + +Assumptions: \tabularnewline +A & 20 & 1.0 \tabularnewline +B & 10 & 0.8 \tabularnewline +C & 8 & 0.4 \tabularnewline +D & 3 & \tabularnewline +\midrule +Method 1 & Adjusted Areas:\tabularnewline +$A-B-C+D$ & $20-10-8+3 = 5$ & $1.0$ & $5*1.0 = 5.00$ \tabularnewline +$B-D$ & $10-3=7$ & $0.8$ & $7*0.8 = 5.60$ \tabularnewline +$C-D$ & $8-3=5$ & $0.4$ & $5*0.4 = 2.00$ \tabularnewline +$D$ & $3$ & $0.8 * 0.4 = 0.32$ & $3*0.32 = 0.96$ \tabularnewline +Total & $20$ & & $13.56$ \tabularnewline +\midrule +Method 2 & Full Areas:\tabularnewline +$A$ & $20$ & n/a & $20.00$ \tabularnewline +$B$ & $-10$ & $0.8$ & $-10*(1-0.8) = -2.00$ \tabularnewline +$C$ & $-8$ & $0.4$ & $-8*(1-0.4) = -4.80$ \tabularnewline +$D$ & $3$ & $(0.8+0.4)-(0.8 * 0.4) = 0.88$ & $+3*(1-0.88) = 0.36$ \tabularnewline +Total & & & $13.56$ \tabularnewline +\bottomrule +\end{longtable} + + It is easy to determine the sunlit area of a window once all the shadow and overlap vertices on the wall have been computed. Consider wall 2 of Figure~\ref{fig:overall-shadowing-scheme-depiction}. First, the wall is considered a simple rectangle and the window on it is ignored. The shadow overlapping is performed and the sunlit portion of the gross wall area is computed. Then the window rectangle is overlapped with the shadow to determine its sunlit area. The sunlit area of the window is subtracted from the gross wall sunlit area to determine the net wall sunlit area. During this calculation it is not necessary to recompute the shadows, because they were precisely determined on the wall. diff --git a/src/EnergyPlus/SolarShading.cc b/src/EnergyPlus/SolarShading.cc index 949435eb689..bcb1ae1d925 100644 --- a/src/EnergyPlus/SolarShading.cc +++ b/src/EnergyPlus/SolarShading.cc @@ -4637,8 +4637,6 @@ void DeterminePolygonOverlap(EnergyPlusData &state, // SUBROUTINE INFORMATION: // AUTHOR Legacy Code - // DATE WRITTEN - // MODIFIED na // RE-ENGINEERED Lawrie, Oct 2000 // PURPOSE OF THIS SUBROUTINE: @@ -4667,20 +4665,11 @@ void DeterminePolygonOverlap(EnergyPlusData &state, // REFERENCES: // BLAST/IBLAST code, original author George Walton - // Using/Aliasing - - int N; // Loop index - int NV1; // Number of vertices of figure NS1 - int NV2; // Number of vertices of figure NS2 - int NV3; // Number of vertices of figure NS3 (the overlap of NS1 and NS2) - int NIN1; // Number of vertices of NS1 within NS2 - int NIN2; // Number of vertices of NS2 within NS1 - - // Check for exceeding array limits. #ifdef EP_Count_Calls ++state.dataTimingsData->NumDetPolyOverlap_Calls; #endif + // Check for exceeding array limits. if (NS3 > state.dataSolarShading->MaxHCS) { state.dataSolarShading->OverlapStatus = TooManyFigures; @@ -4705,9 +4694,11 @@ void DeterminePolygonOverlap(EnergyPlusData &state, } state.dataSolarShading->OverlapStatus = PartialOverlap; - NV1 = state.dataSolarShading->HCNV(NS1); - NV2 = state.dataSolarShading->HCNV(NS2); - NV3 = 0; + int NV1 = state.dataSolarShading->HCNV(NS1); // Number of vertices of figure NS1 + int NV2 = state.dataSolarShading->HCNV(NS2); // Number of vertices of figure NS2 + int NV3 = 0; // Number of vertices of figure NS3 (the overlap of NS1 and NS2) + int NIN1 = 0; // Number of vertices of NS1 within NS2 + int NIN2 = 0; // Number of vertices of NS2 within NS1 if (!state.dataSysVars->SutherlandHodgman) { INCLOS(state, NS1, NV1, NS2, NV2, NV3, NIN1); // Find vertices of NS1 within NS2. @@ -4740,14 +4731,14 @@ void DeterminePolygonOverlap(EnergyPlusData &state, CLIPPOLY(state, NS1, NS2, NV1, NV2, NV3); } - if (NV3 < state.dataSolarShading->MaxHCV && NS3 <= state.dataSolarShading->MaxHCS) { + if (NV3 < state.dataSolarShading->MaxHCV) { if (!state.dataSysVars->SutherlandHodgman) { ORDER(state, NV3, NS3); // Put vertices in clockwise order. } else { assert(equal_dimensions(state.dataSolarShading->HCX, state.dataSolarShading->HCY)); int l = state.dataSolarShading->HCX.index(NS3, 1); - for (N = 1; N <= NV3; ++N, ++l) { + for (int N = 1; N <= NV3; ++N, ++l) { state.dataSolarShading->HCX[l] = nint64(state.dataSolarShading->XTEMP(N)); // [ l ] == ( N, NS3 ) state.dataSolarShading->HCY[l] = nint64(state.dataSolarShading->YTEMP(N)); } @@ -4759,20 +4750,23 @@ void DeterminePolygonOverlap(EnergyPlusData &state, if (std::abs(state.dataSolarShading->HCAREA(NS3)) * HCMULT < std::abs(state.dataSolarShading->HCAREA(NS1))) { state.dataSolarShading->OverlapStatus = NoOverlap; } else { - if (state.dataSolarShading->HCAREA(NS1) * state.dataSolarShading->HCAREA(NS2) > 0.0) + if (state.dataSolarShading->HCAREA(NS1) * state.dataSolarShading->HCAREA(NS2) > 0.0) { state.dataSolarShading->HCAREA(NS3) = -state.dataSolarShading->HCAREA(NS3); // Determine sign of area of overlap - Real64 const HCT_1(state.dataSolarShading->HCT(NS1)); - Real64 const HCT_2(state.dataSolarShading->HCT(NS2)); - Real64 HCT_3(HCT_2 * HCT_1); // Determine transmission of overlap - if (HCT_2 >= 0.5 && HCT_1 >= 0.5) { - if (HCT_2 != 1.0 && HCT_1 != 1.0) { - HCT_3 = 1.0 - HCT_3; - } } - state.dataSolarShading->HCT(NS3) = HCT_3; + Real64 const HCT_1 = state.dataSolarShading->HCT(NS1); + Real64 const HCT_2 = state.dataSolarShading->HCT(NS2); + if (HCT_2 == 1.0 || HCT_1 == 1.0) { + state.dataSolarShading->HCT(NS3) = HCT_1 * HCT_2; + } else { + // Determine transmission of overlap which corrects for prior shadows + // The resulting transmission of overlapping shadows is HCT_1 * HCT_2 + // Shadows with HCT_1 and HCT_2 have already been applied in the overlapping area + // so the correction is the difference between (HCT_1+HCT_2) and (HCT_1*HCT_2) + state.dataSolarShading->HCT(NS3) = (HCT_1 + HCT_2) - HCT_1 * HCT_2; + } } - } else if (NV3 > state.dataSolarShading->MaxHCV) { + } else { state.dataSolarShading->OverlapStatus = TooManyVertices; @@ -4791,26 +4785,6 @@ void DeterminePolygonOverlap(EnergyPlusData &state, state.dataSolarShading->TrackTooManyVertices(state.dataSolarShading->NumTooManyVertices).SurfIndex2 = state.dataSolarShading->CurrentSurfaceBeingShadowed; } - - } else if (NS3 > state.dataSolarShading->MaxHCS) { - - state.dataSolarShading->OverlapStatus = TooManyFigures; - - if (!state.dataSolarShading->TooManyFiguresMessage && !state.dataGlobal->DisplayExtraWarnings) { - ShowWarningError(state, - format("DeterminePolygonOverlap: Too many figures [>{}] detected in an overlap calculation. Use " - "Output:Diagnostics,DisplayExtraWarnings; for more details.", - state.dataSolarShading->MaxHCS)); - state.dataSolarShading->TooManyFiguresMessage = true; - } - - if (state.dataGlobal->DisplayExtraWarnings) { - state.dataSolarShading->TrackTooManyFigures.redimension(++state.dataSolarShading->NumTooManyFigures); - state.dataSolarShading->TrackTooManyFigures(state.dataSolarShading->NumTooManyFigures).SurfIndex1 = - state.dataSolarShading->CurrentShadowingSurface; - state.dataSolarShading->TrackTooManyFigures(state.dataSolarShading->NumTooManyFigures).SurfIndex2 = - state.dataSolarShading->CurrentSurfaceBeingShadowed; - } } } diff --git a/src/EnergyPlus/SolarShading.hh b/src/EnergyPlus/SolarShading.hh index 202a7793b4f..54dd9c08ca3 100644 --- a/src/EnergyPlus/SolarShading.hh +++ b/src/EnergyPlus/SolarShading.hh @@ -371,18 +371,18 @@ struct SolarShadingData : BaseGlobalStruct Array1D SurfMultCircumSolar; // Contribution to eff sky view factor from circumsolar brightening Array1D SurfMultHorizonZenith; // Contribution to eff sky view factor from horizon or zenith brightening - int FBKSHC; // HC location of first back surface - int FGSSHC; // HC location of first general shadowing surface - int FINSHC; // HC location of first back surface overlap - int FRVLHC; // HC location of first reveal surface - int FSBSHC; // HC location of first subsurface + int FBKSHC = 0; // HC location of first back surface + int FGSSHC = 0; // HC location of first general shadowing surface + int FINSHC = 0; // HC location of first back surface overlap + int FRVLHC = 0; // HC location of first reveal surface + int FSBSHC = 0; // HC location of first subsurface int LOCHCA = 0; // Location of highest data in the HC arrays - int NBKSHC; // Number of back surfaces in the HC arrays - int NGSSHC; // Number of general shadowing surfaces in the HC arrays - int NINSHC; // Number of back surface overlaps in the HC arrays - int NRVLHC; // Number of reveal surfaces in HC array - int NSBSHC; // Number of subsurfaces in the HC arrays - bool CalcSkyDifShading; // True when sky diffuse solar shading is + int NBKSHC = 0; // Number of back surfaces in the HC arrays + int NGSSHC = 0; // Number of general shadowing surfaces in the HC arrays + int NINSHC = 0; // Number of back surface overlaps in the HC arrays + int NRVLHC = 0; // Number of reveal surfaces in HC array + int NSBSHC = 0; // Number of subsurfaces in the HC arrays + bool CalcSkyDifShading = false; // True when sky diffuse solar shading is int ShadowingCalcFrequency = 0; // Frequency for Shadowing Calculations int ShadowingDaysLeft = 0; // Days left in current shadowing period diff --git a/tst/EnergyPlus/unit/SolarShading.unit.cc b/tst/EnergyPlus/unit/SolarShading.unit.cc index f3136f68803..8c63b304c33 100644 --- a/tst/EnergyPlus/unit/SolarShading.unit.cc +++ b/tst/EnergyPlus/unit/SolarShading.unit.cc @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -3973,3 +3974,1055 @@ TEST_F(EnergyPlusFixture, ShadowCalculation_CSV) EXPECT_EQ(expected_values, stream_str); } } + +TEST_F(EnergyPlusFixture, SolarShadingTest_PolygonOverlap) +{ + std::string const idf_objects = delimited_string({ + " Building,", + " DemoFDT, !- Name", + " 0, !- North Axis {deg}", + " Suburbs, !- Terrain", + " 3.9999999E-02, !- Loads Convergence Tolerance Value", + " 4.0000002E-03, !- Temperature Convergence Tolerance Value {deltaC}", + " FullExterior; !- Solar Distribution", + " ShadowCalculation,", + " PolygonClipping, !- Shading Calculation Method", + " Timestep, !- Shading Calculation Update Frequency Method", + " , !- Shading Calculation Update Frequency", + " , !- Maximum Figures in Shadow Overlap Calculations", + " , !- Polygon Clipping Algorithm", + " , !- Pixel Counting Resolution", + " DetailedSkyDiffuseModeling; !- Sky Diffuse Modeling Algorithm", + " Timestep,6;", + " Site:Location,", + " CHICAGO_IL_USA TMY2-94846, !- Name", + " 42.00, !- Latitude {deg}", + " -87.88, !- Longitude {deg}", + " -6.00, !- Time Zone {hr}", + " 190.00; !- Elevation {m}", + " Material,", + " C12 - 2 IN HW CONCRETE, !- Name", + " MediumRough, !- Roughness", + " 5.0901599E-02, !- Thickness {m}", + " 1.729577, !- Conductivity {W/m-K}", + " 2242.585, !- Density {kg/m3}", + " 836.8000, !- Specific Heat {J/kg-K}", + " 0.9000000, !- Thermal Absorptance", + " 0.6500000, !- Solar Absorptance", + " 0.6500000; !- Visible Absorptance", + " WindowMaterial:Glazing,", + " GLASS - CLEAR PLATE 1 / 4 IN, !- Name", + " SpectralAverage, !- Optical Data Type", + " , !- Window Glass Spectral Data Set Name", + " 0.006, !- Thickness {m}", + " 0.80, !- Solar Transmittance at Normal Incidence", + " 0.10, !- Front Side Solar Reflectance at Normal Incidence", + " 0.10, !- Back Side Solar Reflectance at Normal Incidence", + " 0.80, !- Visible Transmittance at Normal Incidence", + " 0.10, !- Front Side Visible Reflectance at Normal Incidence", + " 0.10, !- Back Side Visible Reflectance at Normal Incidence", + " 0.0, !- Infrared Transmittance at Normal Incidence", + " 0.84, !- Front Side Infrared Hemispherical Emissivity", + " 0.84, !- Back Side Infrared Hemispherical Emissivity", + " 0.9; !- Conductivity {W/m-K}", + " Construction,", + " Surfaces, !- Name", + " C12 - 2 IN HW CONCRETE; !- Layer 4", + " Construction,", + " Single PANE HW WINDOW, !- Name", + " GLASS - CLEAR PLATE 1 / 4 IN; !- Outside Layer", + " GlobalGeometryRules,", + " UpperLeftCorner, !- Starting Vertex Position", + " Counterclockwise, !- Vertex Entry Direction", + " Relative; !- Coordinate System", + " Zone,", + " ZONE ONE; !- Name", + " FenestrationSurface:Detailed,", + " Zn001:Wall-South:Win001, !- Name", + " Window, !- Surface Type", + " Single PANE HW WINDOW, !- Construction Name", + " Zn001:Wall-South, !- Building Surface Name", + " , !- Outside Boundary Condition Object", + " 0.5000000, !- View Factor to Ground", + " TestFrameAndDivider, !- Frame and Divider Name", + " 1.0, !- Multiplier", + " 4, !- Number of Vertices", + " -3,-5,2.5, !- X,Y,Z ==> Vertex 1 {m}", + " -3,-5,0.5, !- X,Y,Z ==> Vertex 2 {m}", + " 3,-5,0.5, !- X,Y,Z ==> Vertex 3 {m}", + " 3,-5,2.5; !- X,Y,Z ==> Vertex 4 {m}", + " WindowProperty:FrameAndDivider,", + " TestFrameAndDivider, !- Name", + " 0.05, !- Frame Width {m}", + " 0.05, !- Frame Outside Projection {m}", + " 0.05, !- Frame Inside Projection {m}", + " 5.0, !- Frame Conductance {W/m2-K}", + " 1.2, !- Ratio of Frame-Edge Glass Conductance to Center-Of-Gl", + " 0.8, !- Frame Solar Absorptance", + " 0.8, !- Frame Visible Absorptance", + " 0.9, !- Frame Thermal Hemispherical Emissivity", + " DividedLite, !- Divider Type", + " 0.02, !- Divider Width {m}", + " 2, !- Number of Horizontal Dividers", + " 2, !- Number of Vertical Dividers", + " 0.02, !- Divider Outside Projection {m}", + " 0.02, !- Divider Inside Projection {m}", + " 5.0, !- Divider Conductance {W/m2-K}", + " 1.2, !- Ratio of Divider-Edge Glass Conductance to Center-Of-", + " 0.8, !- Divider Solar Absorptance", + " 0.8, !- Divider Visible Absorptance", + " 0.9; !- Divider Thermal Hemispherical Emissivity", + " ScheduleTypeLimits,", + " Fraction, !- Name", + " 0.0, !- Lower Limit Value", + " 1.0, !- Upper Limit Value", + " Continuous; !- Numeric Type", + " BuildingSurface:Detailed,", + " Zn001:floor, !- Name", + " Floor, !- Surface Type", + " Surfaces, !- Construction Name", + " ZONE ONE, !- Zone Name", + " , !- Space Name", + " Outdoors, !- Outside Boundary Condition", + " , !- Outside Boundary Condition Object", + " SunExposed, !- Sun Exposure", + " WindExposed, !- Wind Exposure", + " 0.0000000, !- View Factor to Ground", + " 4, !- Number of Vertices", + " -5,5,0, !- X,Y,Z ==> Vertex 1 {m}", + " 5,5,0, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-5,0, !- X,Y,Z ==> Vertex 3 {m}", + " -5,-5,0; !- X,Y,Z ==> Vertex 4 {m}", + " BuildingSurface:Detailed,", + " Zn001:Wall-South, !- Name", + " Wall, !- Surface Type", + " Surfaces, !- Construction Name", + " ZONE ONE, !- Zone Name", + " , !- Space Name", + " Outdoors, !- Outside Boundary Condition", + " , !- Outside Boundary Condition Object", + " SunExposed, !- Sun Exposure", + " WindExposed, !- Wind Exposure", + " 0.5000000, !- View Factor to Ground", + " 4, !- Number of Vertices", + " -5,-5,3, !- X,Y,Z ==> Vertex 1 {m}", + " -5,-5,0, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-5,0, !- X,Y,Z ==> Vertex 3 {m}", + " 5,-5,3; !- X,Y,Z ==> Vertex 4 {m}", + " Shading:Zone:Detailed,", + " Zn001:Wall-South:Shade001, !- Name", + " Zn001:Wall-South, !- Base Surface Name", + " OverhangTransmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -3,-5,2.5, !- X,Y,Z ==> Vertex 1 {m}", + " -3,-6,2.5, !- X,Y,Z ==> Vertex 2 {m}", + " 3,-6,2.5, !- X,Y,Z ==> Vertex 3 {m}", + " 3,-5,2.5; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " OverhangTransmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.5, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + " Shading:Building:Detailed,", + " Zn001:Wall-South:Tree, !- Name", + " TreeTransmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -3,-8,10, !- X,Y,Z ==> Vertex 1 {m}", + " -5,-8,2, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-8,2, !- X,Y,Z ==> Vertex 3 {m}", + " 3,-8,10; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " TreeTransmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.8, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + }); + + ASSERT_TRUE(process_idf(idf_objects)); + + SimulationManager::GetProjectData(*state); + bool FoundError = false; + + HeatBalanceManager::GetProjectControlData(*state, FoundError); // read project control data + EXPECT_FALSE(FoundError); // expect no errors + + HeatBalanceManager::SetPreConstructionInputParameters(*state); + ScheduleManager::ProcessScheduleInput(*state); // read schedules + + Material::GetMaterialData(*state, FoundError); + EXPECT_FALSE(FoundError); + + HeatBalanceManager::GetFrameAndDividerData(*state); + + HeatBalanceManager::GetConstructData(*state, FoundError); + EXPECT_FALSE(FoundError); + + HeatBalanceManager::GetZoneData(*state, FoundError); // Read Zone data from input file + EXPECT_FALSE(FoundError); + + SurfaceGeometry::GetGeometryParameters(*state, FoundError); + EXPECT_FALSE(FoundError); + + WeatherManager::GetLocationInfo(*state, FoundError); + EXPECT_FALSE(FoundError); + + WeatherManager::CheckLocationValidity(*state); + + state->dataSurfaceGeometry->CosZoneRelNorth.allocate(1); + state->dataSurfaceGeometry->SinZoneRelNorth.allocate(1); + + state->dataSurfaceGeometry->CosZoneRelNorth(1) = std::cos(-state->dataHeatBal->Zone(1).RelNorth * Constant::DegToRadians); + state->dataSurfaceGeometry->SinZoneRelNorth(1) = std::sin(-state->dataHeatBal->Zone(1).RelNorth * Constant::DegToRadians); + state->dataSurfaceGeometry->CosBldgRelNorth = 1.0; + state->dataSurfaceGeometry->SinBldgRelNorth = 0.0; + + SurfaceGeometry::GetSurfaceData(*state, FoundError); // setup zone geometry and get zone data + EXPECT_FALSE(FoundError); // expect no errors + + // compare_err_stream( "" ); // just for debugging + + SurfaceGeometry::SetupZoneGeometry(*state, FoundError); // this calls GetSurfaceData() + EXPECT_FALSE(FoundError); + + SolarShading::AllocateModuleArrays(*state); + SolarShading::DetermineShadowingCombinations(*state); + + // compare_err_stream( "" ); // just for debugging + + state->dataSurface->ShadingTransmittanceVaries = true; + state->dataHeatBal->SolarDistribution = DataHeatBalance::Shadowing::FullExterior; + + // Set up solar for Jan 1 + state->dataEnvrn->DayOfYear_Schedule = 1; // Jan 1 + state->dataEnvrn->DayOfYear = 1; // Jan 1 + state->dataEnvrn->DayOfWeek = 6; + state->dataGlobal->TimeStep = 4; + state->dataGlobal->HourOfDay = 12; + state->dataGlobal->BeginSimFlag = true; + state->dataGlobal->BeginEnvrnFlag = true; + SolarShading::InitSolarCalculations(*state); + state->dataSolarShading->CalcSkyDifShading = true; + SolarShading::SkyDifSolarShading(*state); + state->dataSolarShading->CalcSkyDifShading = false; + state->dataGlobal->BeginSimFlag = false; + state->dataGlobal->BeginEnvrnFlag = false; + HeatBalanceIntRadExchange::InitSolarViewFactors(*state); // prevents crash in GetDaylightingParametersInput + SolarShading::PerformSolarCalculations(*state); + + // Get surface nums + int winSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:WIN001", state->dataSurface->Surface); + int wallSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH", state->dataSurface->Surface); + int overhangSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:SHADE001", state->dataSurface->Surface); + int treeSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:TREE", state->dataSurface->Surface); + + // Get shading surface schedule indexes + int overhangSchedNum = state->dataSurface->Surface(overhangSurfNum).SchedShadowSurfIndex; + int treeSchedNum = state->dataSurface->Surface(treeSurfNum).SchedShadowSurfIndex; + + // Use EMS to turn shading surfaces on and off + auto &overhangSchedEMSOn = state->dataScheduleMgr->Schedule(overhangSchedNum).EMSActuatedOn; + auto &treeSchedEMSOn = state->dataScheduleMgr->Schedule(treeSchedNum).EMSActuatedOn; + auto &overhangSchedEMSValue = state->dataScheduleMgr->Schedule(overhangSchedNum).EMSValue; + auto &treeSchedEMSValue = state->dataScheduleMgr->Schedule(treeSchedNum).EMSValue; + + // Overhang transparent, Tree transparent + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 1.0; + treeSchedEMSValue = 1.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + EXPECT_NEAR(1.0, state->dataSurface->SurfSunlitFrac(wallSurfNum), 0.0001); + EXPECT_NEAR(1.0, state->dataSurface->SurfSunlitFrac(winSurfNum), 0.0001); + + // Overhang opaque, Tree transparent + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 0.0; + treeSchedEMSValue = 1.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracOverhangOnly = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracOverhangOnly = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.99915, wallSunLitFracOverhangOnly, 0.0001); + EXPECT_NEAR(0.76955, winSunLitFracOverhangOnly, 0.0001); + + // Overhang tranparent, Tree opaque + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 1.0; + treeSchedEMSValue = 0.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracTreeOnly = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracTreeOnly = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.38584, wallSunLitFracTreeOnly, 0.0001); + EXPECT_NEAR(0.05484, winSunLitFracTreeOnly, 0.0001); + + // Overhang opaque, Tree opaque + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 0.0; + treeSchedEMSValue = 0.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracOverhangTree = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracOverhangTree = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.38584, wallSunLitFracOverhangTree, 0.0001); + EXPECT_NEAR(0.05484, winSunLitFracOverhangTree, 0.0001); + + // Use the base transmittance schedules (no EMS override) + // Overhang transmittance = 0.5, Tree transmittance = 0.8 + overhangSchedEMSOn = false; + treeSchedEMSOn = false; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracOverhangTreePartial = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracOverhangTreePartial = state->dataSurface->SurfSunlitFrac(winSurfNum); + + // Expected results - wall + Real64 treeShadowFrac = (1 - wallSunLitFracTreeOnly); + Real64 overhangShadowFrac = (1 - wallSunLitFracOverhangOnly); + Real64 combinedShadowFrac = (1 - wallSunLitFracOverhangTree); + Real64 overlapShdowFrac = treeShadowFrac + overhangShadowFrac - combinedShadowFrac; + Real64 treeOnlyShadowFrac = max(0.0, treeShadowFrac - overlapShdowFrac); + Real64 overhangOnlyShadowFrac = max(0.0, overhangShadowFrac - overlapShdowFrac); + Real64 expWallSunlitFrac = + 1.0 - ((1.0 - 0.5) * overhangOnlyShadowFrac) - ((1.0 - 0.8) * treeOnlyShadowFrac) - ((1.0 - 0.5 * 0.8) * overlapShdowFrac); + EXPECT_NEAR(expWallSunlitFrac, wallSunLitFracOverhangTreePartial, 0.0001); + + // Expected results - window + treeShadowFrac = (1 - winSunLitFracTreeOnly); + overhangShadowFrac = (1 - winSunLitFracOverhangOnly); + combinedShadowFrac = (1 - winSunLitFracOverhangTree); + overlapShdowFrac = treeShadowFrac + overhangShadowFrac - combinedShadowFrac; + treeOnlyShadowFrac = max(0.0, treeShadowFrac - overlapShdowFrac); + overhangOnlyShadowFrac = max(0.0, overhangShadowFrac - overlapShdowFrac); + Real64 expWinSunlitFrac = + 1.0 - ((1.0 - 0.5) * overhangOnlyShadowFrac) - ((1.0 - 0.8) * treeOnlyShadowFrac) - ((1.0 - 0.5 * 0.8) * overlapShdowFrac); + EXPECT_NEAR(expWinSunlitFrac, winSunLitFracOverhangTreePartial, 0.0001); + + // Set up solar for Jul 31 + state->dataEnvrn->DayOfYear_Schedule = 213; // Jul 31 + state->dataEnvrn->DayOfYear = 213; // Jul 31 + state->dataEnvrn->DayOfWeek = 6; + state->dataGlobal->TimeStep = 1; + state->dataGlobal->HourOfDay = 11; + SolarShading::PerformSolarCalculations(*state); + state->dataSolarShading->CalcSkyDifShading = false; + + // Overhang transparent, Tree transparent + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 1.0; + treeSchedEMSValue = 1.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + EXPECT_NEAR(1.0, state->dataSurface->SurfSunlitFrac(wallSurfNum), 0.0001); + EXPECT_NEAR(1.0, state->dataSurface->SurfSunlitFrac(winSurfNum), 0.0001); + + // Overhang opaque, Tree transparent + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 0.0; + treeSchedEMSValue = 1.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + wallSunLitFracOverhangOnly = state->dataSurface->SurfSunlitFrac(wallSurfNum); + winSunLitFracOverhangOnly = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.77626, wallSunLitFracOverhangOnly, 0.0001); + EXPECT_NEAR(0.08560, winSunLitFracOverhangOnly, 0.0001); + + // Overhang tranparent, Tree opaque + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 1.0; + treeSchedEMSValue = 0.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + wallSunLitFracTreeOnly = state->dataSurface->SurfSunlitFrac(wallSurfNum); + winSunLitFracTreeOnly = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.64961, wallSunLitFracTreeOnly, 0.0001); + EXPECT_NEAR(0.60655, winSunLitFracTreeOnly, 0.0001); + + // Overhang opaque, Tree opaque + overhangSchedEMSOn = true; + treeSchedEMSOn = true; + overhangSchedEMSValue = 0.0; + treeSchedEMSValue = 0.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + wallSunLitFracOverhangTree = state->dataSurface->SurfSunlitFrac(wallSurfNum); + winSunLitFracOverhangTree = state->dataSurface->SurfSunlitFrac(winSurfNum); + + EXPECT_NEAR(0.58989, wallSunLitFracOverhangTree, 0.0001); + EXPECT_NEAR(0.08560, winSunLitFracOverhangTree, 0.0001); + + // Use the base transmittance schedules (no EMS override) + // Overhang transmittance = 0.5, Tree transmittance = 0.8 + overhangSchedEMSOn = false; + treeSchedEMSOn = false; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + wallSunLitFracOverhangTreePartial = state->dataSurface->SurfSunlitFrac(wallSurfNum); + winSunLitFracOverhangTreePartial = state->dataSurface->SurfSunlitFrac(winSurfNum); + + // Expected results - wall + treeShadowFrac = (1 - wallSunLitFracTreeOnly); + overhangShadowFrac = (1 - wallSunLitFracOverhangOnly); + combinedShadowFrac = (1 - wallSunLitFracOverhangTree); + overlapShdowFrac = treeShadowFrac + overhangShadowFrac - combinedShadowFrac; + treeOnlyShadowFrac = max(0.0, treeShadowFrac - overlapShdowFrac); + overhangOnlyShadowFrac = max(0.0, overhangShadowFrac - overlapShdowFrac); + expWallSunlitFrac = 1.0 - ((1.0 - 0.5) * overhangOnlyShadowFrac) - ((1.0 - 0.8) * treeOnlyShadowFrac) - ((1.0 - 0.5 * 0.8) * overlapShdowFrac); + EXPECT_NEAR(expWallSunlitFrac, wallSunLitFracOverhangTreePartial, 0.0001); + + // Expected results - window + treeShadowFrac = (1 - winSunLitFracTreeOnly); + overhangShadowFrac = (1 - winSunLitFracOverhangOnly); + combinedShadowFrac = (1 - winSunLitFracOverhangTree); + overlapShdowFrac = treeShadowFrac + overhangShadowFrac - combinedShadowFrac; + treeOnlyShadowFrac = max(0.0, treeShadowFrac - overlapShdowFrac); + overhangOnlyShadowFrac = max(0.0, overhangShadowFrac - overlapShdowFrac); + expWinSunlitFrac = 1.0 - ((1.0 - 0.5) * overhangOnlyShadowFrac) - ((1.0 - 0.8) * treeOnlyShadowFrac) - ((1.0 - 0.5 * 0.8) * overlapShdowFrac); + EXPECT_NEAR(expWinSunlitFrac, winSunLitFracOverhangTreePartial, 0.0001); +} +TEST_F(EnergyPlusFixture, SolarShadingTest_PolygonOverlap2) +// Tests complete overlap of shadows which completely shade the test surfaces +{ + std::string const idf_objects = delimited_string({ + " Building,", + " DemoFDT, !- Name", + " 0, !- North Axis {deg}", + " Suburbs, !- Terrain", + " 3.9999999E-02, !- Loads Convergence Tolerance Value", + " 4.0000002E-03, !- Temperature Convergence Tolerance Value {deltaC}", + " FullExterior; !- Solar Distribution", + " ShadowCalculation,", + " PolygonClipping, !- Shading Calculation Method", + " Timestep, !- Shading Calculation Update Frequency Method", + " , !- Shading Calculation Update Frequency", + " , !- Maximum Figures in Shadow Overlap Calculations", + " , !- Polygon Clipping Algorithm", + " , !- Pixel Counting Resolution", + " DetailedSkyDiffuseModeling; !- Sky Diffuse Modeling Algorithm", + " Timestep,6;", + " Site:Location,", + " CHICAGO_IL_USA TMY2-94846, !- Name", + " 42.00, !- Latitude {deg}", + " -87.88, !- Longitude {deg}", + " -6.00, !- Time Zone {hr}", + " 190.00; !- Elevation {m}", + " Material,", + " C12 - 2 IN HW CONCRETE, !- Name", + " MediumRough, !- Roughness", + " 5.0901599E-02, !- Thickness {m}", + " 1.729577, !- Conductivity {W/m-K}", + " 2242.585, !- Density {kg/m3}", + " 836.8000, !- Specific Heat {J/kg-K}", + " 0.9000000, !- Thermal Absorptance", + " 0.6500000, !- Solar Absorptance", + " 0.6500000; !- Visible Absorptance", + " WindowMaterial:Glazing,", + " GLASS - CLEAR PLATE 1 / 4 IN, !- Name", + " SpectralAverage, !- Optical Data Type", + " , !- Window Glass Spectral Data Set Name", + " 0.006, !- Thickness {m}", + " 0.80, !- Solar Transmittance at Normal Incidence", + " 0.10, !- Front Side Solar Reflectance at Normal Incidence", + " 0.10, !- Back Side Solar Reflectance at Normal Incidence", + " 0.80, !- Visible Transmittance at Normal Incidence", + " 0.10, !- Front Side Visible Reflectance at Normal Incidence", + " 0.10, !- Back Side Visible Reflectance at Normal Incidence", + " 0.0, !- Infrared Transmittance at Normal Incidence", + " 0.84, !- Front Side Infrared Hemispherical Emissivity", + " 0.84, !- Back Side Infrared Hemispherical Emissivity", + " 0.9; !- Conductivity {W/m-K}", + " Construction,", + " Surfaces, !- Name", + " C12 - 2 IN HW CONCRETE; !- Layer 4", + " Construction,", + " Single PANE HW WINDOW, !- Name", + " GLASS - CLEAR PLATE 1 / 4 IN; !- Outside Layer", + " GlobalGeometryRules,", + " UpperLeftCorner, !- Starting Vertex Position", + " Counterclockwise, !- Vertex Entry Direction", + " Relative; !- Coordinate System", + " Zone,", + " ZONE ONE; !- Name", + " FenestrationSurface:Detailed,", + " Zn001:Wall-South:Win001, !- Name", + " Window, !- Surface Type", + " Single PANE HW WINDOW, !- Construction Name", + " Zn001:Wall-South, !- Building Surface Name", + " , !- Outside Boundary Condition Object", + " 0.5000000, !- View Factor to Ground", + " TestFrameAndDivider, !- Frame and Divider Name", + " 1.0, !- Multiplier", + " 4, !- Number of Vertices", + " -3,-5,2.5, !- X,Y,Z ==> Vertex 1 {m}", + " -3,-5,0.5, !- X,Y,Z ==> Vertex 2 {m}", + " 3,-5,0.5, !- X,Y,Z ==> Vertex 3 {m}", + " 3,-5,2.5; !- X,Y,Z ==> Vertex 4 {m}", + " WindowProperty:FrameAndDivider,", + " TestFrameAndDivider, !- Name", + " 0.05, !- Frame Width {m}", + " 0.05, !- Frame Outside Projection {m}", + " 0.05, !- Frame Inside Projection {m}", + " 5.0, !- Frame Conductance {W/m2-K}", + " 1.2, !- Ratio of Frame-Edge Glass Conductance to Center-Of-Gl", + " 0.8, !- Frame Solar Absorptance", + " 0.8, !- Frame Visible Absorptance", + " 0.9, !- Frame Thermal Hemispherical Emissivity", + " DividedLite, !- Divider Type", + " 0.02, !- Divider Width {m}", + " 2, !- Number of Horizontal Dividers", + " 2, !- Number of Vertical Dividers", + " 0.02, !- Divider Outside Projection {m}", + " 0.02, !- Divider Inside Projection {m}", + " 5.0, !- Divider Conductance {W/m2-K}", + " 1.2, !- Ratio of Divider-Edge Glass Conductance to Center-Of-", + " 0.8, !- Divider Solar Absorptance", + " 0.8, !- Divider Visible Absorptance", + " 0.9; !- Divider Thermal Hemispherical Emissivity", + " ScheduleTypeLimits,", + " Fraction, !- Name", + " 0.0, !- Lower Limit Value", + " 1.0, !- Upper Limit Value", + " Continuous; !- Numeric Type", + " BuildingSurface:Detailed,", + " Zn001:floor, !- Name", + " Floor, !- Surface Type", + " Surfaces, !- Construction Name", + " ZONE ONE, !- Zone Name", + " , !- Space Name", + " Outdoors, !- Outside Boundary Condition", + " , !- Outside Boundary Condition Object", + " SunExposed, !- Sun Exposure", + " WindExposed, !- Wind Exposure", + " 0.0000000, !- View Factor to Ground", + " 4, !- Number of Vertices", + " -5,5,0, !- X,Y,Z ==> Vertex 1 {m}", + " 5,5,0, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-5,0, !- X,Y,Z ==> Vertex 3 {m}", + " -5,-5,0; !- X,Y,Z ==> Vertex 4 {m}", + " BuildingSurface:Detailed,", + " Zn001:Wall-South, !- Name", + " Wall, !- Surface Type", + " Surfaces, !- Construction Name", + " ZONE ONE, !- Zone Name", + " , !- Space Name", + " Outdoors, !- Outside Boundary Condition", + " , !- Outside Boundary Condition Object", + " SunExposed, !- Sun Exposure", + " WindExposed, !- Wind Exposure", + " 0.5000000, !- View Factor to Ground", + " 4, !- Number of Vertices", + " -5,-5,3, !- X,Y,Z ==> Vertex 1 {m}", + " -5,-5,0, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-5,0, !- X,Y,Z ==> Vertex 3 {m}", + " 5,-5,3; !- X,Y,Z ==> Vertex 4 {m}", + " Shading:Building:Detailed,", + " Zn001:Wall-South:Shade1, !- Name", + " Shade1Transmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -10,-8,20, !- X,Y,Z ==> Vertex 1 {m}", + " -10,-8,0, !- X,Y,Z ==> Vertex 2 {m}", + " 10,-8,0, !- X,Y,Z ==> Vertex 3 {m}", + " 10,-8,20; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " Shade1Transmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.5, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + " Shading:Building:Detailed,", + " Zn001:Wall-South:Shade2, !- Name", + " Shade2Transmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -10,-9,20, !- X,Y,Z ==> Vertex 1 {m}", + " -10,-9,0, !- X,Y,Z ==> Vertex 2 {m}", + " 10,-9,0, !- X,Y,Z ==> Vertex 3 {m}", + " 10,-9,20; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " Shade2Transmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.8, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + }); + + ASSERT_TRUE(process_idf(idf_objects)); + + SimulationManager::GetProjectData(*state); + bool FoundError = false; + + HeatBalanceManager::GetProjectControlData(*state, FoundError); // read project control data + EXPECT_FALSE(FoundError); // expect no errors + + HeatBalanceManager::SetPreConstructionInputParameters(*state); + ScheduleManager::ProcessScheduleInput(*state); // read schedules + + Material::GetMaterialData(*state, FoundError); + EXPECT_FALSE(FoundError); + + HeatBalanceManager::GetFrameAndDividerData(*state); + + HeatBalanceManager::GetConstructData(*state, FoundError); + EXPECT_FALSE(FoundError); + + HeatBalanceManager::GetZoneData(*state, FoundError); // Read Zone data from input file + EXPECT_FALSE(FoundError); + + SurfaceGeometry::GetGeometryParameters(*state, FoundError); + EXPECT_FALSE(FoundError); + + WeatherManager::GetLocationInfo(*state, FoundError); + EXPECT_FALSE(FoundError); + + WeatherManager::CheckLocationValidity(*state); + + state->dataSurfaceGeometry->CosZoneRelNorth.allocate(1); + state->dataSurfaceGeometry->SinZoneRelNorth.allocate(1); + + state->dataSurfaceGeometry->CosZoneRelNorth(1) = std::cos(-state->dataHeatBal->Zone(1).RelNorth * Constant::DegToRadians); + state->dataSurfaceGeometry->SinZoneRelNorth(1) = std::sin(-state->dataHeatBal->Zone(1).RelNorth * Constant::DegToRadians); + state->dataSurfaceGeometry->CosBldgRelNorth = 1.0; + state->dataSurfaceGeometry->SinBldgRelNorth = 0.0; + + SurfaceGeometry::GetSurfaceData(*state, FoundError); // setup zone geometry and get zone data + EXPECT_FALSE(FoundError); // expect no errors + + // compare_err_stream( "" ); // just for debugging + + SurfaceGeometry::SetupZoneGeometry(*state, FoundError); // this calls GetSurfaceData() + EXPECT_FALSE(FoundError); + + SolarShading::AllocateModuleArrays(*state); + SolarShading::DetermineShadowingCombinations(*state); + + // compare_err_stream( "" ); // just for debugging + + state->dataSurface->ShadingTransmittanceVaries = true; + state->dataHeatBal->SolarDistribution = DataHeatBalance::Shadowing::FullExterior; + + // Set up solar for Jan 1 + state->dataEnvrn->DayOfYear_Schedule = 1; // Jan 1 + state->dataEnvrn->DayOfYear = 1; // Jan 1 + state->dataEnvrn->DayOfWeek = 6; + state->dataGlobal->TimeStep = 4; + state->dataGlobal->HourOfDay = 12; + state->dataGlobal->BeginSimFlag = true; + state->dataGlobal->BeginEnvrnFlag = true; + SolarShading::InitSolarCalculations(*state); + state->dataSolarShading->CalcSkyDifShading = true; + SolarShading::SkyDifSolarShading(*state); + state->dataSolarShading->CalcSkyDifShading = false; + state->dataGlobal->BeginSimFlag = false; + state->dataGlobal->BeginEnvrnFlag = false; + HeatBalanceIntRadExchange::InitSolarViewFactors(*state); // prevents crash in GetDaylightingParametersInput + SolarShading::PerformSolarCalculations(*state); + + // Get surface nums + int winSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:WIN001", state->dataSurface->Surface); + int wallSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH", state->dataSurface->Surface); + int shade1SurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:SHADE1", state->dataSurface->Surface); + int shade2SurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:SHADE2", state->dataSurface->Surface); + + // Get shading surface schedule indexes + int shade1SchedNum = state->dataSurface->Surface(shade1SurfNum).SchedShadowSurfIndex; + int shade2SchedNum = state->dataSurface->Surface(shade2SurfNum).SchedShadowSurfIndex; + + // Use EMS to turn shading surfaces on and off + auto &shade1SchedEMSOn = state->dataScheduleMgr->Schedule(shade1SchedNum).EMSActuatedOn; + auto &shade2SchedEMSOn = state->dataScheduleMgr->Schedule(shade2SchedNum).EMSActuatedOn; + auto &shade1SchedEMSValue = state->dataScheduleMgr->Schedule(shade1SchedNum).EMSValue; + auto &shade2SchedEMSValue = state->dataScheduleMgr->Schedule(shade2SchedNum).EMSValue; + + // shade1 transparent, shade2 transparent + shade1SchedEMSOn = true; + shade2SchedEMSOn = true; + shade1SchedEMSValue = 1.0; + shade2SchedEMSValue = 1.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + EXPECT_NEAR(1.0, state->dataSurface->SurfSunlitFrac(wallSurfNum), 0.0001); + EXPECT_NEAR(1.0, state->dataSurface->SurfSunlitFrac(winSurfNum), 0.0001); + + // shade1 opaque, shade2 transparent + shade1SchedEMSOn = true; + shade2SchedEMSOn = true; + shade1SchedEMSValue = 0.0; + shade2SchedEMSValue = 1.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracShade1Only = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracShade1Only = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.0, wallSunLitFracShade1Only, 0.0001); + EXPECT_NEAR(0.0, winSunLitFracShade1Only, 0.0001); + + // shade1 transparent, shade2 opaque + shade1SchedEMSOn = true; + shade2SchedEMSOn = true; + shade1SchedEMSValue = 1.0; + shade2SchedEMSValue = 0.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracShade2Only = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracShade2Only = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.0, wallSunLitFracShade2Only, 0.0001); + EXPECT_NEAR(0.0, winSunLitFracShade2Only, 0.0001); + + // shade1 opaque, shade2 opaque + shade1SchedEMSOn = true; + shade2SchedEMSOn = true; + shade1SchedEMSValue = 0.0; + shade2SchedEMSValue = 0.0; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracShade1Shade2 = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracShade1Shade2 = state->dataSurface->SurfSunlitFrac(winSurfNum); + EXPECT_NEAR(0.0, wallSunLitFracShade1Shade2, 0.0001); + EXPECT_NEAR(0.0, winSunLitFracShade1Shade2, 0.0001); + + // Use the base transmittance schedules (no EMS override) + // shade1 transmittance = 0.5, shade2 transmittance = 0.8 + shade1SchedEMSOn = false; + shade2SchedEMSOn = false; + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracShade1Shade2Partial = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracShade1Shade2Partial = state->dataSurface->SurfSunlitFrac(winSurfNum); + + // Expected results - wall + Real64 shade2ShadowFrac = (1 - wallSunLitFracShade2Only); + Real64 shade1ShadowFrac = (1 - wallSunLitFracShade1Only); + Real64 combinedShadowFrac = (1 - wallSunLitFracShade1Shade2); + Real64 overlapShdowFrac = shade2ShadowFrac + shade1ShadowFrac - combinedShadowFrac; + Real64 shade2OnlyShadowFrac = max(0.0, shade2ShadowFrac - overlapShdowFrac); + Real64 shade1OnlyShadowFrac = max(0.0, shade1ShadowFrac - overlapShdowFrac); + Real64 expWallSunlitFrac = + 1.0 - ((1.0 - 0.5) * shade1OnlyShadowFrac) - ((1.0 - 0.8) * shade2OnlyShadowFrac) - ((1.0 - 0.5 * 0.8) * overlapShdowFrac); + EXPECT_NEAR(expWallSunlitFrac, wallSunLitFracShade1Shade2Partial, 0.0001); + expWallSunlitFrac = 0.5 * 0.8; // again, in simple terms + EXPECT_NEAR(expWallSunlitFrac, wallSunLitFracShade1Shade2Partial, 0.0001); + + // Expected results - window + shade2ShadowFrac = (1 - winSunLitFracShade2Only); + shade1ShadowFrac = (1 - winSunLitFracShade1Only); + combinedShadowFrac = (1 - winSunLitFracShade1Shade2); + overlapShdowFrac = shade2ShadowFrac + shade1ShadowFrac - combinedShadowFrac; + shade2OnlyShadowFrac = max(0.0, shade2ShadowFrac - overlapShdowFrac); + shade1OnlyShadowFrac = max(0.0, shade1ShadowFrac - overlapShdowFrac); + Real64 expWinSunlitFrac = + 1.0 - ((1.0 - 0.5) * shade1OnlyShadowFrac) - ((1.0 - 0.8) * shade2OnlyShadowFrac) - ((1.0 - 0.5 * 0.8) * overlapShdowFrac); + EXPECT_NEAR(expWinSunlitFrac, winSunLitFracShade1Shade2Partial, 0.0001); + expWinSunlitFrac = 0.5 * 0.8; // again, in simple terms + EXPECT_NEAR(expWinSunlitFrac, winSunLitFracShade1Shade2Partial, 0.0001); +} +TEST_F(EnergyPlusFixture, SolarShadingTest_PolygonOverlap3) +// Tests complete overlap of 3 shadows which completely shade the test surfaces +{ + std::string const idf_objects = delimited_string({ + " Building,", + " DemoFDT, !- Name", + " 0, !- North Axis {deg}", + " Suburbs, !- Terrain", + " 3.9999999E-02, !- Loads Convergence Tolerance Value", + " 4.0000002E-03, !- Temperature Convergence Tolerance Value {deltaC}", + " FullExterior; !- Solar Distribution", + " ShadowCalculation,", + " PolygonClipping, !- Shading Calculation Method", + " Timestep, !- Shading Calculation Update Frequency Method", + " , !- Shading Calculation Update Frequency", + " , !- Maximum Figures in Shadow Overlap Calculations", + " , !- Polygon Clipping Algorithm", + " , !- Pixel Counting Resolution", + " DetailedSkyDiffuseModeling; !- Sky Diffuse Modeling Algorithm", + " Timestep,6;", + " Site:Location,", + " CHICAGO_IL_USA TMY2-94846, !- Name", + " 42.00, !- Latitude {deg}", + " -87.88, !- Longitude {deg}", + " -6.00, !- Time Zone {hr}", + " 190.00; !- Elevation {m}", + " Material,", + " C12 - 2 IN HW CONCRETE, !- Name", + " MediumRough, !- Roughness", + " 5.0901599E-02, !- Thickness {m}", + " 1.729577, !- Conductivity {W/m-K}", + " 2242.585, !- Density {kg/m3}", + " 836.8000, !- Specific Heat {J/kg-K}", + " 0.9000000, !- Thermal Absorptance", + " 0.6500000, !- Solar Absorptance", + " 0.6500000; !- Visible Absorptance", + " WindowMaterial:Glazing,", + " GLASS - CLEAR PLATE 1 / 4 IN, !- Name", + " SpectralAverage, !- Optical Data Type", + " , !- Window Glass Spectral Data Set Name", + " 0.006, !- Thickness {m}", + " 0.80, !- Solar Transmittance at Normal Incidence", + " 0.10, !- Front Side Solar Reflectance at Normal Incidence", + " 0.10, !- Back Side Solar Reflectance at Normal Incidence", + " 0.80, !- Visible Transmittance at Normal Incidence", + " 0.10, !- Front Side Visible Reflectance at Normal Incidence", + " 0.10, !- Back Side Visible Reflectance at Normal Incidence", + " 0.0, !- Infrared Transmittance at Normal Incidence", + " 0.84, !- Front Side Infrared Hemispherical Emissivity", + " 0.84, !- Back Side Infrared Hemispherical Emissivity", + " 0.9; !- Conductivity {W/m-K}", + " Construction,", + " Surfaces, !- Name", + " C12 - 2 IN HW CONCRETE; !- Layer 4", + " Construction,", + " Single PANE HW WINDOW, !- Name", + " GLASS - CLEAR PLATE 1 / 4 IN; !- Outside Layer", + " GlobalGeometryRules,", + " UpperLeftCorner, !- Starting Vertex Position", + " Counterclockwise, !- Vertex Entry Direction", + " Relative; !- Coordinate System", + " Zone,", + " ZONE ONE; !- Name", + " FenestrationSurface:Detailed,", + " Zn001:Wall-South:Win001, !- Name", + " Window, !- Surface Type", + " Single PANE HW WINDOW, !- Construction Name", + " Zn001:Wall-South, !- Building Surface Name", + " , !- Outside Boundary Condition Object", + " 0.5000000, !- View Factor to Ground", + " TestFrameAndDivider, !- Frame and Divider Name", + " 1.0, !- Multiplier", + " 4, !- Number of Vertices", + " -3,-5,2.5, !- X,Y,Z ==> Vertex 1 {m}", + " -3,-5,0.5, !- X,Y,Z ==> Vertex 2 {m}", + " 3,-5,0.5, !- X,Y,Z ==> Vertex 3 {m}", + " 3,-5,2.5; !- X,Y,Z ==> Vertex 4 {m}", + " WindowProperty:FrameAndDivider,", + " TestFrameAndDivider, !- Name", + " 0.05, !- Frame Width {m}", + " 0.05, !- Frame Outside Projection {m}", + " 0.05, !- Frame Inside Projection {m}", + " 5.0, !- Frame Conductance {W/m2-K}", + " 1.2, !- Ratio of Frame-Edge Glass Conductance to Center-Of-Gl", + " 0.8, !- Frame Solar Absorptance", + " 0.8, !- Frame Visible Absorptance", + " 0.9, !- Frame Thermal Hemispherical Emissivity", + " DividedLite, !- Divider Type", + " 0.02, !- Divider Width {m}", + " 2, !- Number of Horizontal Dividers", + " 2, !- Number of Vertical Dividers", + " 0.02, !- Divider Outside Projection {m}", + " 0.02, !- Divider Inside Projection {m}", + " 5.0, !- Divider Conductance {W/m2-K}", + " 1.2, !- Ratio of Divider-Edge Glass Conductance to Center-Of-", + " 0.8, !- Divider Solar Absorptance", + " 0.8, !- Divider Visible Absorptance", + " 0.9; !- Divider Thermal Hemispherical Emissivity", + " ScheduleTypeLimits,", + " Fraction, !- Name", + " 0.0, !- Lower Limit Value", + " 1.0, !- Upper Limit Value", + " Continuous; !- Numeric Type", + " BuildingSurface:Detailed,", + " Zn001:floor, !- Name", + " Floor, !- Surface Type", + " Surfaces, !- Construction Name", + " ZONE ONE, !- Zone Name", + " , !- Space Name", + " Outdoors, !- Outside Boundary Condition", + " , !- Outside Boundary Condition Object", + " SunExposed, !- Sun Exposure", + " WindExposed, !- Wind Exposure", + " 0.0000000, !- View Factor to Ground", + " 4, !- Number of Vertices", + " -5,5,0, !- X,Y,Z ==> Vertex 1 {m}", + " 5,5,0, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-5,0, !- X,Y,Z ==> Vertex 3 {m}", + " -5,-5,0; !- X,Y,Z ==> Vertex 4 {m}", + " BuildingSurface:Detailed,", + " Zn001:Wall-South, !- Name", + " Wall, !- Surface Type", + " Surfaces, !- Construction Name", + " ZONE ONE, !- Zone Name", + " , !- Space Name", + " Outdoors, !- Outside Boundary Condition", + " , !- Outside Boundary Condition Object", + " SunExposed, !- Sun Exposure", + " WindExposed, !- Wind Exposure", + " 0.5000000, !- View Factor to Ground", + " 4, !- Number of Vertices", + " -5,-5,3, !- X,Y,Z ==> Vertex 1 {m}", + " -5,-5,0, !- X,Y,Z ==> Vertex 2 {m}", + " 5,-5,0, !- X,Y,Z ==> Vertex 3 {m}", + " 5,-5,3; !- X,Y,Z ==> Vertex 4 {m}", + " Shading:Building:Detailed,", + " Zn001:Wall-South:Shade1, !- Name", + " Shade1Transmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -10,-8,20, !- X,Y,Z ==> Vertex 1 {m}", + " -10,-8,0, !- X,Y,Z ==> Vertex 2 {m}", + " 10,-8,0, !- X,Y,Z ==> Vertex 3 {m}", + " 10,-8,20; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " Shade1Transmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.5, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + " Shading:Building:Detailed,", + " Zn001:Wall-South:Shade2, !- Name", + " Shade2Transmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -10,-9,20, !- X,Y,Z ==> Vertex 1 {m}", + " -10,-9,0, !- X,Y,Z ==> Vertex 2 {m}", + " 10,-9,0, !- X,Y,Z ==> Vertex 3 {m}", + " 10,-9,20; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " Shade2Transmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.8, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + " Shading:Building:Detailed,", + " Zn001:Wall-South:Shade3, !- Name", + " Shade3Transmittance, !- Transmittance Schedule Name", + " 4, !- Number of Vertices", + " -10,-9,20, !- X,Y,Z ==> Vertex 1 {m}", + " -10,-9,0, !- X,Y,Z ==> Vertex 2 {m}", + " 10,-9,0, !- X,Y,Z ==> Vertex 3 {m}", + " 10,-9,20; !- X,Y,Z ==> Vertex 4 {m}", + " Schedule:Compact,", + " Shade3Transmittance, !- Name", + " Fraction, !- Schedule Type Limits Name", + " Through: 10/31, !- Field 5", + " For: AllDays, !- Field 6", + " until: 24:00,0.2, !- Field 7", + " Through: 12/31, !- Field 9", + " For: AllDays, !- Field 10", + " until: 24:00,0; !- Field 11", + }); + + ASSERT_TRUE(process_idf(idf_objects)); + + SimulationManager::GetProjectData(*state); + bool FoundError = false; + + HeatBalanceManager::GetProjectControlData(*state, FoundError); // read project control data + EXPECT_FALSE(FoundError); // expect no errors + + HeatBalanceManager::SetPreConstructionInputParameters(*state); + ScheduleManager::ProcessScheduleInput(*state); // read schedules + + Material::GetMaterialData(*state, FoundError); + EXPECT_FALSE(FoundError); + + HeatBalanceManager::GetFrameAndDividerData(*state); + + HeatBalanceManager::GetConstructData(*state, FoundError); + EXPECT_FALSE(FoundError); + + HeatBalanceManager::GetZoneData(*state, FoundError); // Read Zone data from input file + EXPECT_FALSE(FoundError); + + SurfaceGeometry::GetGeometryParameters(*state, FoundError); + EXPECT_FALSE(FoundError); + + WeatherManager::GetLocationInfo(*state, FoundError); + EXPECT_FALSE(FoundError); + + WeatherManager::CheckLocationValidity(*state); + + state->dataSurfaceGeometry->CosZoneRelNorth.allocate(1); + state->dataSurfaceGeometry->SinZoneRelNorth.allocate(1); + + state->dataSurfaceGeometry->CosZoneRelNorth(1) = std::cos(-state->dataHeatBal->Zone(1).RelNorth * Constant::DegToRadians); + state->dataSurfaceGeometry->SinZoneRelNorth(1) = std::sin(-state->dataHeatBal->Zone(1).RelNorth * Constant::DegToRadians); + state->dataSurfaceGeometry->CosBldgRelNorth = 1.0; + state->dataSurfaceGeometry->SinBldgRelNorth = 0.0; + + SurfaceGeometry::GetSurfaceData(*state, FoundError); // setup zone geometry and get zone data + EXPECT_FALSE(FoundError); // expect no errors + + // compare_err_stream( "" ); // just for debugging + + SurfaceGeometry::SetupZoneGeometry(*state, FoundError); // this calls GetSurfaceData() + EXPECT_FALSE(FoundError); + + SolarShading::AllocateModuleArrays(*state); + SolarShading::DetermineShadowingCombinations(*state); + + // compare_err_stream( "" ); // just for debugging + + state->dataSurface->ShadingTransmittanceVaries = true; + state->dataHeatBal->SolarDistribution = DataHeatBalance::Shadowing::FullExterior; + + // Set up solar for Jan 1 + state->dataEnvrn->DayOfYear_Schedule = 1; // Jan 1 + state->dataEnvrn->DayOfYear = 1; // Jan 1 + state->dataEnvrn->DayOfWeek = 6; + state->dataGlobal->TimeStep = 4; + state->dataGlobal->HourOfDay = 12; + state->dataGlobal->BeginSimFlag = true; + state->dataGlobal->BeginEnvrnFlag = true; + SolarShading::InitSolarCalculations(*state); + state->dataSolarShading->CalcSkyDifShading = true; + SolarShading::SkyDifSolarShading(*state); + state->dataSolarShading->CalcSkyDifShading = false; + state->dataGlobal->BeginSimFlag = false; + state->dataGlobal->BeginEnvrnFlag = false; + HeatBalanceIntRadExchange::InitSolarViewFactors(*state); // prevents crash in GetDaylightingParametersInput + SolarShading::PerformSolarCalculations(*state); + + // Get surface nums + int winSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH:WIN001", state->dataSurface->Surface); + int wallSurfNum = UtilityRoutines::FindItemInList("ZN001:WALL-SOUTH", state->dataSurface->Surface); + + // Use the base transmittance schedules (no EMS override) + // shade1 transmittance = 0.5, shade2 transmittance = 0.8 + FigureSolarBeamAtTimestep(*state, state->dataGlobal->HourOfDay, state->dataGlobal->TimeStep); + ReportSurfaceShading(*state); + + Real64 wallSunLitFracShade1Shade2Partial = state->dataSurface->SurfSunlitFrac(wallSurfNum); + Real64 winSunLitFracShade1Shade2Partial = state->dataSurface->SurfSunlitFrac(winSurfNum); + + Real64 expWallSunlitFrac = 0.5 * 0.8 * 0.2; // again, in simple terms + EXPECT_NEAR(expWallSunlitFrac, wallSunLitFracShade1Shade2Partial, 0.0001); + + // Expected results - window + Real64 expWinSunlitFrac = 0.5 * 0.8 * 0.2; // again, in simple terms + EXPECT_NEAR(expWinSunlitFrac, winSunLitFracShade1Shade2Partial, 0.0001); +}