LCOV - code coverage report
Current view: top level - EnergyPlus - DataSurfaces.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 84.7 % 452 383
Test Date: 2025-06-02 07:23:51 Functions: 95.8 % 24 23

            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        44753 : Surface2D::Surface2D(ShapeCat const shapeCat, int const axis, Vertices const &v, Vector2D const &vl, Vector2D const &vu)
      91        44753 :     : axis(axis), vertices(v), vl(vl), vu(vu)
      92              : {
      93        44753 :     size_type const n(vertices.size());
      94        44753 :     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        44753 :     Real64 area(0.0); // Actually 2x the signed area
      99       224606 :     for (Vertices::size_type i = 0; i < n; ++i) {
     100       179853 :         Vector2D const &v(vertices[i]);
     101       179853 :         Vector2D const &w(vertices[(i + 1) % n]);
     102       179853 :         area += (v.x * w.y) - (w.x * v.y);
     103              :     }
     104        44753 :     if (area < 0.0) {
     105        22364 :         std::reverse(vertices.begin() + 1, vertices.end()); // Vertices in clockwise order: Reverse all but first
     106              :     }
     107              : 
     108              :     // Set up edge vectors for ray--surface intersection tests
     109        44753 :     edges.reserve(n);
     110       224606 :     for (Vertices::size_type i = 0; i < n; ++i) {
     111       179853 :         edges.push_back(vertices[(i + 1) % n] - vertices[i]);
     112              :     }
     113        44753 :     if (shapeCat == ShapeCat::Rectangular) { // Set side length squared for ray--surface intersection tests
     114        39067 :         assert(n == 4u);
     115        39067 :         s1 = edges[0].magnitude_squared();
     116        39067 :         s3 = edges[3].magnitude_squared();
     117         5686 :     } else if ((shapeCat == ShapeCat::Nonconvex) || (n >= nVerticesBig)) { // Set up slabs
     118          197 :         assert(n >= 4u);
     119          197 :         slabYs.reserve(n);
     120         2025 :         for (size_type i = 0; i < n; ++i) {
     121         1828 :             slabYs.push_back(vertices[i].y);
     122              :         }
     123          197 :         std::sort(slabYs.begin(), slabYs.end());                     // Sort the vertex y coordinates
     124          197 :         auto const iClip(std::unique(slabYs.begin(), slabYs.end())); // Remove duplicate y-coordinate elements
     125          197 :         slabYs.erase(iClip, slabYs.end());
     126          197 :         slabYs.shrink_to_fit();
     127          884 :         for (size_type iSlab = 0, iSlab_end = slabYs.size() - 1; iSlab < iSlab_end; ++iSlab) { // Create slabs
     128          687 :             Real64 xl(std::numeric_limits<Real64>::max());
     129          687 :             Real64 xu(std::numeric_limits<Real64>::lowest());
     130          687 :             Real64 const yl(slabYs[iSlab]);
     131          687 :             Real64 const yu(slabYs[iSlab + 1]);
     132          687 :             slabs.push_back(Slab(yl, yu));
     133          687 :             Slab &slab(slabs.back());
     134              :             using CrossEdge = std::tuple<Real64, Real64, size_type>;
     135              :             using CrossEdges = std::vector<CrossEdge>;
     136          687 :             CrossEdges crossEdges;
     137         8073 :             for (size_type i = 0; i < n; ++i) { // Find edges crossing slab
     138         7386 :                 Vector2D const &v(vertices[i]);
     139         7386 :                 Vector2D const &w(vertices[(i + 1) % n]);
     140         7386 :                 if (((v.y <= yl) && (yu <= w.y)) || // Crosses upward
     141         6410 :                     ((yu <= v.y) && (w.y <= yl)))   // Crosses downward
     142              :                 {
     143         1952 :                     Edge const &e(edges[i]);
     144         1952 :                     assert(e.y != 0.0);
     145         1952 :                     Real64 const exy(e.x / e.y);
     146         1952 :                     Real64 const xb(v.x + (yl - v.y) * exy); // x_bot coordinate where edge intersects yl
     147         1952 :                     Real64 const xt(v.x + (yu - v.y) * exy); // x_top coordinate where edge intersects yu
     148         1952 :                     xl = std::min(xl, std::min(xb, xt));
     149         1952 :                     xu = std::max(xu, std::max(xb, xt));
     150         1952 :                     crossEdges.push_back(std::make_tuple(xb, xt, i));
     151              :                 }
     152              :             }
     153          687 :             slab.xl = xl;
     154          687 :             slab.xu = xu;
     155          687 :             assert(crossEdges.size() >= 2u);
     156          687 :             std::sort(crossEdges.begin(),
     157         1374 :                       crossEdges.end(),
     158         2724 :                       [](CrossEdge const &e1, CrossEdge const &e2) -> bool // Lambda to sort by x_mid
     159              :                       {
     160         2724 :                           return std::get<0>(e1) + std::get<1>(e1) <
     161         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
     162              :                       });
     163              : #ifndef NDEBUG // Check x_bot and x_top are also sorted
     164          687 :             Real64 xb(std::get<0>(crossEdges[0]));
     165          687 :             Real64 xt(std::get<1>(crossEdges[0]));
     166          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
     167         2639 :             for (auto const &edge : crossEdges) {                            // Detect non-simple polygon with crossing edges
     168         1952 :                 Real64 const xbe(std::get<0>(edge));
     169         1952 :                 Real64 const xte(std::get<1>(edge));
     170         1952 :                 assert(xb <= xbe + tol);
     171         1952 :                 assert(xt <= xte + tol);
     172         1952 :                 xb = xbe;
     173         1952 :                 xt = xte;
     174          687 :             }
     175              : #endif
     176          687 :             assert((shapeCat == ShapeCat::Nonconvex) || (crossEdges.size() == 2u));
     177         2639 :             for (auto const &edge : crossEdges) {
     178         1952 :                 size_type const iEdge(std::get<2>(edge));
     179         1952 :                 slab.edges.push_back(iEdge); // Add edge to slab
     180         1952 :                 Vector2D const &e(edges[iEdge]);
     181         1952 :                 assert(e.y != 0.0);                                   // Constant y edge can't be a crossing edge
     182         1952 :                 slab.edgesXY.push_back(e.y != 0.0 ? e.x / e.y : 0.0); // Edge inverse slope
     183          687 :             }
     184          687 :             assert(slab.edges.size() % 2 == 0u);
     185          687 :             assert(slab.edges.size() == slab.edgesXY.size());
     186          687 :         }
     187          197 :     }
     188        44753 : }
     189              : 
     190              : // Set Precomputed Parameters
     191        47290 : void SurfaceData::set_computed_geometry()
     192              : {
     193        47290 :     if (Vertex.size() >= 3) { // Skip no-vertex "surfaces"
     194        44753 :         shapeCat = computed_shapeCat();
     195        44753 :         plane = computed_plane();
     196        44753 :         surface2d = computed_surface2d();
     197              :     }
     198        47290 : }
     199              : 
     200    487901982 : Real64 SurfaceData::getInsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
     201              : {
     202              :     // SUBROUTINE INFORMATION:
     203              :     //       AUTHOR         Simon Vidanovic
     204              :     //       DATE WRITTEN   June 2016
     205              :     //       MODIFIED       na
     206              :     //       RE-ENGINEERED  na
     207              : 
     208              :     // PURPOSE OF THIS SUBROUTINE:
     209              :     // Routine calculates reference air temperature for given surface (refactoring from the code)
     210              :     //
     211              :     // NOTE: This routine has been copy/pasted in the past in several different modules with slight
     212              :     //       modifications at some of those places. It is quite logical that reference air temperature
     213              :     //       for the surface is calculated as public function of SurfaceData structure (class) and is
     214              :     //       later called as needed. Note that SurfaceNum had to be passed to this routine because of
     215              :     //       access to global array SurfTempEffBulkAir. I would propose refactoring where SurfTempEffBulkAir
     216              :     //       is part of SurfaceData structure and instead of calling SurfTempEffBulkAir( SurfNum ) it should
     217              :     //       be called Surface( SurfNum ).TempEffBulkAir (Simon Vidanovic)
     218              : 
     219    487901982 :     Real64 RefAirTemp = 0;
     220              : 
     221              :     // determine reference air temperature for this surface
     222    487901982 :     auto &thisSpaceHB = state.dataZoneTempPredictorCorrector->spaceHeatBalance(this->spaceNum);
     223    487901982 :     switch (state.dataSurface->SurfTAirRef(t_SurfNum)) {
     224      2522441 :     case RefAirTemp::ZoneMeanAirTemp: {
     225      2522441 :         RefAirTemp = thisSpaceHB.MAT;
     226      2522441 :     } break;
     227      4057129 :     case RefAirTemp::AdjacentAirTemp: {
     228      4057129 :         RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(t_SurfNum);
     229      4057129 :     } break;
     230            0 :     case RefAirTemp::ZoneSupplyAirTemp: {
     231              :         // determine ZoneEquipConfigNum for this zone
     232              :         //            ControlledZoneAirFlag = .FALSE.
     233              :         // ZoneEquipConfigNum = ZoneNum;
     234              :         // check whether this zone is a controlled zone or not
     235            0 :         if (!state.dataHeatBal->Zone(Zone).IsControlled) {
     236            0 :             ShowFatalError(state,
     237            0 :                            format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}",
     238            0 :                                   state.dataHeatBal->Zone(Zone).Name));
     239              :             // return;
     240              :         }
     241              :         // determine supply air conditions
     242            0 :         Real64 SumSysMCp = 0;
     243            0 :         Real64 SumSysMCpT = 0;
     244            0 :         auto const &inletNodes = (state.dataHeatBal->doSpaceHeatBalance) ? state.dataZoneEquip->spaceEquipConfig(this->spaceNum).InletNode
     245            0 :                                                                          : state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode;
     246            0 :         for (int nodeNum : inletNodes) {
     247            0 :             auto const &inNode = state.dataLoopNodes->Node(nodeNum);
     248            0 :             Real64 CpAir = PsyCpAirFnW(thisSpaceHB.airHumRat);
     249            0 :             SumSysMCp += inNode.MassFlowRate * CpAir;
     250            0 :             SumSysMCpT += inNode.MassFlowRate * CpAir * inNode.Temp;
     251              :         }
     252              :         // a weighted average of the inlet temperatures.
     253            0 :         if (SumSysMCp > 0.0) {
     254              :             // a weighted average of the inlet temperatures.
     255            0 :             RefAirTemp = SumSysMCpT / SumSysMCp;
     256              :         } else {
     257            0 :             RefAirTemp = thisSpaceHB.MAT;
     258              :         }
     259            0 :     } break;
     260    481322412 :     default: {
     261              :         // currently set to mean air temp but should add error warning here
     262    481322412 :         RefAirTemp = thisSpaceHB.MAT;
     263    481322412 :     } break;
     264              :     }
     265              : 
     266    487901982 :     return RefAirTemp;
     267              : }
     268              : 
     269         8064 : Real64 SurfaceData::getOutsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
     270              : {
     271              :     // SUBROUTINE INFORMATION:
     272              :     //       AUTHOR         Simon Vidanovic
     273              :     //       DATE WRITTEN   June 2016
     274              :     //       MODIFIED       na
     275              :     //       RE-ENGINEERED  na
     276              : 
     277              :     // PURPOSE OF THIS SUBROUTINE:
     278              :     // Routine calculates outside air temperature for given surface.
     279              :     // Routine will return inside air temperature if it is interior surface. (refactoring from the code)
     280              :     //
     281              :     // NOTE: This routine has been copy/pasted in the past in several different modules with slight
     282              :     //       modifications at some of those places. Exterior/interior surface air temperature is tied to surface.
     283         8064 :     Real64 temperature = 0;
     284              : 
     285         8064 :     if (ExtBoundCond > 0) // Interzone window
     286              :     {
     287            0 :         temperature = getInsideAirTemperature(state, t_SurfNum);
     288              :     } else {
     289         8064 :         if (ExtWind) {
     290              :             // Window is exposed to wind (and possibly rain)
     291         8064 :             if (state.dataEnvrn->IsRain) {
     292              :                 // Raining: since wind exposed, outside window surface gets wet
     293            0 :                 temperature = state.dataSurface->SurfOutWetBulbTemp(t_SurfNum);
     294              :             } else {
     295              :                 // Dry
     296         8064 :                 temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
     297              :             }
     298              :         } else {
     299              :             // Window not exposed to wind
     300            0 :             temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
     301              :         }
     302              :     }
     303              : 
     304         8064 :     return temperature;
     305              : }
     306              : 
     307         4032 : Real64 SurfaceData::getOutsideIR(EnergyPlusData &state, const int t_SurfNum) const
     308              : {
     309              :     // SUBROUTINE INFORMATION:
     310              :     //       AUTHOR         Simon Vidanovic
     311              :     //       DATE WRITTEN   July 2016
     312              :     //       MODIFIED       na
     313              :     //       RE-ENGINEERED  na
     314              : 
     315              :     // PURPOSE OF THIS SUBROUTINE:
     316              :     // Calculates outside infrared radiation
     317         4032 :     Real64 value = 0;
     318         4032 :     if (ExtBoundCond > 0) {
     319            0 :         value = state.dataSurface->SurfWinIRfromParentZone(ExtBoundCond) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(ExtBoundCond);
     320              :     } else {
     321         4032 :         Real64 tout = getOutsideAirTemperature(state, t_SurfNum) + Constant::Kelvin;
     322         4032 :         value = Constant::StefanBoltzmann * pow_4(tout);
     323         4032 :         value =
     324         4032 :             ViewFactorSkyIR * (state.dataSurface->SurfAirSkyRadSplit(t_SurfNum) * Constant::StefanBoltzmann * pow_4(state.dataEnvrn->SkyTempKelvin) +
     325         4032 :                                (1.0 - state.dataSurface->SurfAirSkyRadSplit(t_SurfNum)) * value) +
     326         4032 :             ViewFactorGroundIR * value;
     327              :     }
     328         4032 :     return value;
     329              : }
     330              : 
     331         4032 : Real64 SurfaceData::getSWIncident(EnergyPlusData &state, const int t_SurfNum)
     332              : {
     333              :     // SUBROUTINE INFORMATION:
     334              :     //       AUTHOR         Simon Vidanovic
     335              :     //       DATE WRITTEN   July 2016
     336              :     //       MODIFIED       na
     337              :     //       RE-ENGINEERED  na
     338              : 
     339              :     // PURPOSE OF THIS SUBROUTINE:
     340              :     // Return total short wave incident to the surface
     341              : 
     342         4032 :     return state.dataHeatBal->SurfQRadSWOutIncident(t_SurfNum) +
     343         4032 :            state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(t_SurfNum).SolarEnclIndex);
     344              : }
     345              : 
     346        10752 : int SurfaceData::getTotLayers(EnergyPlusData &state) const
     347              : {
     348              :     // SUBROUTINE INFORMATION:
     349              :     //       AUTHOR         Simon Vidanovic
     350              :     //       DATE WRITTEN   August 2016
     351              :     //       MODIFIED       na
     352              :     //       RE-ENGINEERED  na
     353              : 
     354              :     // PURPOSE OF THIS SUBROUTINE:
     355              :     // Returns total number of layer for current surface
     356              : 
     357        10752 :     auto &construction(state.dataConstruction->Construct(Construction));
     358        10752 :     return construction.TotLayers;
     359              : }
     360              : 
     361              : // Computed Shape Category
     362        44753 : ShapeCat SurfaceData::computed_shapeCat() const
     363              : {
     364        44753 :     if (Shape == SurfaceShape::Triangle) {
     365          197 :         return ShapeCat::Triangular;
     366        44556 :     } else if (Shape == SurfaceShape::TriangularWindow) {
     367            2 :         return ShapeCat::Triangular;
     368        44554 :     } else if (Shape == SurfaceShape::TriangularDoor) {
     369            0 :         return ShapeCat::Triangular;
     370        44554 :     } else if (Shape == SurfaceShape::Rectangle) {
     371        32229 :         return ShapeCat::Rectangular;
     372        12325 :     } else if (Shape == SurfaceShape::RectangularDoorWindow) {
     373         6838 :         return ShapeCat::Rectangular;
     374         5487 :     } else if (Shape == SurfaceShape::RectangularOverhang) {
     375            0 :         return ShapeCat::Rectangular;
     376         5487 :     } else if (Shape == SurfaceShape::RectangularLeftFin) {
     377            0 :         return ShapeCat::Rectangular;
     378         5487 :     } else if (Shape == SurfaceShape::RectangularRightFin) {
     379            0 :         return ShapeCat::Rectangular;
     380         5487 :     } else if (IsConvex) {
     381         5290 :         return ShapeCat::Convex;
     382              :     } else {
     383          197 :         return ShapeCat::Nonconvex;
     384              :     }
     385              : }
     386              : 
     387              : // Computed Plane
     388        89506 : SurfaceData::Plane SurfaceData::computed_plane() const
     389              : {
     390        89506 :     Vertices::size_type const n(Vertex.size());
     391        89506 :     assert(n >= 3);
     392        89506 :     Vector center(0.0);                           // Center (vertex average) point (not mass centroid)
     393        89506 :     Real64 a(0.0), b(0.0), c(0.0), d(0.0);        // Plane coefficients
     394       449212 :     for (Vertices::size_type i = 0; i < n; ++i) { // Newell's method for robustness (not speed)
     395       359706 :         Vector const &v(Vertex[i]);
     396       359706 :         Vector const &w(Vertex[(i + 1) % n]);
     397       359706 :         a += (v.y - w.y) * (v.z + w.z);
     398       359706 :         b += (v.z - w.z) * (v.x + w.x);
     399       359706 :         c += (v.x - w.x) * (v.y + w.y);
     400       359706 :         center += v;
     401              :     }
     402        89506 :     d = -(dot(center, Vector(a, b, c)) / n); // center/n is the center point
     403        89506 :     return Plane(a, b, c, d);                // a*x + b*y + c*z + d = 0
     404        89506 : }
     405              : 
     406              : // Computed axis-projected 2D surface
     407        44753 : Surface2D SurfaceData::computed_surface2d() const
     408              : {
     409              :     // Project along axis of min surface range for 2D intersection use
     410        44753 :     Vertices::size_type const n(Vertex.size());
     411        44753 :     assert(n >= 3);
     412        44753 :     assert(plane == computed_plane()); // Set plane first
     413              :     using Vertex2D = ObjexxFCL::Vector2<Real64>;
     414              :     using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
     415              : 
     416              :     // Select axis to project along
     417        44753 :     Real64 const a(std::abs(plane.x));                                       // Plane normal x coordinate magnitude
     418        44753 :     Real64 const b(std::abs(plane.y));                                       // Plane normal y coordinate magnitude
     419        44753 :     Real64 const c(std::abs(plane.z));                                       // Plane normal z coordinate magnitude
     420        44753 :     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
     421              : 
     422              :     // Set up 2D surface
     423        44753 :     Vertices2D v2d(n);
     424        44753 :     Vector const &v0(Vertex[0]);
     425        44753 :     if (axis == 0) {               // Use y,z for 2D surface
     426        13855 :         Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
     427        13855 :         Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
     428        69187 :         for (Vertices::size_type i = 0; i < n; ++i) {
     429        55332 :             Vector const &v(Vertex[i]);
     430        55332 :             v2d[i] = Vertex2D(v.y, v.z);
     431        55332 :             yl = std::min(yl, v.y);
     432        55332 :             yu = std::max(yu, v.y);
     433        55332 :             zl = std::min(zl, v.z);
     434        55332 :             zu = std::max(zu, v.z);
     435              :         }
     436        13855 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(yl, zl), Vertex2D(yu, zu));
     437        30898 :     } else if (axis == 1) {        // Use x,z for 2D surface
     438        16011 :         Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
     439        16011 :         Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
     440        80017 :         for (Vertices::size_type i = 0; i < n; ++i) {
     441        64006 :             Vector const &v(Vertex[i]);
     442        64006 :             v2d[i] = Vertex2D(v.x, v.z);
     443        64006 :             xl = std::min(xl, v.x);
     444        64006 :             xu = std::max(xu, v.x);
     445        64006 :             zl = std::min(zl, v.z);
     446        64006 :             zu = std::max(zu, v.z);
     447              :         }
     448        16011 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, zl), Vertex2D(xu, zu));
     449              :     } else {                       // Use x,y for 2D surface
     450        14887 :         Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
     451        14887 :         Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
     452        75402 :         for (Vertices::size_type i = 0; i < n; ++i) {
     453        60515 :             Vector const &v(Vertex[i]);
     454        60515 :             v2d[i] = Vertex2D(v.x, v.y);
     455        60515 :             xl = std::min(xl, v.x);
     456        60515 :             xu = std::max(xu, v.x);
     457        60515 :             yl = std::min(yl, v.y);
     458        60515 :             yu = std::max(yu, v.y);
     459              :         }
     460        14887 :         return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, yl), Vertex2D(xu, yu));
     461              :     }
     462        44753 : }
     463              : 
     464           16 : Real64 SurfaceData::get_average_height(EnergyPlusData &state) const
     465              : {
     466           16 :     if (std::abs(SinTilt) < Constant::SmallDistance) {
     467            0 :         return 0.0;
     468              :     }
     469              :     using Vertex2D = ObjexxFCL::Vector2<Real64>;
     470              :     using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
     471           16 :     Vertices::size_type const n(Vertex.size());
     472           16 :     assert(n >= 3);
     473              : 
     474           16 :     Vertices2D v2d(n);
     475              : 
     476              :     // project onto 2D vertical plane
     477           16 :     Real64 xRef = Vertex[0].x;
     478           16 :     Real64 yRef = Vertex[0].y;
     479           16 :     Real64 const &saz(SinAzim);
     480           16 :     Real64 const &caz(CosAzim);
     481           80 :     for (Vertices::size_type i = 0; i < n; ++i) {
     482           64 :         Vector const &v(Vertex[i]);
     483           64 :         v2d[i] = Vertex2D(-(v.x - xRef) * caz + (v.y - yRef) * saz, v.z);
     484              :     }
     485              : 
     486              :     // piecewise linear integration
     487              : 
     488              :     // Get total width of polygon
     489           16 :     Real64 minX(v2d[0].x), maxX(v2d[0].x);
     490           80 :     for (Vertices::size_type i = 0; i < n; ++i) {
     491           64 :         Vertex2D const &v(v2d[i]);
     492           64 :         minX = std::min(minX, v.x);
     493           64 :         maxX = std::max(maxX, v.x);
     494              :     }
     495           16 :     Real64 totalWidth = maxX - minX;
     496              : 
     497           16 :     if (totalWidth == 0.0) {
     498              :         // This should never happen, but if it does, print a somewhat meaningful fatal error
     499              :         // (instead of allowing a divide by zero).
     500            0 :         ShowFatalError(state, format("Calculated projected surface width is zero for surface=\"{}\"", Name));
     501              :     }
     502              : 
     503           16 :     Real64 averageHeight = 0.0;
     504           80 :     for (Vertices::size_type i = 0; i < n; ++i) {
     505           64 :         Vertex2D const &v(v2d[i]);
     506              : 
     507              :         Vertex2D *v2;
     508           64 :         if (i == n - 1) {
     509           16 :             v2 = &v2d[0];
     510              :         } else {
     511           48 :             v2 = &v2d[i + 1];
     512              :         }
     513           64 :         averageHeight += 0.5 * (v.y + v2->y) * (v2->x - v.x) / totalWidth;
     514              :     }
     515           16 :     return std::abs(averageHeight) / SinTilt;
     516           16 : }
     517              : 
     518          579 : void SurfaceData::make_hash_key(EnergyPlusData &state, const int SurfNum)
     519              : {
     520          579 :     auto &s_surf = state.dataSurface;
     521              : 
     522          579 :     calcHashKey = SurfaceCalcHashKey();
     523          579 :     calcHashKey.Construction = Construction;
     524          579 :     calcHashKey.Azimuth = round(Azimuth * 10.0) / 10.0;
     525          579 :     calcHashKey.Tilt = round(Tilt * 10.0) / 10.0;
     526          579 :     calcHashKey.Height = round(Height * 10.0) / 10.0;
     527          579 :     calcHashKey.Zone = Zone;
     528          579 :     calcHashKey.EnclIndex = SolarEnclIndex;
     529          579 :     calcHashKey.TAirRef = s_surf->SurfTAirRef(SurfNum);
     530              : 
     531          579 :     int extBoundCond = s_surf->Surface(SurfNum).ExtBoundCond;
     532          579 :     if (extBoundCond > 0) {
     533          256 :         calcHashKey.ExtZone = s_surf->Surface(extBoundCond).Zone;
     534          256 :         calcHashKey.ExtEnclIndex = s_surf->Surface(extBoundCond).SolarEnclIndex;
     535          256 :         calcHashKey.ExtCond = 1;
     536              :     } else {
     537          323 :         calcHashKey.ExtZone = 0;
     538          323 :         calcHashKey.ExtEnclIndex = 0;
     539          323 :         calcHashKey.ExtCond = extBoundCond;
     540              :     }
     541              : 
     542          579 :     calcHashKey.ExtSolar = ExtSolar;
     543          579 :     calcHashKey.ExtWind = ExtWind;
     544          579 :     calcHashKey.ViewFactorGround = round(ViewFactorGround * 10.0) / 10.0;
     545          579 :     calcHashKey.ViewFactorSky = round(ViewFactorSky * 10.0) / 10.0;
     546              : 
     547          579 :     calcHashKey.HeatTransferAlgorithm = HeatTransferAlgorithm;
     548          579 :     calcHashKey.intConvModel = s_surf->surfIntConv(SurfNum).model;
     549          579 :     calcHashKey.extConvModel = s_surf->surfExtConv(SurfNum).model;
     550          579 :     calcHashKey.intConvUserModelNum = s_surf->surfIntConv(SurfNum).userModelNum;
     551          579 :     calcHashKey.extConvUserModelNum = s_surf->surfExtConv(SurfNum).userModelNum;
     552          579 :     calcHashKey.OSCPtr = OSCPtr;
     553          579 :     calcHashKey.OSCMPtr = OSCMPtr;
     554              : 
     555          579 :     calcHashKey.FrameDivider = FrameDivider;
     556          579 :     calcHashKey.SurfWinStormWinConstr = s_surf->SurfWinStormWinConstr(SurfNum);
     557              : 
     558          579 :     calcHashKey.MaterialMovInsulExt = s_surf->extMovInsuls(SurfNum).matNum;
     559          579 :     calcHashKey.MaterialMovInsulInt = s_surf->intMovInsuls(SurfNum).matNum;
     560          579 :     calcHashKey.movInsulExtSchedNum = (s_surf->extMovInsuls(SurfNum).sched == nullptr) ? -1 : s_surf->extMovInsuls(SurfNum).sched->Num;
     561          579 :     calcHashKey.movInsulIntSchedNum = (s_surf->intMovInsuls(SurfNum).sched == nullptr) ? -1 : s_surf->intMovInsuls(SurfNum).sched->Num;
     562              : 
     563          579 :     calcHashKey.externalShadingSchedNum =
     564          579 :         (s_surf->Surface(SurfNum).surfExternalShadingSched != nullptr) ? s_surf->Surface(SurfNum).surfExternalShadingSched->Num : -1;
     565          579 :     calcHashKey.SurroundingSurfacesNum = s_surf->Surface(SurfNum).SurfSurroundingSurfacesNum;
     566          579 :     calcHashKey.LinkedOutAirNode = s_surf->Surface(SurfNum).SurfLinkedOutAirNode;
     567          579 :     calcHashKey.outsideHeatSourceTermSchedNum = (outsideHeatSourceTermSched != nullptr) ? outsideHeatSourceTermSched->Num : -1;
     568          579 :     calcHashKey.insideHeatSourceTermSchedNum = (insideHeatSourceTermSched != nullptr) ? insideHeatSourceTermSched->Num : -1;
     569          579 :     calcHashKey.ViewFactorSrdSurfs = s_surf->Surface(SurfNum).ViewFactorSrdSurfs;
     570          579 : }
     571              : 
     572          579 : void SurfaceData::set_representative_surface(EnergyPlusData &state, const int SurfNum)
     573              : {
     574              :     // Make hash key for this surface (used to determine uniqueness)
     575          579 :     state.dataSurface->Surface(SurfNum).make_hash_key(state, SurfNum);
     576              :     // Insert surface key into map. If key already exists, it will not be added.
     577              :     // Assign the representative surface number based on the first instance of the identical key
     578          579 :     state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum =
     579         1158 :         state.dataSurface->RepresentativeSurfaceMap.insert({state.dataSurface->Surface(SurfNum).calcHashKey, SurfNum}).first->second;
     580              : 
     581          579 :     state.dataSurface->Surface(state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum).ConstituentSurfaceNums.push_back(SurfNum);
     582          579 : }
     583              : 
     584              : // Functions
     585              : 
     586      2828408 : void SetSurfaceOutBulbTempAt(EnergyPlusData &state)
     587              : {
     588      2828408 :     if (state.dataEnvrn->SiteTempGradient == 0.0) {
     589            0 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     590            0 :             state.dataSurface->SurfOutDryBulbTemp(SurfNum) = state.dataEnvrn->OutDryBulbTemp;
     591            0 :             state.dataSurface->SurfOutWetBulbTemp(SurfNum) = state.dataEnvrn->OutWetBulbTemp;
     592              :         }
     593              :     } else {
     594      2828408 :         Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     595      2828408 :         Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     596    185371798 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     597              :             // Base temperatures at Z = 0 (C)
     598    182543390 :             Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
     599    182543390 :             if (Z <= 0.0) {
     600     25080723 :                 state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp;
     601     25080723 :                 state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp;
     602              :             } else {
     603    157462667 :                 Real64 GradientDividend = state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z;
     604    157462667 :                 Real64 GradientDivisor = DataEnvironment::EarthRadius + Z;
     605    157462667 :                 state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp - GradientDividend / GradientDivisor;
     606    157462667 :                 state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp - GradientDividend / GradientDivisor;
     607              :             }
     608              :         }
     609              :     }
     610      2828408 : }
     611              : 
     612      2828408 : void CheckSurfaceOutBulbTempAt(EnergyPlusData &state)
     613              : {
     614              :     // Using/Aliasing
     615              :     using DataEnvironment::SetOutBulbTempAt_error;
     616              : 
     617      2828408 :     Real64 minBulb = 0.0;
     618    185371798 :     for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     619    182543390 :         minBulb = min(minBulb, state.dataSurface->SurfOutDryBulbTemp(SurfNum), state.dataSurface->SurfOutWetBulbTemp(SurfNum));
     620    182543390 :         if (minBulb < -100.0) {
     621            0 :             SetOutBulbTempAt_error(state, "Surface", state.dataSurface->Surface(SurfNum).Centroid.z, state.dataSurface->Surface(SurfNum).Name);
     622              :         }
     623              :     }
     624      2828408 : }
     625              : 
     626      2828408 : void SetSurfaceWindSpeedAt(EnergyPlusData &state)
     627              : {
     628      2828408 :     Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
     629      2828408 :                      std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
     630      2828408 :     if (state.dataEnvrn->SiteWindExp == 0.0) {
     631            0 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     632            0 :             state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataEnvrn->WindSpeed;
     633              :         }
     634              :     } else {
     635              : 
     636    185371798 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     637    182543390 :             if (!state.dataSurface->Surface(SurfNum).ExtWind) {
     638    110926129 :                 continue;
     639              :             }
     640     71617261 :             Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
     641     71617261 :             if (Z <= 0.0) {
     642        72555 :                 state.dataSurface->SurfOutWindSpeed(SurfNum) = 0.0;
     643              :             } else {
     644              :                 //  [Met] - at meterological Station, Height of measurement is usually 10m above ground
     645              :                 //  LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
     646              :                 //                     * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
     647     71544706 :                 state.dataSurface->SurfOutWindSpeed(SurfNum) = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
     648              :             }
     649              :         }
     650              :     }
     651      2828408 : }
     652              : 
     653      2828408 : void SetSurfaceWindDirAt(EnergyPlusData &state)
     654              : {
     655    185371798 :     for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
     656    182543390 :         state.dataSurface->SurfOutWindDir(SurfNum) = state.dataEnvrn->WindDir;
     657              :     }
     658      2828408 : }
     659              : 
     660        41659 : std::string cSurfaceClass(SurfaceClass const ClassNo)
     661              : {
     662              : 
     663              :     // FUNCTION INFORMATION:
     664              :     //       AUTHOR         Linda Lawrie
     665              :     //       DATE WRITTEN   May 2006
     666              :     //       MODIFIED       na
     667              :     //       RE-ENGINEERED  na
     668              : 
     669              :     // PURPOSE OF THIS FUNCTION:
     670              :     // This function returns a string based on class number.
     671              : 
     672              :     // Return value
     673        41659 :     std::string ClassName;
     674              : 
     675        41659 :     switch (ClassNo) {
     676        19804 :     case SurfaceClass::Wall: {
     677        19804 :         ClassName = "Wall";
     678        19804 :     } break;
     679         6146 :     case SurfaceClass::Floor: {
     680         6146 :         ClassName = "Floor";
     681         6146 :     } break;
     682         5263 :     case SurfaceClass::Roof: {
     683         5263 :         ClassName = "Roof";
     684         5263 :     } break;
     685         5848 :     case SurfaceClass::Window: {
     686         5848 :         ClassName = "Window";
     687         5848 :     } break;
     688            0 :     case SurfaceClass::GlassDoor: {
     689            0 :         ClassName = "Glass Door";
     690            0 :     } break;
     691          425 :     case SurfaceClass::Door: {
     692          425 :         ClassName = "Door";
     693          425 :     } break;
     694            4 :     case SurfaceClass::TDD_Dome: {
     695            4 :         ClassName = "TubularDaylightDome";
     696            4 :     } break;
     697            0 :     case SurfaceClass::TDD_Diffuser: {
     698            0 :         ClassName = "TubularDaylightDiffuser";
     699            0 :     } break;
     700         3451 :     case SurfaceClass::IntMass: {
     701         3451 :         ClassName = "Internal Mass";
     702         3451 :     } break;
     703          630 :     case SurfaceClass::Shading: {
     704          630 :         ClassName = "Shading";
     705          630 :     } break;
     706           34 :     case SurfaceClass::Detached_B: {
     707           34 :         ClassName = "Detached Shading:Building";
     708           34 :     } break;
     709           54 :     case SurfaceClass::Detached_F: {
     710           54 :         ClassName = "Detached Shading:Fixed";
     711           54 :     } break;
     712            0 :     default: {
     713            0 :         ClassName = "Invalid/Unknown";
     714            0 :     } break;
     715              :     }
     716              : 
     717        41659 :     return ClassName;
     718            0 : }
     719       186135 : Real64 AbsFrontSide(EnergyPlusData &state, int SurfNum)
     720              : {
     721              :     Real64 AbsorptanceFromExteriorFrontSide =
     722       186135 :         (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
     723       186135 :         state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
     724              :     Real64 AbsorptanceFromInteriorFrontSide =
     725       186135 :         (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
     726       186135 :         state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
     727       186135 :     return AbsorptanceFromExteriorFrontSide + AbsorptanceFromInteriorFrontSide;
     728              : }
     729              : 
     730       186135 : Real64 AbsBackSide(EnergyPlusData &state, int SurfNum)
     731              : {
     732              :     Real64 AbsorptanceFromInteriorBackSide =
     733       186135 :         (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
     734       186135 :         state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
     735              :     Real64 AbsorptanceFromExteriorBackSide =
     736       186135 :         (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
     737       186135 :         state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
     738       186135 :     return AbsorptanceFromExteriorBackSide + AbsorptanceFromInteriorBackSide;
     739              : }
     740              : 
     741          801 : void GetVariableAbsorptanceSurfaceList(EnergyPlusData &state)
     742              : {
     743          801 :     if (!state.dataMaterial->AnyVariableAbsorptance) {
     744          800 :         return;
     745              :     }
     746            7 :     for (int surfNum : state.dataSurface->AllHTSurfaceList) {
     747            6 :         auto const &thisSurface = state.dataSurface->Surface(surfNum);
     748            6 :         auto const &thisConstruct = state.dataConstruction->Construct(thisSurface.Construction);
     749            6 :         if (thisConstruct.TotLayers == 0) {
     750            0 :             continue;
     751              :         }
     752            6 :         if (thisConstruct.LayerPoint(1) == 0) {
     753            0 :             continue; // error finding material number
     754              :         }
     755            6 :         auto const *mat = state.dataMaterial->materials(thisConstruct.LayerPoint(1));
     756            6 :         if (mat->group != Material::Group::Regular) {
     757            0 :             continue;
     758              :         }
     759              : 
     760            6 :         if (mat->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
     761              :             // check for dynamic coating defined on interior surface
     762            1 :             if (thisSurface.ExtBoundCond != ExternalEnvironment) {
     763            0 :                 ShowWarningError(state,
     764            0 :                                  format("MaterialProperty:VariableAbsorptance defined on an interior surface, {}. This VariableAbsorptance property "
     765              :                                         "will be ignored here",
     766            0 :                                         thisSurface.Name));
     767              :             } else {
     768            1 :                 state.dataSurface->AllVaryAbsOpaqSurfaceList.push_back(surfNum);
     769              :             }
     770              :         }
     771            1 :     }
     772              :     // check for dynamic coating defined on the non-outside layer of a construction
     773            5 :     for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
     774            4 :         auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     775            4 :         for (int Layer = 2; Layer <= thisConstruct.TotLayers; ++Layer) {
     776            0 :             auto const *mat = state.dataMaterial->materials(thisConstruct.LayerPoint(Layer));
     777            0 :             if (mat->group != Material::Group::Regular) {
     778            0 :                 continue;
     779              :             }
     780            0 :             if (mat->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
     781            0 :                 ShowWarningError(state,
     782            0 :                                  format("MaterialProperty:VariableAbsorptance defined on a inside-layer materials, {}. This VariableAbsorptance "
     783              :                                         "property will be ignored here",
     784            0 :                                         mat->Name));
     785              :             }
     786              :         }
     787              :     }
     788              : } // GetVariableAbsorptanceSurfaceList()
     789              : 
     790            0 : Compass4 AzimuthToCompass4(Real64 azimuth)
     791              : {
     792            0 :     assert(azimuth >= 0.0 && azimuth < 360.0);
     793            0 :     for (int c4 = 0; c4 < static_cast<int>(Compass4::Num); ++c4) {
     794            0 :         Real64 lo = Compass4AzimuthLo[c4];
     795            0 :         Real64 hi = Compass4AzimuthHi[c4];
     796            0 :         if (lo > hi) {
     797            0 :             if (azimuth >= lo || azimuth < hi) {
     798            0 :                 return static_cast<Compass4>(c4);
     799              :             }
     800              :         } else {
     801            0 :             if (azimuth >= lo && azimuth < hi) {
     802            0 :                 return static_cast<Compass4>(c4);
     803              :             }
     804              :         }
     805              :     }
     806            0 :     assert(false);
     807              :     return Compass4::Invalid;
     808              : }
     809              : 
     810        30818 : Compass8 AzimuthToCompass8(Real64 azimuth)
     811              : {
     812        30818 :     assert(azimuth >= 0.0 && azimuth < 360.0);
     813       128296 :     for (int c8 = 0; c8 < static_cast<int>(Compass8::Num); ++c8) {
     814       128296 :         Real64 lo = Compass8AzimuthLo[c8];
     815       128296 :         Real64 hi = Compass8AzimuthHi[c8];
     816       128296 :         if (lo > hi) {
     817        30818 :             if (azimuth >= lo || azimuth < hi) {
     818         6296 :                 return static_cast<Compass8>(c8);
     819              :             }
     820              :         } else {
     821        97478 :             if (azimuth >= lo && azimuth < hi) {
     822        24522 :                 return static_cast<Compass8>(c8);
     823              :             }
     824              :         }
     825              :     }
     826            0 :     assert(false);
     827              :     return Compass8::Invalid;
     828              : }
     829              : 
     830              : } // namespace EnergyPlus::DataSurfaces
        

Generated by: LCOV version 2.0-1