LCOV - code coverage report
Current view: top level - EnergyPlus - DataSurfaces.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 73.2 % 436 319
Test Date: 2025-05-22 16:09:37 Functions: 70.8 % 24 17

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <algorithm>
      50              : #include <cassert>
      51              : #include <cmath>
      52              : #include <limits>
      53              : #include <tuple>
      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/DataLoopNode.hh>
      62              : #include <EnergyPlus/DataZoneEquipment.hh>
      63              : #include <EnergyPlus/Psychrometrics.hh>
      64              : #include <EnergyPlus/UtilityRoutines.hh>
      65              : #include <EnergyPlus/WindowManager.hh>
      66              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      67              : 
      68              : namespace EnergyPlus::DataSurfaces {
      69              : 
      70              : // MODULE INFORMATION:
      71              : //       AUTHOR         Linda Lawrie
      72              : //       DATE WRITTEN   May 2000
      73              : //       MODIFIED       July 2003, (CC) added a flag for reference air temperature
      74              : //                      Dec 2006, DJS (PSU) added logical ecoroof variable
      75              : //                      Dec 2008, TH added new properties to SurfaceWindowCalc for thermochromic windows
      76              : //                      Jul 2011, M.J. Witte and C.O. Pedersen, add new fields to OSC for last T, max and min
      77              : 
      78              : // Using/Aliasing
      79              : using namespace DataVectorTypes;
      80              : using namespace DataBSDFWindow;
      81              : using namespace DataHeatBalance;
      82              : using namespace DataZoneEquipment;
      83              : using namespace DataLoopNode;
      84              : using namespace Psychrometrics;
      85              : using namespace DataEnvironment;
      86              : using namespace Window;
      87              : 
      88              : Array1D_string const cExtBoundCondition({-6, 0}, {"KivaFoundation", "FCGround", "OSCM", "OSC", "OSC", "Ground", "ExternalEnvironment"});
      89              : 
      90          757 : Surface2D::Surface2D(ShapeCat const shapeCat, int const axis, Vertices const &v, Vector2D const &vl, Vector2D const &vu)
      91          757 :     : axis(axis), vertices(v), vl(vl), vu(vu)
      92              : {
      93          757 :     size_type const n(vertices.size());
      94          757 :     assert(n >= 3);
      95              : 
      96              :     // Reverse vertices order if clockwise
      97              :     // If sorting by y for slab method can detect clockwise faster by just comparing edges at bottom or top-most vertex
      98          757 :     Real64 area(0.0); // Actually 2x the signed area
      99         3862 :     for (Vertices::size_type i = 0; i < n; ++i) {
     100         3105 :         Vector2D const &v(vertices[i]);
     101         3105 :         Vector2D const &w(vertices[(i + 1) % n]);
     102         3105 :         area += (v.x * w.y) - (w.x * v.y);
     103              :     }
     104          757 :     if (area < 0.0) std::reverse(vertices.begin() + 1, vertices.end()); // Vertices in clockwise order: Reverse all but first
     105              : 
     106              :     // Set up edge vectors for ray--surface intersection tests
     107          757 :     edges.reserve(n);
     108         3862 :     for (Vertices::size_type i = 0; i < n; ++i) {
     109         3105 :         edges.push_back(vertices[(i + 1) % n] - vertices[i]);
     110              :     }
     111          757 :     if (shapeCat == ShapeCat::Rectangular) { // Set side length squared for ray--surface intersection tests
     112          663 :         assert(n == 4u);
     113          663 :         s1 = edges[0].magnitude_squared();
     114          663 :         s3 = edges[3].magnitude_squared();
     115           94 :     } else if ((shapeCat == ShapeCat::Nonconvex) || (n >= nVerticesBig)) { // Set up slabs
     116            6 :         assert(n >= 4u);
     117            6 :         slabYs.reserve(n);
     118          104 :         for (size_type i = 0; i < n; ++i)
     119           98 :             slabYs.push_back(vertices[i].y);
     120            6 :         std::sort(slabYs.begin(), slabYs.end());                     // Sort the vertex y coordinates
     121            6 :         auto const iClip(std::unique(slabYs.begin(), slabYs.end())); // Remove duplicate y-coordinate elements
     122            6 :         slabYs.erase(iClip, slabYs.end());
     123            6 :         slabYs.shrink_to_fit();
     124           76 :         for (size_type iSlab = 0, iSlab_end = slabYs.size() - 1; iSlab < iSlab_end; ++iSlab) { // Create slabs
     125           70 :             Real64 xl(std::numeric_limits<Real64>::max());
     126           70 :             Real64 xu(std::numeric_limits<Real64>::lowest());
     127           70 :             Real64 const yl(slabYs[iSlab]);
     128           70 :             Real64 const yu(slabYs[iSlab + 1]);
     129           70 :             slabs.push_back(Slab(yl, yu));
     130           70 :             Slab &slab(slabs.back());
     131              :             using CrossEdge = std::tuple<Real64, Real64, size_type>;
     132              :             using CrossEdges = std::vector<CrossEdge>;
     133           70 :             CrossEdges crossEdges;
     134         1816 :             for (size_type i = 0; i < n; ++i) { // Find edges crossing slab
     135         1746 :                 Vector2D const &v(vertices[i]);
     136         1746 :                 Vector2D const &w(vertices[(i + 1) % n]);
     137         1746 :                 if (((v.y <= yl) && (yu <= w.y)) || // Crosses upward
     138         1645 :                     ((yu <= v.y) && (w.y <= yl)))   // Crosses downward
     139              :                 {
     140          202 :                     Edge const &e(edges[i]);
     141          202 :                     assert(e.y != 0.0);
     142          202 :                     Real64 const exy(e.x / e.y);
     143          202 :                     Real64 const xb(v.x + (yl - v.y) * exy); // x_bot coordinate where edge intersects yl
     144          202 :                     Real64 const xt(v.x + (yu - v.y) * exy); // x_top coordinate where edge intersects yu
     145          202 :                     xl = std::min(xl, std::min(xb, xt));
     146          202 :                     xu = std::max(xu, std::max(xb, xt));
     147          202 :                     crossEdges.push_back(std::make_tuple(xb, xt, i));
     148              :                 }
     149              :             }
     150           70 :             slab.xl = xl;
     151           70 :             slab.xu = xu;
     152           70 :             assert(crossEdges.size() >= 2u);
     153           70 :             std::sort(crossEdges.begin(),
     154              :                       crossEdges.end(),
     155          331 :                       [](CrossEdge const &e1, CrossEdge const &e2) -> bool // Lambda to sort by x_mid
     156              :                       {
     157          331 :                           return std::get<0>(e1) + std::get<1>(e1) <
     158          331 :                                  std::get<0>(e2) + std::get<1>(e2); // Sort edges by x_mid: x_bot or x_top could have repeats with shared vertex
     159              :                       });
     160              : #ifndef NDEBUG // Check x_bot and x_top are also sorted
     161           70 :             Real64 xb(std::get<0>(crossEdges[0]));
     162           70 :             Real64 xt(std::get<1>(crossEdges[0]));
     163           70 :             Real64 const tol(1.0e-9 * std::max(std::abs(xl), std::abs(xu))); // EnergyPlus vertex precision is not tight so tolerance isn't either
     164          272 :             for (auto const &edge : crossEdges) {                            // Detect non-simple polygon with crossing edges
     165          202 :                 Real64 const xbe(std::get<0>(edge));
     166          202 :                 Real64 const xte(std::get<1>(edge));
     167          202 :                 assert(xb <= xbe + tol);
     168          202 :                 assert(xt <= xte + tol);
     169          202 :                 xb = xbe;
     170          202 :                 xt = xte;
     171              :             }
     172              : #endif
     173           70 :             assert((shapeCat == ShapeCat::Nonconvex) || (crossEdges.size() == 2u));
     174          272 :             for (auto const &edge : crossEdges) {
     175          202 :                 size_type const iEdge(std::get<2>(edge));
     176          202 :                 slab.edges.push_back(iEdge); // Add edge to slab
     177          202 :                 Vector2D const &e(edges[iEdge]);
     178          202 :                 assert(e.y != 0.0);                                   // Constant y edge can't be a crossing edge
     179          202 :                 slab.edgesXY.push_back(e.y != 0.0 ? e.x / e.y : 0.0); // Edge inverse slope
     180              :             }
     181           70 :             assert(slab.edges.size() % 2 == 0u);
     182           70 :             assert(slab.edges.size() == slab.edgesXY.size());
     183           70 :         }
     184              :     }
     185          757 : }
     186              : 
     187              : // Set Precomputed Parameters
     188          760 : void SurfaceData::set_computed_geometry()
     189              : {
     190          760 :     if (Vertex.size() >= 3) { // Skip no-vertex "surfaces"
     191          757 :         shapeCat = computed_shapeCat();
     192          757 :         plane = computed_plane();
     193          757 :         surface2d = computed_surface2d();
     194              :     }
     195          760 : }
     196              : 
     197      4671106 : Real64 SurfaceData::getInsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
     198              : {
     199              :     // SUBROUTINE INFORMATION:
     200              :     //       AUTHOR         Simon Vidanovic
     201              :     //       DATE WRITTEN   June 2016
     202              :     //       MODIFIED       na
     203              :     //       RE-ENGINEERED  na
     204              : 
     205              :     // PURPOSE OF THIS SUBROUTINE:
     206              :     // Routine calculates reference air temperature for given surface (refactoring from the code)
     207              :     //
     208              :     // NOTE: This routine has been copy/pasted in the past in several different modules with slight
     209              :     //       modifications at some of those places. It is quite logical that reference air temperature
     210              :     //       for the surface is calculated as public function of SurfaceData structure (class) and is
     211              :     //       later called as needed. Note that SurfaceNum had to be passed to this routine because of
     212              :     //       access to global array SurfTempEffBulkAir. I would propose refactoring where SurfTempEffBulkAir
     213              :     //       is part of SurfaceData structure and instead of calling SurfTempEffBulkAir( SurfNum ) it should
     214              :     //       be called Surface( SurfNum ).TempEffBulkAir (Simon Vidanovic)
     215              : 
     216      4671106 :     Real64 RefAirTemp = 0;
     217              : 
     218              :     // determine reference air temperature for this surface
     219      4671106 :     auto &thisSpaceHB = state.dataZoneTempPredictorCorrector->spaceHeatBalance(this->spaceNum);
     220      4671106 :     switch (state.dataSurface->SurfTAirRef(t_SurfNum)) {
     221            4 :     case RefAirTemp::ZoneMeanAirTemp: {
     222            4 :         RefAirTemp = thisSpaceHB.MAT;
     223            4 :     } break;
     224            4 :     case RefAirTemp::AdjacentAirTemp: {
     225            4 :         RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(t_SurfNum);
     226            4 :     } break;
     227            9 :     case RefAirTemp::ZoneSupplyAirTemp: {
     228              :         // determine ZoneEquipConfigNum for this zone
     229              :         //            ControlledZoneAirFlag = .FALSE.
     230              :         // ZoneEquipConfigNum = ZoneNum;
     231              :         // check whether this zone is a controlled zone or not
     232            9 :         if (!state.dataHeatBal->Zone(Zone).IsControlled) {
     233            0 :             ShowFatalError(state,
     234            0 :                            format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}",
     235            0 :                                   state.dataHeatBal->Zone(Zone).Name));
     236              :             // return;
     237              :         }
     238              :         // determine supply air conditions
     239            9 :         Real64 SumSysMCp = 0;
     240            9 :         Real64 SumSysMCpT = 0;
     241            9 :         auto const &inletNodes = (state.dataHeatBal->doSpaceHeatBalance) ? state.dataZoneEquip->spaceEquipConfig(this->spaceNum).InletNode
     242            9 :                                                                          : state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode;
     243           27 :         for (int nodeNum : inletNodes) {
     244           18 :             auto const &inNode = state.dataLoopNodes->Node(nodeNum);
     245           18 :             Real64 CpAir = PsyCpAirFnW(thisSpaceHB.airHumRat);
     246           18 :             SumSysMCp += inNode.MassFlowRate * CpAir;
     247           18 :             SumSysMCpT += inNode.MassFlowRate * CpAir * inNode.Temp;
     248              :         }
     249              :         // a weighted average of the inlet temperatures.
     250            9 :         if (SumSysMCp > 0.0) {
     251              :             // a weighted average of the inlet temperatures.
     252            6 :             RefAirTemp = SumSysMCpT / SumSysMCp;
     253              :         } else {
     254            3 :             RefAirTemp = thisSpaceHB.MAT;
     255              :         }
     256            9 :     } break;
     257      4671089 :     default: {
     258              :         // currently set to mean air temp but should add error warning here
     259      4671089 :         RefAirTemp = thisSpaceHB.MAT;
     260      4671089 :     } break;
     261              :     }
     262              : 
     263      4671106 :     return RefAirTemp;
     264              : }
     265              : 
     266            0 : Real64 SurfaceData::getOutsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
     267              : {
     268              :     // SUBROUTINE INFORMATION:
     269              :     //       AUTHOR         Simon Vidanovic
     270              :     //       DATE WRITTEN   June 2016
     271              :     //       MODIFIED       na
     272              :     //       RE-ENGINEERED  na
     273              : 
     274              :     // PURPOSE OF THIS SUBROUTINE:
     275              :     // Routine calculates outside air temperature for given surface.
     276              :     // Routine will return inside air temperature if it is interior surface. (refactoring from the code)
     277              :     //
     278              :     // NOTE: This routine has been copy/pasted in the past in several different modules with slight
     279              :     //       modifications at some of those places. Exterior/interior surface air temperature is tied to surface.
     280            0 :     Real64 temperature = 0;
     281              : 
     282            0 :     if (ExtBoundCond > 0) // Interzone window
     283              :     {
     284            0 :         temperature = getInsideAirTemperature(state, t_SurfNum);
     285              :     } else {
     286            0 :         if (ExtWind) {
     287              :             // Window is exposed to wind (and possibly rain)
     288            0 :             if (state.dataEnvrn->IsRain) {
     289              :                 // Raining: since wind exposed, outside window surface gets wet
     290            0 :                 temperature = state.dataSurface->SurfOutWetBulbTemp(t_SurfNum);
     291              :             } else {
     292              :                 // Dry
     293            0 :                 temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
     294              :             }
     295              :         } else {
     296              :             // Window not exposed to wind
     297            0 :             temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
     298              :         }
     299              :     }
     300              : 
     301            0 :     return temperature;
     302              : }
     303              : 
     304            0 : Real64 SurfaceData::getOutsideIR(EnergyPlusData &state, const int t_SurfNum) const
     305              : {
     306              :     // SUBROUTINE INFORMATION:
     307              :     //       AUTHOR         Simon Vidanovic
     308              :     //       DATE WRITTEN   July 2016
     309              :     //       MODIFIED       na
     310              :     //       RE-ENGINEERED  na
     311              : 
     312              :     // PURPOSE OF THIS SUBROUTINE:
     313              :     // Calculates outside infrared radiation
     314            0 :     Real64 value = 0;
     315            0 :     if (ExtBoundCond > 0) {
     316            0 :         value = state.dataSurface->SurfWinIRfromParentZone(ExtBoundCond) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(ExtBoundCond);
     317              :     } else {
     318            0 :         Real64 tout = getOutsideAirTemperature(state, t_SurfNum) + Constant::Kelvin;
     319            0 :         value = Constant::StefanBoltzmann * pow_4(tout);
     320            0 :         value =
     321            0 :             ViewFactorSkyIR * (state.dataSurface->SurfAirSkyRadSplit(t_SurfNum) * Constant::StefanBoltzmann * pow_4(state.dataEnvrn->SkyTempKelvin) +
     322            0 :                                (1.0 - state.dataSurface->SurfAirSkyRadSplit(t_SurfNum)) * value) +
     323            0 :             ViewFactorGroundIR * value;
     324              :     }
     325            0 :     return value;
     326              : }
     327              : 
     328            0 : Real64 SurfaceData::getSWIncident(EnergyPlusData &state, const int t_SurfNum)
     329              : {
     330              :     // SUBROUTINE INFORMATION:
     331              :     //       AUTHOR         Simon Vidanovic
     332              :     //       DATE WRITTEN   July 2016
     333              :     //       MODIFIED       na
     334              :     //       RE-ENGINEERED  na
     335              : 
     336              :     // PURPOSE OF THIS SUBROUTINE:
     337              :     // Return total short wave incident to the surface
     338              : 
     339            0 :     return state.dataHeatBal->SurfQRadSWOutIncident(t_SurfNum) +
     340            0 :            state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(t_SurfNum).SolarEnclIndex);
     341              : }
     342              : 
     343            0 : int SurfaceData::getTotLayers(EnergyPlusData &state) const
     344              : {
     345              :     // SUBROUTINE INFORMATION:
     346              :     //       AUTHOR         Simon Vidanovic
     347              :     //       DATE WRITTEN   August 2016
     348              :     //       MODIFIED       na
     349              :     //       RE-ENGINEERED  na
     350              : 
     351              :     // PURPOSE OF THIS SUBROUTINE:
     352              :     // Returns total number of layer for current surface
     353              : 
     354            0 :     auto &construction(state.dataConstruction->Construct(Construction));
     355            0 :     return construction.TotLayers;
     356              : }
     357              : 
     358              : // Computed Shape Category
     359          757 : ShapeCat SurfaceData::computed_shapeCat() const
     360              : {
     361          757 :     if (Shape == SurfaceShape::Triangle) {
     362            5 :         return ShapeCat::Triangular;
     363          752 :     } else if (Shape == SurfaceShape::TriangularWindow) {
     364            0 :         return ShapeCat::Triangular;
     365          752 :     } else if (Shape == SurfaceShape::TriangularDoor) {
     366            0 :         return ShapeCat::Triangular;
     367          752 :     } else if (Shape == SurfaceShape::Rectangle) {
     368          619 :         return ShapeCat::Rectangular;
     369          133 :     } else if (Shape == SurfaceShape::RectangularDoorWindow) {
     370           44 :         return ShapeCat::Rectangular;
     371           89 :     } else if (Shape == SurfaceShape::RectangularOverhang) {
     372            0 :         return ShapeCat::Rectangular;
     373           89 :     } else if (Shape == SurfaceShape::RectangularLeftFin) {
     374            0 :         return ShapeCat::Rectangular;
     375           89 :     } else if (Shape == SurfaceShape::RectangularRightFin) {
     376            0 :         return ShapeCat::Rectangular;
     377           89 :     } else if (IsConvex) {
     378           85 :         return ShapeCat::Convex;
     379              :     } else {
     380            4 :         return ShapeCat::Nonconvex;
     381              :     }
     382              : }
     383              : 
     384              : // Computed Plane
     385         1514 : SurfaceData::Plane SurfaceData::computed_plane() const
     386              : {
     387         1514 :     Vertices::size_type const n(Vertex.size());
     388         1514 :     assert(n >= 3);
     389         1514 :     Vector center(0.0);                           // Center (vertex average) point (not mass centroid)
     390         1514 :     Real64 a(0.0), b(0.0), c(0.0), d(0.0);        // Plane coefficients
     391         7724 :     for (Vertices::size_type i = 0; i < n; ++i) { // Newell's method for robustness (not speed)
     392         6210 :         Vector const &v(Vertex[i]);
     393         6210 :         Vector const &w(Vertex[(i + 1) % n]);
     394         6210 :         a += (v.y - w.y) * (v.z + w.z);
     395         6210 :         b += (v.z - w.z) * (v.x + w.x);
     396         6210 :         c += (v.x - w.x) * (v.y + w.y);
     397         6210 :         center += v;
     398              :     }
     399         1514 :     d = -(dot(center, Vector(a, b, c)) / n); // center/n is the center point
     400         1514 :     return Plane(a, b, c, d);                // a*x + b*y + c*z + d = 0
     401         1514 : }
     402              : 
     403              : // Computed axis-projected 2D surface
     404          757 : Surface2D SurfaceData::computed_surface2d() const
     405              : {
     406              :     // Project along axis of min surface range for 2D intersection use
     407          757 :     Vertices::size_type const n(Vertex.size());
     408          757 :     assert(n >= 3);
     409          757 :     assert(plane == computed_plane()); // Set plane first
     410              :     using Vertex2D = ObjexxFCL::Vector2<Real64>;
     411              :     using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
     412              : 
     413              :     // Select axis to project along
     414          757 :     Real64 const a(std::abs(plane.x));                                       // Plane normal x coordinate magnitude
     415          757 :     Real64 const b(std::abs(plane.y));                                       // Plane normal y coordinate magnitude
     416          757 :     Real64 const c(std::abs(plane.z));                                       // Plane normal z coordinate magnitude
     417          757 :     int const axis(a >= std::max(b, c) ? 0 : (b >= std::max(a, c) ? 1 : 2)); // Project along plane's normal's largest magnitude coordinate
     418              : 
     419              :     // Set up 2D surface
     420          757 :     Vertices2D v2d(n);
     421          757 :     Vector const &v0(Vertex[0]);
     422          757 :     if (axis == 0) {               // Use y,z for 2D surface
     423          197 :         Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
     424          197 :         Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
     425          982 :         for (Vertices::size_type i = 0; i < n; ++i) {
     426          785 :             Vector const &v(Vertex[i]);
     427          785 :             v2d[i] = Vertex2D(v.y, v.z);
     428          785 :             yl = std::min(yl, v.y);
     429          785 :             yu = std::max(yu, v.y);
     430          785 :             zl = std::min(zl, v.z);
     431          785 :             zu = std::max(zu, v.z);
     432              :         }
     433          197 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(yl, zl), Vertex2D(yu, zu));
     434          560 :     } else if (axis == 1) {        // Use x,z for 2D surface
     435          284 :         Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
     436          284 :         Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
     437         1419 :         for (Vertices::size_type i = 0; i < n; ++i) {
     438         1135 :             Vector const &v(Vertex[i]);
     439         1135 :             v2d[i] = Vertex2D(v.x, v.z);
     440         1135 :             xl = std::min(xl, v.x);
     441         1135 :             xu = std::max(xu, v.x);
     442         1135 :             zl = std::min(zl, v.z);
     443         1135 :             zu = std::max(zu, v.z);
     444              :         }
     445          284 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, zl), Vertex2D(xu, zu));
     446              :     } else {                       // Use x,y for 2D surface
     447          276 :         Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
     448          276 :         Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
     449         1461 :         for (Vertices::size_type i = 0; i < n; ++i) {
     450         1185 :             Vector const &v(Vertex[i]);
     451         1185 :             v2d[i] = Vertex2D(v.x, v.y);
     452         1185 :             xl = std::min(xl, v.x);
     453         1185 :             xu = std::max(xu, v.x);
     454         1185 :             yl = std::min(yl, v.y);
     455         1185 :             yu = std::max(yu, v.y);
     456              :         }
     457          276 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, yl), Vertex2D(xu, yu));
     458              :     }
     459          757 : }
     460              : 
     461            9 : Real64 SurfaceData::get_average_height(EnergyPlusData &state) const
     462              : {
     463            9 :     if (std::abs(SinTilt) < Constant::SmallDistance) {
     464            1 :         return 0.0;
     465              :     }
     466              :     using Vertex2D = ObjexxFCL::Vector2<Real64>;
     467              :     using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
     468            8 :     Vertices::size_type const n(Vertex.size());
     469            8 :     assert(n >= 3);
     470              : 
     471            8 :     Vertices2D v2d(n);
     472              : 
     473              :     // project onto 2D vertical plane
     474            8 :     Real64 xRef = Vertex[0].x;
     475            8 :     Real64 yRef = Vertex[0].y;
     476            8 :     Real64 const &saz(SinAzim);
     477            8 :     Real64 const &caz(CosAzim);
     478           42 :     for (Vertices::size_type i = 0; i < n; ++i) {
     479           34 :         Vector const &v(Vertex[i]);
     480           34 :         v2d[i] = Vertex2D(-(v.x - xRef) * caz + (v.y - yRef) * saz, v.z);
     481              :     }
     482              : 
     483              :     // piecewise linear integration
     484              : 
     485              :     // Get total width of polygon
     486            8 :     Real64 minX(v2d[0].x), maxX(v2d[0].x);
     487           42 :     for (Vertices::size_type i = 0; i < n; ++i) {
     488           34 :         Vertex2D const &v(v2d[i]);
     489           34 :         minX = std::min(minX, v.x);
     490           34 :         maxX = std::max(maxX, v.x);
     491              :     }
     492            8 :     Real64 totalWidth = maxX - minX;
     493              : 
     494            8 :     if (totalWidth == 0.0) {
     495              :         // This should never happen, but if it does, print a somewhat meaningful fatal error
     496              :         // (instead of allowing a divide by zero).
     497            0 :         ShowFatalError(state, format("Calculated projected surface width is zero for surface=\"{}\"", Name));
     498              :     }
     499              : 
     500            8 :     Real64 averageHeight = 0.0;
     501           42 :     for (Vertices::size_type i = 0; i < n; ++i) {
     502           34 :         Vertex2D const &v(v2d[i]);
     503              : 
     504              :         Vertex2D *v2;
     505           34 :         if (i == n - 1) {
     506            8 :             v2 = &v2d[0];
     507              :         } else {
     508           26 :             v2 = &v2d[i + 1];
     509              :         }
     510           34 :         averageHeight += 0.5 * (v.y + v2->y) * (v2->x - v.x) / totalWidth;
     511              :     }
     512            8 :     return std::abs(averageHeight) / SinTilt;
     513            8 : }
     514              : 
     515           12 : void SurfaceData::make_hash_key(EnergyPlusData &state, const int SurfNum)
     516              : {
     517           12 :     auto &s_surf = state.dataSurface;
     518              : 
     519           12 :     calcHashKey = SurfaceCalcHashKey();
     520           12 :     calcHashKey.Construction = Construction;
     521           12 :     calcHashKey.Azimuth = round(Azimuth * 10.0) / 10.0;
     522           12 :     calcHashKey.Tilt = round(Tilt * 10.0) / 10.0;
     523           12 :     calcHashKey.Height = round(Height * 10.0) / 10.0;
     524           12 :     calcHashKey.Zone = Zone;
     525           12 :     calcHashKey.EnclIndex = SolarEnclIndex;
     526           12 :     calcHashKey.TAirRef = s_surf->SurfTAirRef(SurfNum);
     527              : 
     528           12 :     int extBoundCond = s_surf->Surface(SurfNum).ExtBoundCond;
     529           12 :     if (extBoundCond > 0) {
     530            0 :         calcHashKey.ExtZone = s_surf->Surface(extBoundCond).Zone;
     531            0 :         calcHashKey.ExtEnclIndex = s_surf->Surface(extBoundCond).SolarEnclIndex;
     532            0 :         calcHashKey.ExtCond = 1;
     533              :     } else {
     534           12 :         calcHashKey.ExtZone = 0;
     535           12 :         calcHashKey.ExtEnclIndex = 0;
     536           12 :         calcHashKey.ExtCond = extBoundCond;
     537              :     }
     538              : 
     539           12 :     calcHashKey.ExtSolar = ExtSolar;
     540           12 :     calcHashKey.ExtWind = ExtWind;
     541           12 :     calcHashKey.ViewFactorGround = round(ViewFactorGround * 10.0) / 10.0;
     542           12 :     calcHashKey.ViewFactorSky = round(ViewFactorSky * 10.0) / 10.0;
     543              : 
     544           12 :     calcHashKey.HeatTransferAlgorithm = HeatTransferAlgorithm;
     545           12 :     calcHashKey.intConvModel = s_surf->surfIntConv(SurfNum).model;
     546           12 :     calcHashKey.extConvModel = s_surf->surfExtConv(SurfNum).model;
     547           12 :     calcHashKey.intConvUserModelNum = s_surf->surfIntConv(SurfNum).userModelNum;
     548           12 :     calcHashKey.extConvUserModelNum = s_surf->surfExtConv(SurfNum).userModelNum;
     549           12 :     calcHashKey.OSCPtr = OSCPtr;
     550           12 :     calcHashKey.OSCMPtr = OSCMPtr;
     551              : 
     552           12 :     calcHashKey.FrameDivider = FrameDivider;
     553           12 :     calcHashKey.SurfWinStormWinConstr = s_surf->SurfWinStormWinConstr(SurfNum);
     554              : 
     555           12 :     calcHashKey.MaterialMovInsulExt = s_surf->extMovInsuls(SurfNum).matNum;
     556           12 :     calcHashKey.MaterialMovInsulInt = s_surf->intMovInsuls(SurfNum).matNum;
     557           12 :     calcHashKey.movInsulExtSchedNum = (s_surf->extMovInsuls(SurfNum).sched == nullptr) ? -1 : s_surf->extMovInsuls(SurfNum).sched->Num;
     558           12 :     calcHashKey.movInsulIntSchedNum = (s_surf->intMovInsuls(SurfNum).sched == nullptr) ? -1 : s_surf->intMovInsuls(SurfNum).sched->Num;
     559              : 
     560           12 :     calcHashKey.externalShadingSchedNum =
     561           12 :         (s_surf->Surface(SurfNum).surfExternalShadingSched != nullptr) ? s_surf->Surface(SurfNum).surfExternalShadingSched->Num : -1;
     562           12 :     calcHashKey.SurroundingSurfacesNum = s_surf->Surface(SurfNum).SurfSurroundingSurfacesNum;
     563           12 :     calcHashKey.LinkedOutAirNode = s_surf->Surface(SurfNum).SurfLinkedOutAirNode;
     564           12 :     calcHashKey.outsideHeatSourceTermSchedNum = (outsideHeatSourceTermSched != nullptr) ? outsideHeatSourceTermSched->Num : -1;
     565           12 :     calcHashKey.insideHeatSourceTermSchedNum = (insideHeatSourceTermSched != nullptr) ? insideHeatSourceTermSched->Num : -1;
     566           12 :     calcHashKey.ViewFactorSrdSurfs = s_surf->Surface(SurfNum).ViewFactorSrdSurfs;
     567           12 : }
     568              : 
     569           12 : void SurfaceData::set_representative_surface(EnergyPlusData &state, const int SurfNum)
     570              : {
     571              :     // Make hash key for this surface (used to determine uniqueness)
     572           12 :     state.dataSurface->Surface(SurfNum).make_hash_key(state, SurfNum);
     573              :     // Insert surface key into map. If key already exists, it will not be added.
     574              :     // Assign the representative surface number based on the first instance of the identical key
     575           12 :     state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum =
     576           12 :         state.dataSurface->RepresentativeSurfaceMap.insert({state.dataSurface->Surface(SurfNum).calcHashKey, SurfNum}).first->second;
     577              : 
     578           12 :     state.dataSurface->Surface(state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum).ConstituentSurfaceNums.push_back(SurfNum);
     579           12 : }
     580              : 
     581              : // Functions
     582              : 
     583       249958 : void SetSurfaceOutBulbTempAt(EnergyPlusData &state)
     584              : {
     585       249958 :     if (state.dataEnvrn->SiteTempGradient == 0.0) {
     586            0 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     587            0 :             state.dataSurface->SurfOutDryBulbTemp(SurfNum) = state.dataEnvrn->OutDryBulbTemp;
     588            0 :             state.dataSurface->SurfOutWetBulbTemp(SurfNum) = state.dataEnvrn->OutWetBulbTemp;
     589              :         }
     590              :     } else {
     591       249958 :         Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     592       249958 :         Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     593      2359345 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     594              :             // Base temperatures at Z = 0 (C)
     595      2109387 :             Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
     596      2109387 :             if (Z <= 0.0) {
     597       369482 :                 state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp;
     598       369482 :                 state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp;
     599              :             } else {
     600      1739905 :                 Real64 GradientDividend = state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z;
     601      1739905 :                 Real64 GradientDivisor = DataEnvironment::EarthRadius + Z;
     602      1739905 :                 state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp - GradientDividend / GradientDivisor;
     603      1739905 :                 state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp - GradientDividend / GradientDivisor;
     604              :             }
     605              :         }
     606              :     }
     607       249958 : }
     608              : 
     609       249956 : void CheckSurfaceOutBulbTempAt(EnergyPlusData &state)
     610              : {
     611              :     // Using/Aliasing
     612              :     using DataEnvironment::SetOutBulbTempAt_error;
     613              : 
     614       249956 :     Real64 minBulb = 0.0;
     615      2359340 :     for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     616      2109384 :         minBulb = min(minBulb, state.dataSurface->SurfOutDryBulbTemp(SurfNum), state.dataSurface->SurfOutWetBulbTemp(SurfNum));
     617      2109384 :         if (minBulb < -100.0)
     618            0 :             SetOutBulbTempAt_error(state, "Surface", state.dataSurface->Surface(SurfNum).Centroid.z, state.dataSurface->Surface(SurfNum).Name);
     619              :     }
     620       249956 : }
     621              : 
     622       249956 : void SetSurfaceWindSpeedAt(EnergyPlusData &state)
     623              : {
     624       249956 :     Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
     625       249956 :                      std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
     626       249956 :     if (state.dataEnvrn->SiteWindExp == 0.0) {
     627            0 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     628            0 :             state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataEnvrn->WindSpeed;
     629              :         }
     630              :     } else {
     631              : 
     632      2359340 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     633      2109384 :             if (!state.dataSurface->Surface(SurfNum).ExtWind) continue;
     634      1339325 :             Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
     635      1339325 :             if (Z <= 0.0) {
     636        52141 :                 state.dataSurface->SurfOutWindSpeed(SurfNum) = 0.0;
     637              :             } else {
     638              :                 //  [Met] - at meterological Station, Height of measurement is usually 10m above ground
     639              :                 //  LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
     640              :                 //                     * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
     641      1287184 :                 state.dataSurface->SurfOutWindSpeed(SurfNum) = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
     642              :             }
     643              :         }
     644              :     }
     645       249956 : }
     646              : 
     647       249956 : void SetSurfaceWindDirAt(EnergyPlusData &state)
     648              : {
     649      2359340 :     for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     650      2109384 :         state.dataSurface->SurfOutWindDir(SurfNum) = state.dataEnvrn->WindDir;
     651              :     }
     652       249956 : }
     653              : 
     654           96 : std::string cSurfaceClass(SurfaceClass const ClassNo)
     655              : {
     656              : 
     657              :     // FUNCTION INFORMATION:
     658              :     //       AUTHOR         Linda Lawrie
     659              :     //       DATE WRITTEN   May 2006
     660              :     //       MODIFIED       na
     661              :     //       RE-ENGINEERED  na
     662              : 
     663              :     // PURPOSE OF THIS FUNCTION:
     664              :     // This function returns a string based on class number.
     665              : 
     666              :     // Return value
     667           96 :     std::string ClassName;
     668              : 
     669           96 :     switch (ClassNo) {
     670           79 :     case SurfaceClass::Wall: {
     671           79 :         ClassName = "Wall";
     672           79 :     } break;
     673            7 :     case SurfaceClass::Floor: {
     674            7 :         ClassName = "Floor";
     675            7 :     } break;
     676            7 :     case SurfaceClass::Roof: {
     677            7 :         ClassName = "Roof";
     678            7 :     } break;
     679            3 :     case SurfaceClass::Window: {
     680            3 :         ClassName = "Window";
     681            3 :     } break;
     682            0 :     case SurfaceClass::GlassDoor: {
     683            0 :         ClassName = "Glass Door";
     684            0 :     } break;
     685            0 :     case SurfaceClass::Door: {
     686            0 :         ClassName = "Door";
     687            0 :     } break;
     688            0 :     case SurfaceClass::TDD_Dome: {
     689            0 :         ClassName = "TubularDaylightDome";
     690            0 :     } break;
     691            0 :     case SurfaceClass::TDD_Diffuser: {
     692            0 :         ClassName = "TubularDaylightDiffuser";
     693            0 :     } break;
     694            0 :     case SurfaceClass::IntMass: {
     695            0 :         ClassName = "Internal Mass";
     696            0 :     } break;
     697            0 :     case SurfaceClass::Shading: {
     698            0 :         ClassName = "Shading";
     699            0 :     } break;
     700            0 :     case SurfaceClass::Detached_B: {
     701            0 :         ClassName = "Detached Shading:Building";
     702            0 :     } break;
     703            0 :     case SurfaceClass::Detached_F: {
     704            0 :         ClassName = "Detached Shading:Fixed";
     705            0 :     } break;
     706            0 :     default: {
     707            0 :         ClassName = "Invalid/Unknown";
     708            0 :     } break;
     709              :     }
     710              : 
     711           96 :     return ClassName;
     712            0 : }
     713            0 : Real64 AbsFrontSide(EnergyPlusData &state, int SurfNum)
     714              : {
     715              :     Real64 AbsorptanceFromExteriorFrontSide =
     716            0 :         (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
     717            0 :         state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
     718              :     Real64 AbsorptanceFromInteriorFrontSide =
     719            0 :         (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
     720            0 :         state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
     721            0 :     return AbsorptanceFromExteriorFrontSide + AbsorptanceFromInteriorFrontSide;
     722              : }
     723              : 
     724            0 : Real64 AbsBackSide(EnergyPlusData &state, int SurfNum)
     725              : {
     726              :     Real64 AbsorptanceFromInteriorBackSide =
     727            0 :         (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
     728            0 :         state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
     729              :     Real64 AbsorptanceFromExteriorBackSide =
     730            0 :         (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
     731            0 :         state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
     732            0 :     return AbsorptanceFromExteriorBackSide + AbsorptanceFromInteriorBackSide;
     733              : }
     734              : 
     735          116 : void GetVariableAbsorptanceSurfaceList(EnergyPlusData &state)
     736              : {
     737          116 :     if (!state.dataMaterial->AnyVariableAbsorptance) return;
     738            0 :     for (int surfNum : state.dataSurface->AllHTSurfaceList) {
     739            0 :         auto const &thisSurface = state.dataSurface->Surface(surfNum);
     740            0 :         auto const &thisConstruct = state.dataConstruction->Construct(thisSurface.Construction);
     741            0 :         if (thisConstruct.TotLayers == 0) continue;
     742            0 :         if (thisConstruct.LayerPoint(1) == 0) continue; // error finding material number
     743            0 :         auto const *mat = state.dataMaterial->materials(thisConstruct.LayerPoint(1));
     744            0 :         if (mat->group != Material::Group::Regular) continue;
     745              : 
     746            0 :         if (mat->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
     747              :             // check for dynamic coating defined on interior surface
     748            0 :             if (thisSurface.ExtBoundCond != ExternalEnvironment) {
     749            0 :                 ShowWarningError(state,
     750            0 :                                  format("MaterialProperty:VariableAbsorptance defined on an interior surface, {}. This VariableAbsorptance property "
     751              :                                         "will be ignored here",
     752            0 :                                         thisSurface.Name));
     753              :             } else {
     754            0 :                 state.dataSurface->AllVaryAbsOpaqSurfaceList.push_back(surfNum);
     755              :             }
     756              :         }
     757              :     }
     758              :     // check for dynamic coating defined on the non-outside layer of a construction
     759            0 :     for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
     760            0 :         auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     761            0 :         for (int Layer = 2; Layer <= thisConstruct.TotLayers; ++Layer) {
     762            0 :             auto const *mat = state.dataMaterial->materials(thisConstruct.LayerPoint(Layer));
     763            0 :             if (mat->group != Material::Group::Regular) continue;
     764            0 :             if (mat->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
     765            0 :                 ShowWarningError(state,
     766            0 :                                  format("MaterialProperty:VariableAbsorptance defined on a inside-layer materials, {}. This VariableAbsorptance "
     767              :                                         "property will be ignored here",
     768            0 :                                         mat->Name));
     769              :             }
     770              :         }
     771              :     }
     772              : } // GetVariableAbsorptanceSurfaceList()
     773              : 
     774            0 : Compass4 AzimuthToCompass4(Real64 azimuth)
     775              : {
     776            0 :     assert(azimuth >= 0.0 && azimuth < 360.0);
     777            0 :     for (int c4 = 0; c4 < static_cast<int>(Compass4::Num); ++c4) {
     778            0 :         Real64 lo = Compass4AzimuthLo[c4];
     779            0 :         Real64 hi = Compass4AzimuthHi[c4];
     780            0 :         if (lo > hi) {
     781            0 :             if (azimuth >= lo || azimuth < hi) return static_cast<Compass4>(c4);
     782              :         } else {
     783            0 :             if (azimuth >= lo && azimuth < hi) return static_cast<Compass4>(c4);
     784              :         }
     785              :     }
     786            0 :     assert(false);
     787              :     return Compass4::Invalid;
     788              : }
     789              : 
     790          806 : Compass8 AzimuthToCompass8(Real64 azimuth)
     791              : {
     792          806 :     assert(azimuth >= 0.0 && azimuth < 360.0);
     793         3390 :     for (int c8 = 0; c8 < static_cast<int>(Compass8::Num); ++c8) {
     794         3390 :         Real64 lo = Compass8AzimuthLo[c8];
     795         3390 :         Real64 hi = Compass8AzimuthHi[c8];
     796         3390 :         if (lo > hi) {
     797          806 :             if (azimuth >= lo || azimuth < hi) return static_cast<Compass8>(c8);
     798              :         } else {
     799         2584 :             if (azimuth >= lo && azimuth < hi) return static_cast<Compass8>(c8);
     800              :         }
     801              :     }
     802            0 :     assert(false);
     803              :     return Compass8::Invalid;
     804              : }
     805              : 
     806              : } // namespace EnergyPlus::DataSurfaces
        

Generated by: LCOV version 2.0-1