LCOV - code coverage report
Current view: top level - EnergyPlus - DataSurfaces.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 379 441 85.9 %
Date: 2024-08-23 23:50:59 Functions: 23 24 95.8 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <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       43646 : Surface2D::Surface2D(ShapeCat const shapeCat, int const axis, Vertices const &v, Vector2D const &vl, Vector2D const &vu)
      91       43646 :     : axis(axis), vertices(v), vl(vl), vu(vu)
      92             : {
      93       43646 :     size_type const n(vertices.size());
      94       43646 :     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       43646 :     Real64 area(0.0); // Actually 2x the signed area
      99      219071 :     for (Vertices::size_type i = 0; i < n; ++i) {
     100      175425 :         Vector2D const &v(vertices[i]);
     101      175425 :         Vector2D const &w(vertices[(i + 1) % n]);
     102      175425 :         area += (v.x * w.y) - (w.x * v.y);
     103             :     }
     104       43646 :     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       43646 :     edges.reserve(n);
     108      219071 :     for (Vertices::size_type i = 0; i < n; ++i) {
     109      175425 :         edges.push_back(vertices[(i + 1) % n] - vertices[i]);
     110             :     }
     111       43646 :     if (shapeCat == ShapeCat::Rectangular) { // Set side length squared for ray--surface intersection tests
     112       38008 :         assert(n == 4u);
     113       38008 :         s1 = edges[0].magnitude_squared();
     114       38008 :         s3 = edges[3].magnitude_squared();
     115        5638 :     } else if ((shapeCat == ShapeCat::Nonconvex) || (n >= nVerticesBig)) { // Set up slabs
     116         197 :         assert(n >= 4u);
     117         197 :         slabYs.reserve(n);
     118        2025 :         for (size_type i = 0; i < n; ++i)
     119        1828 :             slabYs.push_back(vertices[i].y);
     120         197 :         std::sort(slabYs.begin(), slabYs.end());                     // Sort the vertex y coordinates
     121         394 :         auto const iClip(std::unique(slabYs.begin(), slabYs.end())); // Remove duplicate y-coordinate elements
     122         197 :         slabYs.erase(iClip, slabYs.end());
     123         197 :         slabYs.shrink_to_fit();
     124         884 :         for (size_type iSlab = 0, iSlab_end = slabYs.size() - 1; iSlab < iSlab_end; ++iSlab) { // Create slabs
     125         687 :             Real64 xl(std::numeric_limits<Real64>::max());
     126         687 :             Real64 xu(std::numeric_limits<Real64>::lowest());
     127         687 :             Real64 const yl(slabYs[iSlab]);
     128         687 :             Real64 const yu(slabYs[iSlab + 1]);
     129         687 :             slabs.push_back(Slab(yl, yu));
     130         687 :             Slab &slab(slabs.back());
     131             :             using CrossEdge = std::tuple<Real64, Real64, size_type>;
     132             :             using CrossEdges = std::vector<CrossEdge>;
     133         687 :             CrossEdges crossEdges;
     134        8073 :             for (size_type i = 0; i < n; ++i) { // Find edges crossing slab
     135        7386 :                 Vector2D const &v(vertices[i]);
     136        7386 :                 Vector2D const &w(vertices[(i + 1) % n]);
     137        7386 :                 if (((v.y <= yl) && (yu <= w.y)) || // Crosses upward
     138        6410 :                     ((yu <= v.y) && (w.y <= yl)))   // Crosses downward
     139             :                 {
     140        1952 :                     Edge const &e(edges[i]);
     141        1952 :                     assert(e.y != 0.0);
     142        1952 :                     Real64 const exy(e.x / e.y);
     143        1952 :                     Real64 const xb(v.x + (yl - v.y) * exy); // x_bot coordinate where edge intersects yl
     144        1952 :                     Real64 const xt(v.x + (yu - v.y) * exy); // x_top coordinate where edge intersects yu
     145        1952 :                     xl = std::min(xl, std::min(xb, xt));
     146        1952 :                     xu = std::max(xu, std::max(xb, xt));
     147        1952 :                     crossEdges.push_back(std::make_tuple(xb, xt, i));
     148             :                 }
     149             :             }
     150         687 :             slab.xl = xl;
     151         687 :             slab.xu = xu;
     152         687 :             assert(crossEdges.size() >= 2u);
     153         687 :             std::sort(crossEdges.begin(),
     154        1374 :                       crossEdges.end(),
     155        2724 :                       [](CrossEdge const &e1, CrossEdge const &e2) -> bool // Lambda to sort by x_mid
     156             :                       {
     157        2724 :                           return std::get<0>(e1) + std::get<1>(e1) <
     158        2724 :                                  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         687 :             Real64 xb(std::get<0>(crossEdges[0]));
     162         687 :             Real64 xt(std::get<1>(crossEdges[0]));
     163         687 :             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        2639 :             for (auto const &edge : crossEdges) {                            // Detect non-simple polygon with crossing edges
     165        1952 :                 Real64 const xbe(std::get<0>(edge));
     166        1952 :                 Real64 const xte(std::get<1>(edge));
     167        1952 :                 assert(xb <= xbe + tol);
     168        1952 :                 assert(xt <= xte + tol);
     169        1952 :                 xb = xbe;
     170        1952 :                 xt = xte;
     171         687 :             }
     172             : #endif
     173         687 :             assert((shapeCat == ShapeCat::Nonconvex) || (crossEdges.size() == 2u));
     174        2639 :             for (auto const &edge : crossEdges) {
     175        1952 :                 size_type const iEdge(std::get<2>(edge));
     176        1952 :                 slab.edges.push_back(iEdge); // Add edge to slab
     177        1952 :                 Vector2D const &e(edges[iEdge]);
     178        1952 :                 assert(e.y != 0.0);                                   // Constant y edge can't be a crossing edge
     179        1952 :                 slab.edgesXY.push_back(e.y != 0.0 ? e.x / e.y : 0.0); // Edge inverse slope
     180         687 :             }
     181         687 :             assert(slab.edges.size() % 2 == 0u);
     182         687 :             assert(slab.edges.size() == slab.edgesXY.size());
     183         687 :         }
     184         197 :     }
     185       43646 : }
     186             : 
     187             : // Set Precomputed Parameters
     188       46044 : void SurfaceData::set_computed_geometry()
     189             : {
     190       46044 :     if (Vertex.size() >= 3) { // Skip no-vertex "surfaces"
     191       43646 :         shapeCat = computed_shapeCat();
     192       43646 :         plane = computed_plane();
     193       43646 :         surface2d = computed_surface2d();
     194             :     }
     195       46044 : }
     196             : 
     197   469676950 : 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   469676950 :     Real64 RefAirTemp = 0;
     217             : 
     218             :     // determine reference air temperature for this surface
     219   469676950 :     auto &thisSpaceHB = state.dataZoneTempPredictorCorrector->spaceHeatBalance(this->spaceNum);
     220   469676950 :     switch (state.dataSurface->SurfTAirRef(t_SurfNum)) {
     221     2522441 :     case RefAirTemp::ZoneMeanAirTemp: {
     222     2522441 :         RefAirTemp = thisSpaceHB.MAT;
     223     2522441 :     } break;
     224     4023016 :     case RefAirTemp::AdjacentAirTemp: {
     225     4023016 :         RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(t_SurfNum);
     226     4023016 :     } break;
     227           0 :     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           0 :         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           0 :         Real64 SumSysMCp = 0;
     240           0 :         Real64 SumSysMCpT = 0;
     241           0 :         auto &inletNodes = (state.dataHeatBal->doSpaceHeatBalance) ? state.dataZoneEquip->spaceEquipConfig(this->spaceNum).InletNode
     242           0 :                                                                    : state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode;
     243           0 :         for (int nodeNum : inletNodes) {
     244           0 :             auto &inNode = state.dataLoopNodes->Node(nodeNum);
     245           0 :             Real64 CpAir = PsyCpAirFnW(thisSpaceHB.airHumRat);
     246           0 :             SumSysMCp += inNode.MassFlowRate * CpAir;
     247           0 :             SumSysMCpT += inNode.MassFlowRate * CpAir * inNode.Temp;
     248             :         }
     249             :         // a weighted average of the inlet temperatures.
     250           0 :         if (SumSysMCp > 0.0) {
     251             :             // a weighted average of the inlet temperatures.
     252           0 :             RefAirTemp = SumSysMCpT / SumSysMCp;
     253             :         } else {
     254           0 :             RefAirTemp = thisSpaceHB.MAT;
     255             :         }
     256           0 :     } break;
     257   463131493 :     default: {
     258             :         // currently set to mean air temp but should add error warning here
     259   463131493 :         RefAirTemp = thisSpaceHB.MAT;
     260   463131493 :     } break;
     261             :     }
     262             : 
     263   469676950 :     return RefAirTemp;
     264             : }
     265             : 
     266        8064 : 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        8064 :     Real64 temperature = 0;
     281             : 
     282        8064 :     if (ExtBoundCond > 0) // Interzone window
     283             :     {
     284           0 :         temperature = getInsideAirTemperature(state, t_SurfNum);
     285             :     } else {
     286        8064 :         if (ExtWind) {
     287             :             // Window is exposed to wind (and possibly rain)
     288        8064 :             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        8064 :                 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        8064 :     return temperature;
     302             : }
     303             : 
     304        4032 : 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        4032 :     Real64 value = 0;
     315        4032 :     if (ExtBoundCond > 0) {
     316           0 :         value = state.dataSurface->SurfWinIRfromParentZone(ExtBoundCond) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(ExtBoundCond);
     317             :     } else {
     318        4032 :         Real64 tout = getOutsideAirTemperature(state, t_SurfNum) + Constant::Kelvin;
     319        4032 :         value = Constant::StefanBoltzmann * pow_4(tout);
     320        4032 :         value =
     321        4032 :             ViewFactorSkyIR * (state.dataSurface->SurfAirSkyRadSplit(t_SurfNum) * Constant::StefanBoltzmann * pow_4(state.dataEnvrn->SkyTempKelvin) +
     322        4032 :                                (1.0 - state.dataSurface->SurfAirSkyRadSplit(t_SurfNum)) * value) +
     323        4032 :             ViewFactorGroundIR * value;
     324             :     }
     325        4032 :     return value;
     326             : }
     327             : 
     328        4032 : 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        4032 :     return state.dataHeatBal->SurfQRadSWOutIncident(t_SurfNum) +
     340        4032 :            state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(t_SurfNum).SolarEnclIndex);
     341             : }
     342             : 
     343       10752 : 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       10752 :     auto &construction(state.dataConstruction->Construct(Construction));
     355       10752 :     return construction.TotLayers;
     356             : }
     357             : 
     358             : // Computed Shape Category
     359       43646 : ShapeCat SurfaceData::computed_shapeCat() const
     360             : {
     361       43646 :     if (Shape == SurfaceShape::Triangle) {
     362         197 :         return ShapeCat::Triangular;
     363       43449 :     } else if (Shape == SurfaceShape::TriangularWindow) {
     364           2 :         return ShapeCat::Triangular;
     365       43447 :     } else if (Shape == SurfaceShape::TriangularDoor) {
     366           0 :         return ShapeCat::Triangular;
     367       43447 :     } else if (Shape == SurfaceShape::Rectangle) {
     368       31314 :         return ShapeCat::Rectangular;
     369       12133 :     } else if (Shape == SurfaceShape::RectangularDoorWindow) {
     370        6694 :         return ShapeCat::Rectangular;
     371        5439 :     } else if (Shape == SurfaceShape::RectangularOverhang) {
     372           0 :         return ShapeCat::Rectangular;
     373        5439 :     } else if (Shape == SurfaceShape::RectangularLeftFin) {
     374           0 :         return ShapeCat::Rectangular;
     375        5439 :     } else if (Shape == SurfaceShape::RectangularRightFin) {
     376           0 :         return ShapeCat::Rectangular;
     377        5439 :     } else if (IsConvex) {
     378        5242 :         return ShapeCat::Convex;
     379             :     } else {
     380         197 :         return ShapeCat::Nonconvex;
     381             :     }
     382             : }
     383             : 
     384             : // Computed Plane
     385       87292 : SurfaceData::Plane SurfaceData::computed_plane() const
     386             : {
     387       87292 :     Vertices::size_type const n(Vertex.size());
     388       87292 :     assert(n >= 3);
     389       87292 :     Vector center(0.0);                           // Center (vertex average) point (not mass centroid)
     390       87292 :     Real64 a(0.0), b(0.0), c(0.0), d(0.0);        // Plane coefficients
     391      438142 :     for (Vertices::size_type i = 0; i < n; ++i) { // Newell's method for robustness (not speed)
     392      350850 :         Vector const &v(Vertex[i]);
     393      350850 :         Vector const &w(Vertex[(i + 1) % n]);
     394      350850 :         a += (v.y - w.y) * (v.z + w.z);
     395      350850 :         b += (v.z - w.z) * (v.x + w.x);
     396      350850 :         c += (v.x - w.x) * (v.y + w.y);
     397      350850 :         center += v;
     398             :     }
     399       87292 :     d = -(dot(center, Vector(a, b, c)) / n); // center/n is the center point
     400       87292 :     return Plane(a, b, c, d);                // a*x + b*y + c*z + d = 0
     401       87292 : }
     402             : 
     403             : // Computed axis-projected 2D surface
     404       43646 : Surface2D SurfaceData::computed_surface2d() const
     405             : {
     406             :     // Project along axis of min surface range for 2D intersection use
     407       43646 :     Vertices::size_type const n(Vertex.size());
     408       43646 :     assert(n >= 3);
     409       43646 :     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       43646 :     Real64 const a(std::abs(plane.x));                                       // Plane normal x coordinate magnitude
     415       43646 :     Real64 const b(std::abs(plane.y));                                       // Plane normal y coordinate magnitude
     416       43646 :     Real64 const c(std::abs(plane.z));                                       // Plane normal z coordinate magnitude
     417       43646 :     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       43646 :     Vertices2D v2d(n);
     421       43646 :     Vector const &v0(Vertex[0]);
     422       43646 :     if (axis == 0) {               // Use y,z for 2D surface
     423       13481 :         Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
     424       13481 :         Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
     425       67317 :         for (Vertices::size_type i = 0; i < n; ++i) {
     426       53836 :             Vector const &v(Vertex[i]);
     427       53836 :             v2d[i] = Vertex2D(v.y, v.z);
     428       53836 :             yl = std::min(yl, v.y);
     429       53836 :             yu = std::max(yu, v.y);
     430       53836 :             zl = std::min(zl, v.z);
     431       53836 :             zu = std::max(zu, v.z);
     432             :         }
     433       26962 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(yl, zl), Vertex2D(yu, zu));
     434       30165 :     } else if (axis == 1) {        // Use x,z for 2D surface
     435       15639 :         Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
     436       15639 :         Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
     437       78157 :         for (Vertices::size_type i = 0; i < n; ++i) {
     438       62518 :             Vector const &v(Vertex[i]);
     439       62518 :             v2d[i] = Vertex2D(v.x, v.z);
     440       62518 :             xl = std::min(xl, v.x);
     441       62518 :             xu = std::max(xu, v.x);
     442       62518 :             zl = std::min(zl, v.z);
     443       62518 :             zu = std::max(zu, v.z);
     444             :         }
     445       31278 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, zl), Vertex2D(xu, zu));
     446             :     } else {                       // Use x,y for 2D surface
     447       14526 :         Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
     448       14526 :         Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
     449       73597 :         for (Vertices::size_type i = 0; i < n; ++i) {
     450       59071 :             Vector const &v(Vertex[i]);
     451       59071 :             v2d[i] = Vertex2D(v.x, v.y);
     452       59071 :             xl = std::min(xl, v.x);
     453       59071 :             xu = std::max(xu, v.x);
     454       59071 :             yl = std::min(yl, v.y);
     455       59071 :             yu = std::max(yu, v.y);
     456             :         }
     457       29052 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, yl), Vertex2D(xu, yu));
     458             :     }
     459       43646 : }
     460             : 
     461          15 : Real64 SurfaceData::get_average_height(EnergyPlusData &state) const
     462             : {
     463          15 :     if (std::abs(SinTilt) < Constant::SmallDistance) {
     464           0 :         return 0.0;
     465             :     }
     466             :     using Vertex2D = ObjexxFCL::Vector2<Real64>;
     467             :     using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
     468          15 :     Vertices::size_type const n(Vertex.size());
     469          15 :     assert(n >= 3);
     470             : 
     471          15 :     Vertices2D v2d(n);
     472             : 
     473             :     // project onto 2D vertical plane
     474          15 :     Real64 xRef = Vertex[0].x;
     475          15 :     Real64 yRef = Vertex[0].y;
     476          15 :     Real64 const &saz(SinAzim);
     477          15 :     Real64 const &caz(CosAzim);
     478          75 :     for (Vertices::size_type i = 0; i < n; ++i) {
     479          60 :         Vector const &v(Vertex[i]);
     480          60 :         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          15 :     Real64 minX(v2d[0].x), maxX(v2d[0].x);
     487          75 :     for (Vertices::size_type i = 0; i < n; ++i) {
     488          60 :         Vertex2D const &v(v2d[i]);
     489          60 :         minX = std::min(minX, v.x);
     490          60 :         maxX = std::max(maxX, v.x);
     491             :     }
     492          15 :     Real64 totalWidth = maxX - minX;
     493             : 
     494          15 :     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          15 :     Real64 averageHeight = 0.0;
     501          75 :     for (Vertices::size_type i = 0; i < n; ++i) {
     502          60 :         Vertex2D const &v(v2d[i]);
     503             : 
     504             :         Vertex2D *v2;
     505          60 :         if (i == n - 1) {
     506          15 :             v2 = &v2d[0];
     507             :         } else {
     508          45 :             v2 = &v2d[i + 1];
     509             :         }
     510          60 :         averageHeight += 0.5 * (v.y + v2->y) * (v2->x - v.x) / totalWidth;
     511             :     }
     512          15 :     return std::abs(averageHeight) / SinTilt;
     513          15 : }
     514             : 
     515         579 : void SurfaceData::make_hash_key(EnergyPlusData &state, const int SurfNum)
     516             : {
     517         579 :     calcHashKey = SurfaceCalcHashKey();
     518         579 :     calcHashKey.Construction = Construction;
     519         579 :     calcHashKey.Azimuth = round(Azimuth * 10.0) / 10.0;
     520         579 :     calcHashKey.Tilt = round(Tilt * 10.0) / 10.0;
     521         579 :     calcHashKey.Height = round(Height * 10.0) / 10.0;
     522         579 :     calcHashKey.Zone = Zone;
     523         579 :     calcHashKey.EnclIndex = SolarEnclIndex;
     524         579 :     calcHashKey.TAirRef = state.dataSurface->SurfTAirRef(SurfNum);
     525             : 
     526         579 :     int extBoundCond = state.dataSurface->Surface(SurfNum).ExtBoundCond;
     527         579 :     if (extBoundCond > 0) {
     528         256 :         calcHashKey.ExtZone = state.dataSurface->Surface(extBoundCond).Zone;
     529         256 :         calcHashKey.ExtEnclIndex = state.dataSurface->Surface(extBoundCond).SolarEnclIndex;
     530         256 :         calcHashKey.ExtCond = 1;
     531             :     } else {
     532         323 :         calcHashKey.ExtZone = 0;
     533         323 :         calcHashKey.ExtEnclIndex = 0;
     534         323 :         calcHashKey.ExtCond = extBoundCond;
     535             :     }
     536             : 
     537         579 :     calcHashKey.ExtSolar = ExtSolar;
     538         579 :     calcHashKey.ExtWind = ExtWind;
     539         579 :     calcHashKey.ViewFactorGround = round(ViewFactorGround * 10.0) / 10.0;
     540         579 :     calcHashKey.ViewFactorSky = round(ViewFactorSky * 10.0) / 10.0;
     541             : 
     542         579 :     calcHashKey.HeatTransferAlgorithm = HeatTransferAlgorithm;
     543         579 :     calcHashKey.intConvModel = state.dataSurface->surfIntConv(SurfNum).model;
     544         579 :     calcHashKey.extConvModel = state.dataSurface->surfExtConv(SurfNum).model;
     545         579 :     calcHashKey.intConvUserModelNum = state.dataSurface->surfIntConv(SurfNum).userModelNum;
     546         579 :     calcHashKey.extConvUserModelNum = state.dataSurface->surfExtConv(SurfNum).userModelNum;
     547         579 :     calcHashKey.OSCPtr = OSCPtr;
     548         579 :     calcHashKey.OSCMPtr = OSCMPtr;
     549             : 
     550         579 :     calcHashKey.FrameDivider = FrameDivider;
     551         579 :     calcHashKey.SurfWinStormWinConstr = state.dataSurface->SurfWinStormWinConstr(SurfNum);
     552             : 
     553         579 :     calcHashKey.MaterialMovInsulExt = state.dataSurface->SurfMaterialMovInsulExt(SurfNum);
     554         579 :     calcHashKey.MaterialMovInsulInt = state.dataSurface->SurfMaterialMovInsulInt(SurfNum);
     555         579 :     calcHashKey.SchedMovInsulExt = state.dataSurface->SurfSchedMovInsulExt(SurfNum);
     556         579 :     calcHashKey.SchedMovInsulInt = state.dataSurface->SurfSchedMovInsulInt(SurfNum);
     557         579 :     calcHashKey.ExternalShadingSchInd = state.dataSurface->Surface(SurfNum).SurfExternalShadingSchInd;
     558         579 :     calcHashKey.SurroundingSurfacesNum = state.dataSurface->Surface(SurfNum).SurfSurroundingSurfacesNum;
     559         579 :     calcHashKey.LinkedOutAirNode = state.dataSurface->Surface(SurfNum).SurfLinkedOutAirNode;
     560         579 :     calcHashKey.OutsideHeatSourceTermSchedule = OutsideHeatSourceTermSchedule;
     561         579 :     calcHashKey.InsideHeatSourceTermSchedule = InsideHeatSourceTermSchedule;
     562         579 :     calcHashKey.ViewFactorSrdSurfs = state.dataSurface->Surface(SurfNum).ViewFactorSrdSurfs;
     563         579 : }
     564             : 
     565         579 : void SurfaceData::set_representative_surface(EnergyPlusData &state, const int SurfNum)
     566             : {
     567             :     // Make hash key for this surface (used to determine uniqueness)
     568         579 :     state.dataSurface->Surface(SurfNum).make_hash_key(state, SurfNum);
     569             :     // Insert surface key into map. If key already exists, it will not be added.
     570             :     // Assign the representative surface number based on the first instance of the identical key
     571         579 :     state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum =
     572        1158 :         state.dataSurface->RepresentativeSurfaceMap.insert({state.dataSurface->Surface(SurfNum).calcHashKey, SurfNum}).first->second;
     573             : 
     574         579 :     state.dataSurface->Surface(state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum).ConstituentSurfaceNums.push_back(SurfNum);
     575         579 : }
     576             : 
     577             : // Functions
     578             : 
     579     2804678 : void SetSurfaceOutBulbTempAt(EnergyPlusData &state)
     580             : {
     581     2804678 :     if (state.dataEnvrn->SiteTempGradient == 0.0) {
     582           0 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     583           0 :             state.dataSurface->SurfOutDryBulbTemp(SurfNum) = state.dataEnvrn->OutDryBulbTemp;
     584           0 :             state.dataSurface->SurfOutWetBulbTemp(SurfNum) = state.dataEnvrn->OutWetBulbTemp;
     585             :         }
     586             :     } else {
     587     2804678 :         Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     588     2804678 :         Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     589   179412538 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     590             :             // Base temperatures at Z = 0 (C)
     591   176607860 :             Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
     592   176607860 :             if (Z <= 0.0) {
     593    24211251 :                 state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp;
     594    24211251 :                 state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp;
     595             :             } else {
     596   152396609 :                 Real64 GradientDividend = state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z;
     597   152396609 :                 Real64 GradientDivisor = DataEnvironment::EarthRadius + Z;
     598   152396609 :                 state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp - GradientDividend / GradientDivisor;
     599   152396609 :                 state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp - GradientDividend / GradientDivisor;
     600             :             }
     601             :         }
     602             :     }
     603     2804678 : }
     604             : 
     605     2804678 : void CheckSurfaceOutBulbTempAt(EnergyPlusData &state)
     606             : {
     607             :     // Using/Aliasing
     608             :     using DataEnvironment::SetOutBulbTempAt_error;
     609             : 
     610     2804678 :     Real64 minBulb = 0.0;
     611   179412538 :     for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     612   176607860 :         minBulb = min(minBulb, state.dataSurface->SurfOutDryBulbTemp(SurfNum), state.dataSurface->SurfOutWetBulbTemp(SurfNum));
     613   176607860 :         if (minBulb < -100.0)
     614           0 :             SetOutBulbTempAt_error(state, "Surface", state.dataSurface->Surface(SurfNum).Centroid.z, state.dataSurface->Surface(SurfNum).Name);
     615             :     }
     616     2804678 : }
     617             : 
     618     2804678 : void SetSurfaceWindSpeedAt(EnergyPlusData &state)
     619             : {
     620     2804678 :     Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
     621     2804678 :                      std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
     622     2804678 :     if (state.dataEnvrn->SiteWindExp == 0.0) {
     623           0 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     624           0 :             state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataEnvrn->WindSpeed;
     625             :         }
     626             :     } else {
     627             : 
     628   179412538 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     629   176607860 :             if (!state.dataSurface->Surface(SurfNum).ExtWind) continue;
     630    69659398 :             Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
     631    69659398 :             if (Z <= 0.0) {
     632       72555 :                 state.dataSurface->SurfOutWindSpeed(SurfNum) = 0.0;
     633             :             } else {
     634             :                 //  [Met] - at meterological Station, Height of measurement is usually 10m above ground
     635             :                 //  LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
     636             :                 //                     * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
     637    69586843 :                 state.dataSurface->SurfOutWindSpeed(SurfNum) = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
     638             :             }
     639             :         }
     640             :     }
     641     2804678 : }
     642             : 
     643     2804678 : void SetSurfaceWindDirAt(EnergyPlusData &state)
     644             : {
     645   179412538 :     for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     646   176607860 :         state.dataSurface->SurfOutWindDir(SurfNum) = state.dataEnvrn->WindDir;
     647             :     }
     648     2804678 : }
     649             : 
     650       40503 : std::string cSurfaceClass(SurfaceClass const ClassNo)
     651             : {
     652             : 
     653             :     // FUNCTION INFORMATION:
     654             :     //       AUTHOR         Linda Lawrie
     655             :     //       DATE WRITTEN   May 2006
     656             :     //       MODIFIED       na
     657             :     //       RE-ENGINEERED  na
     658             : 
     659             :     // PURPOSE OF THIS FUNCTION:
     660             :     // This function returns a string based on class number.
     661             : 
     662             :     // Return value
     663       40503 :     std::string ClassName;
     664             : 
     665       40503 :     switch (ClassNo) {
     666       19248 :     case SurfaceClass::Wall: {
     667       19248 :         ClassName = "Wall";
     668       19248 :     } break;
     669        6010 :     case SurfaceClass::Floor: {
     670        6010 :         ClassName = "Floor";
     671        6010 :     } break;
     672        5119 :     case SurfaceClass::Roof: {
     673        5119 :         ClassName = "Roof";
     674        5119 :     } break;
     675        5716 :     case SurfaceClass::Window: {
     676        5716 :         ClassName = "Window";
     677        5716 :     } break;
     678           0 :     case SurfaceClass::GlassDoor: {
     679           0 :         ClassName = "Glass Door";
     680           0 :     } break;
     681         424 :     case SurfaceClass::Door: {
     682         424 :         ClassName = "Door";
     683         424 :     } break;
     684           4 :     case SurfaceClass::TDD_Dome: {
     685           4 :         ClassName = "TubularDaylightDome";
     686           4 :     } break;
     687           0 :     case SurfaceClass::TDD_Diffuser: {
     688           0 :         ClassName = "TubularDaylightDiffuser";
     689           0 :     } break;
     690        3314 :     case SurfaceClass::IntMass: {
     691        3314 :         ClassName = "Internal Mass";
     692        3314 :     } break;
     693         580 :     case SurfaceClass::Shading: {
     694         580 :         ClassName = "Shading";
     695         580 :     } break;
     696          34 :     case SurfaceClass::Detached_B: {
     697          34 :         ClassName = "Detached Shading:Building";
     698          34 :     } break;
     699          54 :     case SurfaceClass::Detached_F: {
     700          54 :         ClassName = "Detached Shading:Fixed";
     701          54 :     } break;
     702           0 :     default: {
     703           0 :         ClassName = "Invalid/Unknown";
     704           0 :     } break;
     705             :     }
     706             : 
     707       40503 :     return ClassName;
     708           0 : }
     709      186115 : Real64 AbsFrontSide(EnergyPlusData &state, int SurfNum)
     710             : {
     711             :     Real64 AbsorptanceFromExteriorFrontSide =
     712      186115 :         (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
     713      186115 :         state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
     714             :     Real64 AbsorptanceFromInteriorFrontSide =
     715      186115 :         (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
     716      186115 :         state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
     717      186115 :     return AbsorptanceFromExteriorFrontSide + AbsorptanceFromInteriorFrontSide;
     718             : }
     719             : 
     720      186115 : Real64 AbsBackSide(EnergyPlusData &state, int SurfNum)
     721             : {
     722             :     Real64 AbsorptanceFromInteriorBackSide =
     723      186115 :         (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
     724      186115 :         state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
     725             :     Real64 AbsorptanceFromExteriorBackSide =
     726      186115 :         (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
     727      186115 :         state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
     728      186115 :     return AbsorptanceFromExteriorBackSide + AbsorptanceFromInteriorBackSide;
     729             : }
     730             : 
     731         796 : void GetVariableAbsorptanceSurfaceList(EnergyPlusData &state)
     732             : {
     733         796 :     if (!state.dataHeatBal->AnyVariableAbsorptance) return;
     734           7 :     for (int surfNum : state.dataSurface->AllHTSurfaceList) {
     735           6 :         auto const &thisSurface = state.dataSurface->Surface(surfNum);
     736           6 :         int ConstrNum = thisSurface.Construction;
     737           6 :         auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     738           6 :         int TotLayers = thisConstruct.TotLayers;
     739           6 :         if (TotLayers == 0) continue;
     740           6 :         int materNum = thisConstruct.LayerPoint(1);
     741           6 :         if (materNum == 0) continue; // error finding material number
     742           6 :         auto const *thisMaterial = dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(materNum));
     743           6 :         assert(thisMaterial != nullptr);
     744           6 :         if (thisMaterial->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
     745             :             // check for dynamic coating defined on interior surface
     746           1 :             if (thisSurface.ExtBoundCond != ExternalEnvironment) {
     747           0 :                 ShowWarningError(state,
     748           0 :                                  format("MaterialProperty:VariableAbsorptance defined on an interior surface, {}. This VariableAbsorptance property "
     749             :                                         "will be ignored here",
     750           0 :                                         thisSurface.Name));
     751             :             } else {
     752           1 :                 state.dataSurface->AllVaryAbsOpaqSurfaceList.push_back(surfNum);
     753             :             }
     754             :         }
     755           1 :     }
     756             :     // check for dynamic coating defined on the non-outside layer of a construction
     757           5 :     for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
     758           4 :         auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     759           4 :         for (int Layer = 2; Layer <= thisConstruct.TotLayers; ++Layer) {
     760           0 :             auto const *thisMaterial = dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(thisConstruct.LayerPoint(Layer)));
     761           0 :             if (thisMaterial->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
     762           0 :                 ShowWarningError(state,
     763           0 :                                  format("MaterialProperty:VariableAbsorptance defined on a inside-layer materials, {}. This VariableAbsorptance "
     764             :                                         "property will be ignored here",
     765           0 :                                         thisMaterial->Name));
     766             :             }
     767             :         }
     768             :     }
     769             : }
     770             : 
     771           0 : Compass4 AzimuthToCompass4(Real64 azimuth)
     772             : {
     773           0 :     assert(azimuth >= 0.0 && azimuth < 360.0);
     774           0 :     for (int c4 = 0; c4 < static_cast<int>(Compass4::Num); ++c4) {
     775           0 :         Real64 lo = Compass4AzimuthLo[c4];
     776           0 :         Real64 hi = Compass4AzimuthHi[c4];
     777           0 :         if (lo > hi) {
     778           0 :             if (azimuth >= lo || azimuth < hi) return static_cast<Compass4>(c4);
     779             :         } else {
     780           0 :             if (azimuth >= lo && azimuth < hi) return static_cast<Compass4>(c4);
     781             :         }
     782             :     }
     783           0 :     assert(false);
     784             :     return Compass4::Invalid;
     785             : }
     786             : 
     787       30206 : Compass8 AzimuthToCompass8(Real64 azimuth)
     788             : {
     789       30206 :     assert(azimuth >= 0.0 && azimuth < 360.0);
     790      125704 :     for (int c8 = 0; c8 < static_cast<int>(Compass8::Num); ++c8) {
     791      125704 :         Real64 lo = Compass8AzimuthLo[c8];
     792      125704 :         Real64 hi = Compass8AzimuthHi[c8];
     793      125704 :         if (lo > hi) {
     794       30206 :             if (azimuth >= lo || azimuth < hi) return static_cast<Compass8>(c8);
     795             :         } else {
     796       95498 :             if (azimuth >= lo && azimuth < hi) return static_cast<Compass8>(c8);
     797             :         }
     798             :     }
     799           0 :     assert(false);
     800             :     return Compass8::Invalid;
     801             : }
     802             : 
     803             : } // namespace EnergyPlus::DataSurfaces

Generated by: LCOV version 1.14