LCOV - code coverage report
Current view: top level - EnergyPlus - DaylightingDevices.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 54.5 % 769 419
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 15 15

            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 <cmath>
      50              : 
      51              : // ObjexxFCL Headers
      52              : #include <ObjexxFCL/Fmath.hh>
      53              : #include <ObjexxFCL/numeric.hh>
      54              : 
      55              : // EnergyPlus Headers
      56              : #include <EnergyPlus/Construction.hh>
      57              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      58              : #include <EnergyPlus/DataDaylighting.hh>
      59              : #include <EnergyPlus/DataDaylightingDevices.hh>
      60              : #include <EnergyPlus/DataHeatBalance.hh>
      61              : #include <EnergyPlus/DataIPShortCuts.hh>
      62              : #include <EnergyPlus/DataSurfaces.hh>
      63              : #include <EnergyPlus/DataSystemVariables.hh>
      64              : #include <EnergyPlus/DaylightingDevices.hh>
      65              : #include <EnergyPlus/DaylightingManager.hh>
      66              : #include <EnergyPlus/DisplayRoutines.hh>
      67              : #include <EnergyPlus/FluidProperties.hh>
      68              : #include <EnergyPlus/General.hh>
      69              : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
      70              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      71              : #include <EnergyPlus/OutputProcessor.hh>
      72              : #include <EnergyPlus/SolarShading.hh>
      73              : #include <EnergyPlus/UtilityRoutines.hh>
      74              : 
      75              : namespace EnergyPlus {
      76              : 
      77              : namespace Dayltg {
      78              : 
      79              :     // MODULE INFORMATION:
      80              :     //       AUTHOR         Peter Graham Ellis
      81              :     //       DATE WRITTEN   May 2003
      82              :     //       MODIFIED       PGE, Aug 2003:  Added daylighting shelves.
      83              :     //       RE-ENGINEERED  na
      84              : 
      85              :     // PURPOSE OF THIS MODULE:
      86              :     // Simulates daylighting devices, namely tubular daylighting devices (a.k.a. light pipes, sun pipes, or
      87              :     // tubular skylights) and daylighting shelves (a.k.a. light shelves).
      88              : 
      89              :     // METHODOLOGY EMPLOYED:
      90              :     // TUBULAR DAYLIGHTING DEVICE
      91              :     // A tubular daylighting device (TDD) is constructed of three components:  a dome, a pipe, and a diffuser.
      92              :     // The dome and diffuser are treated as special window surfaces to take advantage of many of the already
      93              :     // existing daylighting and heat transfer routines.  Together the dome and diffuser become "receiver"
      94              :     // and "transmitter", i.e. radiation entering the dome ends up exiting the diffuser.  The geometry and
      95              :     // construction of the pipe and the constructions of the window surfaces determine the transmittance of
      96              :     // the TDD.
      97              :     // The main task of the module is to determine the total transmittance of the TDD for several
      98              :     // types of radiation, including visible beam, solar beam, solar isotropic, and solar anisotropic sky.
      99              :     // The fundamental building block for each type of radiation is the transmittance of a beam or ray of
     100              :     // radiation (visible or solar) at a given incident angle.  This transmittance is calculated and
     101              :     // tabulated for each TDD during initialization using a numerical integral based on the analytical
     102              :     // solution derived by Swift and Smith.  Diffuse transmittances are subsequently calculated by integrating
     103              :     // discrete rays over the viewable area.
     104              :     // There are three parts to the TDD model:
     105              :     //   1. Daylighting
     106              :     //   2. Solar gain
     107              :     //   3. Thermal conductive/convective gain
     108              :     // The daylighting simulation uses the visible beam transmittance to find the amount of direct beam
     109              :     // solar illuminance that enters the zone.  The visible beam transmittance is also used for calculating
     110              :     // the contribution of each discrete ray from a differential area during a comprehensive sky/ground
     111              :     // integration.
     112              :     // The heat balance simulation handles both the solar gain and thermal conductive/convective gain.
     113              :     // Although visible and solar radiation are similar, solar gain is simulated very differently from the
     114              :     // daylighting illuminance calculations.  The gain from direct beam solar is found using the
     115              :     // solar beam transmittance.  The diffuse solar, however, is more complicated.  A sky/ground integration
     116              :     // is NOT performed.  Instead anisotropic sky view factor multipliers (SurfAnisoSkyMult) are calculated for
     117              :     // each surface.  The diffuse sky/ground transmittance of the TDD is solved using a modification of the
     118              :     // SurfAnisoSkyMult.  The ground radiation transmittance and anisotropic sky transmittance are found separately.
     119              :     // See CalcTDDTransSolIso, CalcTDDTransSolHorizon, CalcTDDTransSolAniso below.
     120              :     // For thermal conductive/convective gain, TDDs are treated as one big object with an effective R value.
     121              :     // The outside face temperature of the dome and the inside face temperature of the diffuser are calculated
     122              :     // with the outside and inside heat balances respectively.  The temperatures are then copied to the inside
     123              :     // face of the dome and the outside face of the diffuser.  Normal exterior and interior convection and IR
     124              :     // radiation exchange occurs for both surfaces.
     125              :     // Solar radiation that is not transmitted through the pipe is absorbed and distributed among the transition
     126              :     // zones that the pipe passes through between dome and diffuser.  The heat is distributed proportionate to
     127              :     // the length of the zone.  Any exterior length of pipe also receives a proportionate amount of heat, but
     128              :     // this is lost to the outside.
     129              :     // REFERENCES:
     130              :     // Ellis, P. G., and Strand, R. K.  Paper to be published.
     131              :     // Swift, P. D., and Smith, G. B.  "Cylindrical Mirror Light Pipes",
     132              :     //   Solar Energy Materials and Solar Cells 36 (1995), pp. 159-168.
     133              :     // DAYLIGHTING SHELVES
     134              :     // A daylighting shelf is constructed of up to three components: a window, an inside shelf, and an outside
     135              :     // shelf.  Both inside shelf and outside shelf are optional, but if neither is specified, nothing happens.
     136              :     // The window must be divided into two window surfaces: an upper window and a lower window.  The upper
     137              :     // window interacts with the daylighting shelf but the lower window does not, except to receive shading from
     138              :     // the outside shelf.  The inside shelf, if specified, acts to reflect all transmitted light from the
     139              :     // upper window onto the ceiling of the zone as diffuse light.  The outside shelf, if specified, changes
     140              :     // the total amount of light incident on the window.  All light reflected from the outside shelf also goes
     141              :     // onto the zone ceiling.
     142              :     // Most of the work for daylighting shelves is actually done in DaylightingManager.cc, SolarShading.cc,
     143              :     // and HeatBalanceSurfaceManager.cc.  The main task of the module is to get the input and initialize the
     144              :     // shelf.  The biggest part of initialization is calculating the window view factor to the outside shelf.
     145              :     // It is up to the user to reduce the window view factor to ground accordingly.
     146              :     // The inside shelf is modeled in both daylighting and heat balance simulations by converting all light
     147              :     // transmitted by the upper window into diffuse upgoing flux.  No beam or downgoing flux can pass the end
     148              :     // of the shelf regardless of the shelf's position or orientation.  Since it is defined as a partition,
     149              :     // the inside shelf is essentially the same as an internal mass surface.  The initialization doubles the
     150              :     // surface area so that both sides are exposed to the zone air.  All beam solar transmitted by the window
     151              :     // is absorbed in one side of the shelf, i.e. half of the doubled area.
     152              :     // The outside shelf is modeled in the daylighting simulation after the detailed sky/ground integration has
     153              :     // been completed.  Since exterior surfaces currently do not reflect or have a luminance in the Daylighting
     154              :     // Manager, the shelf just serves to block part of the ground luminance.  The luminance of the shelf itself
     155              :     // is added as a lump sum based on the view factor to the shelf, the sunlit fraction, the reflectance of the
     156              :     // shelf construction, and the sun and sky illuminance on the shelf.  All the luminance is added to the
     157              :     // diffuse upgoing flux.  The shelf view factor to sky is assumed to be 1.0 for lack of better information.
     158              :     // The outside shelf is treated similarly in the heat balance simulation, but here the shelf view factor to
     159              :     // sky is conveniently given by SurfAnisoSkyMult.  NOTE:  The solar shading code was modified to allow sunlit
     160              :     // fraction, sunlit area, SurfAnisoSkyMult, etc. to be calculated for attached shading surfaces.
     161              :     // Future shelf model improvements:
     162              :     // 1. Allow beam and downgoing flux to pass the end of the inside shelf depending on actual shelf geometry.
     163              :     // 2. Reduce outside shelf view factor to sky (for daylighting) by taking into account anisotropic sky
     164              :     //    distribution and shading, i.e. the daylighting equivalent of SurfAnisoSkyMult.
     165              :     // 3. Expand view factor to shelf calculation to handle more complicated geometry.
     166              :     // REFERENCES:
     167              :     // Mills, A. F.  Heat and Mass Transfer, 1995, p. 499.  (Shape factor for adjacent rectangles.)
     168              : 
     169              :     // Using/Aliasing
     170              :     using DataSurfaces::ExternalEnvironment;
     171              :     using DataSurfaces::SurfaceClass;
     172              : 
     173          801 :     void InitDaylightingDevices(EnergyPlusData &state)
     174              :     {
     175              : 
     176              :         // SUBROUTINE INFORMATION:
     177              :         //       AUTHOR         Peter Graham Ellis
     178              :         //       DATE WRITTEN   May 2003
     179              :         //       MODIFIED       PGE, Aug 2003:  Added daylighting shelves.
     180              : 
     181              :         // PURPOSE OF THIS SUBROUTINE:
     182              :         // This subroutine initializes all daylighting device:  TDD pipes and daylighting shelves.
     183              :         // This is only called once at the beginning of the simulation under the BeginSimFlag.
     184              : 
     185              :         // METHODOLOGY EMPLOYED:
     186              :         // Daylighting and thermal variables are calculated.  BeamTrans/COSAngle table is calculated.
     187              : 
     188              :         struct TDDPipeStoredData
     189              :         {
     190              :             // Members
     191              :             Real64 AspectRatio;        // Aspect ratio, length / diameter
     192              :             Real64 Reflectance;        // Reflectance of surface
     193              :             Array1D<Real64> TransBeam; // Table of beam transmittance vs. cosine angle
     194              : 
     195              :             // Default Constructor
     196            4 :             TDDPipeStoredData() : AspectRatio(0.0), Reflectance(0.0), TransBeam(NumOfAngles, 0.0)
     197              :             {
     198            4 :             }
     199              :         };
     200              : 
     201              :         // Object Data
     202          801 :         Array1D<TDDPipeStoredData> TDDPipeStored;
     203              : 
     204              :         // Initialize tubular daylighting devices (TDDs)
     205          801 :         GetTDDInput(state);
     206              : 
     207          801 :         if ((int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
     208            1 :             DisplayString(state, "Initializing Tubular Daylighting Devices");
     209              :             // Setup COSAngle list for all TDDs
     210            1 :             state.dataDaylightingDevices->COSAngle(1) = 0.0;
     211            1 :             state.dataDaylightingDevices->COSAngle(NumOfAngles) = 1.0;
     212              : 
     213            1 :             Real64 dTheta = 90.0 * Constant::DegToRad / (NumOfAngles - 1.0);
     214            1 :             Real64 Theta = 90.0 * Constant::DegToRad;
     215           18 :             for (int AngleNum = 2; AngleNum <= NumOfAngles - 1; ++AngleNum) {
     216           17 :                 Theta -= dTheta;
     217           17 :                 state.dataDaylightingDevices->COSAngle(AngleNum) = std::cos(Theta);
     218              :             } // AngleNum
     219              : 
     220            1 :             TDDPipeStored.allocate((int)state.dataDaylightingDevicesData->TDDPipe.size() * 2);
     221              : 
     222            3 :             for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
     223              :                 // Initialize optical properties
     224            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio =
     225            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength / state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter;
     226            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectVis =
     227            2 :                     1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).InsideAbsorpVis;
     228            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectSol =
     229            2 :                     1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).InsideAbsorpSolar;
     230              : 
     231              :                 // Calculate the beam transmittance table for visible and solar spectrum
     232              :                 // First time thru use the visible reflectance
     233            2 :                 Real64 Reflectance = state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectVis;
     234            2 :                 int NumStored = 0; // Counter for number of pipes stored as they are calculated
     235              :                 int StoredNum;     // Stored TDD pipe object number
     236            6 :                 for (int Loop = 1; Loop <= 2; ++Loop) {
     237              :                     // For computational efficiency, search stored pipes to see if an identical pipe has already been calculated
     238            4 :                     bool Found = false;
     239            4 :                     for (StoredNum = 1; StoredNum <= NumStored; ++StoredNum) {
     240            2 :                         if (TDDPipeStored(StoredNum).AspectRatio != state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio) {
     241            0 :                             continue;
     242              :                         }
     243            2 :                         if (TDDPipeStored(StoredNum).Reflectance == Reflectance) {
     244            2 :                             Found = true; // StoredNum points to the matching TDDPipeStored
     245            2 :                             break;
     246              :                         }
     247              :                     } // StoredNum
     248              : 
     249            4 :                     if (!Found) { // Not yet calculated
     250              : 
     251              :                         // Add a new pipe to TDDPipeStored
     252            2 :                         ++NumStored;
     253            2 :                         TDDPipeStored(NumStored).AspectRatio = state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio;
     254            2 :                         TDDPipeStored(NumStored).Reflectance = Reflectance;
     255              : 
     256              :                         // Set beam transmittances for 0 and 90 degrees
     257            2 :                         TDDPipeStored(NumStored).TransBeam(1) = 0.0;
     258            2 :                         TDDPipeStored(NumStored).TransBeam(NumOfAngles) = 1.0;
     259              : 
     260              :                         // Calculate intermediate beam transmittances between 0 and 90 degrees
     261            2 :                         Theta = 90.0 * Constant::DegToRad;
     262           36 :                         for (int AngleNum = 2; AngleNum <= NumOfAngles - 1; ++AngleNum) {
     263           34 :                             Theta -= dTheta;
     264           34 :                             TDDPipeStored(NumStored).TransBeam(AngleNum) =
     265           34 :                                 CalcPipeTransBeam(Reflectance, state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio, Theta);
     266              :                         } // AngleNum
     267              : 
     268            2 :                         StoredNum = NumStored;
     269              :                     }
     270              : 
     271              :                     // Assign stored values to TDDPipe
     272            4 :                     if (Loop == 1) { // Visible
     273            2 :                         state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransVisBeam = TDDPipeStored(StoredNum).TransBeam;
     274              :                     } else { // Solar
     275            2 :                         state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransSolBeam = TDDPipeStored(StoredNum).TransBeam;
     276              :                     }
     277              : 
     278              :                     // Second time thru use the solar reflectance
     279            4 :                     Reflectance = state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectSol;
     280              :                 } // Loop
     281              : 
     282              :                 // Calculate the solar isotropic diffuse and horizon transmittances.  These values are constant for a given TDD.
     283            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso = CalcTDDTransSolIso(state, PipeNum);
     284            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolHorizon = CalcTDDTransSolHorizon(state, PipeNum);
     285              : 
     286              :                 // Initialize thermal properties
     287            2 :                 Real64 SumTZoneLengths = 0.0;
     288            4 :                 for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
     289            2 :                     SumTZoneLengths += state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum);
     290              : 
     291            4 :                     SetupZoneInternalGain(state,
     292            2 :                                           state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum),
     293            2 :                                           state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name,
     294              :                                           DataHeatBalance::IntGainType::DaylightingDeviceTubular,
     295            2 :                                           &state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain(TZoneNum));
     296              : 
     297              :                 } // TZoneNum
     298              : 
     299            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).ExtLength =
     300            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength - SumTZoneLengths;
     301              : 
     302              :                 // Setup report variables: CurrentModuleObject='DaylightingDevice:Tubular'
     303            4 :                 SetupOutputVariable(state,
     304              :                                     "Tubular Daylighting Device Transmitted Solar Radiation Rate",
     305              :                                     Constant::Units::W,
     306            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransmittedSolar,
     307              :                                     OutputProcessor::TimeStepType::Zone,
     308              :                                     OutputProcessor::StoreType::Average,
     309            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     310            4 :                 SetupOutputVariable(state,
     311              :                                     "Tubular Daylighting Device Pipe Absorbed Solar Radiation Rate",
     312              :                                     Constant::Units::W,
     313            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeAbsorbedSolar,
     314              :                                     OutputProcessor::TimeStepType::Zone,
     315              :                                     OutputProcessor::StoreType::Average,
     316            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     317            4 :                 SetupOutputVariable(state,
     318              :                                     "Tubular Daylighting Device Heat Gain Rate",
     319              :                                     Constant::Units::W,
     320            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).HeatGain,
     321              :                                     OutputProcessor::TimeStepType::Zone,
     322              :                                     OutputProcessor::StoreType::Average,
     323            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     324            4 :                 SetupOutputVariable(state,
     325              :                                     "Tubular Daylighting Device Heat Loss Rate",
     326              :                                     Constant::Units::W,
     327            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).HeatLoss,
     328              :                                     OutputProcessor::TimeStepType::Zone,
     329              :                                     OutputProcessor::StoreType::Average,
     330            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     331              : 
     332            4 :                 SetupOutputVariable(state,
     333              :                                     "Tubular Daylighting Device Beam Solar Transmittance",
     334              :                                     Constant::Units::None,
     335            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolBeam,
     336              :                                     OutputProcessor::TimeStepType::Zone,
     337              :                                     OutputProcessor::StoreType::Average,
     338            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     339            4 :                 SetupOutputVariable(state,
     340              :                                     "Tubular Daylighting Device Beam Visible Transmittance",
     341              :                                     Constant::Units::None,
     342            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam,
     343              :                                     OutputProcessor::TimeStepType::Zone,
     344              :                                     OutputProcessor::StoreType::Average,
     345            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     346            4 :                 SetupOutputVariable(state,
     347              :                                     "Tubular Daylighting Device Diffuse Solar Transmittance",
     348              :                                     Constant::Units::None,
     349            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolDiff,
     350              :                                     OutputProcessor::TimeStepType::Zone,
     351              :                                     OutputProcessor::StoreType::Average,
     352            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     353            4 :                 SetupOutputVariable(state,
     354              :                                     "Tubular Daylighting Device Diffuse Visible Transmittance",
     355              :                                     Constant::Units::None,
     356            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff,
     357              :                                     OutputProcessor::TimeStepType::Zone,
     358              :                                     OutputProcessor::StoreType::Average,
     359            2 :                                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
     360              : 
     361              :             } // PipeNum
     362              : 
     363            1 :             TDDPipeStored.deallocate();
     364              :         }
     365              : 
     366              :         // Initialize daylighting shelves
     367          801 :         GetShelfInput(state);
     368              : 
     369          801 :         if ((int)state.dataDaylightingDevicesData->Shelf.size() > 0) {
     370            1 :             DisplayString(state, "Initializing Light Shelf Daylighting Devices");
     371              :         }
     372              : 
     373          802 :         for (int ShelfNum = 1; ShelfNum <= (int)state.dataDaylightingDevicesData->Shelf.size(); ++ShelfNum) {
     374            1 :             int WinSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).Window;
     375              : 
     376            1 :             int ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf;
     377            1 :             if (ShelfSurf > 0) {
     378              :                 // Double surface area so that both sides of the shelf are treated as internal mass
     379            1 :                 state.dataSurface->Surface(ShelfSurf).Area *= 2.0;
     380              :             }
     381              : 
     382            1 :             ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf;
     383            1 :             if (ShelfSurf > 0) {
     384            1 :                 state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis =
     385            1 :                     1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction).OutsideAbsorpVis;
     386            1 :                 state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectSol =
     387            1 :                     1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction).OutsideAbsorpSolar;
     388              : 
     389            1 :                 if (state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor < 0) {
     390            1 :                     CalcViewFactorToShelf(state, ShelfNum);
     391              :                 }
     392              : 
     393            1 :                 adjustViewFactorsWithShelf(state,
     394            1 :                                            state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor,
     395            1 :                                            state.dataSurface->Surface(WinSurf).ViewFactorSky,
     396            1 :                                            state.dataSurface->Surface(WinSurf).ViewFactorGround,
     397              :                                            WinSurf,
     398              :                                            ShelfNum);
     399              : 
     400              :                 // Report calculated view factor so that user knows what to make the view factor to ground
     401            1 :                 if (!state.dataDaylightingDevices->ShelfReported) {
     402            1 :                     print(state.files.eio,
     403              :                           "! <Shelf Details>,Name,View Factor to Outside Shelf,Window Name,Window View Factor to Sky,Window View Factor to Ground\n");
     404            1 :                     state.dataDaylightingDevices->ShelfReported = true;
     405              :                 }
     406            1 :                 print(state.files.eio,
     407              :                       "{},{:.2R},{},{:.2R},{:.2R}\n",
     408            1 :                       state.dataDaylightingDevicesData->Shelf(ShelfNum).Name,
     409            1 :                       state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor,
     410            1 :                       state.dataSurface->Surface(WinSurf).Name,
     411            1 :                       state.dataSurface->Surface(WinSurf).ViewFactorSky,
     412            1 :                       state.dataSurface->Surface(WinSurf).ViewFactorGround);
     413              :                 //      CALL SetupOutputVariable(state, 'View Factor To Outside Shelf []', &
     414              :                 //        Shelf(ShelfNum)%ViewFactor,'Zone','Average',Shelf(ShelfNum)%Name)
     415              :             }
     416              :         }
     417              : 
     418              :         // Warning that if Calculate Solar Reflection From Exterior Surfaces = Yes in Building input, then
     419              :         // solar reflection calculated from obstructions will not be used in daylighting shelf or tubular device
     420              :         // calculation
     421              : 
     422          810 :         if (state.dataSurface->CalcSolRefl &&
     423            9 :             ((int)state.dataDaylightingDevicesData->TDDPipe.size() > 0 || (int)state.dataDaylightingDevicesData->Shelf.size() > 0)) {
     424            0 :             ShowWarningError(state, "InitDaylightingDevices: Solar Distribution Model includes Solar Reflection calculations;");
     425            0 :             ShowContinueError(state, "the resulting reflected solar values will not be used in the");
     426            0 :             ShowContinueError(state, "DaylightingDevice:Shelf or DaylightingDevice:Tubular calculations.");
     427              :         }
     428          801 :     }
     429              : 
     430          801 :     void GetTDDInput(EnergyPlusData &state)
     431              :     {
     432              : 
     433              :         // SUBROUTINE INFORMATION:
     434              :         //       AUTHOR         Peter Graham Ellis
     435              :         //       DATE WRITTEN   May 2003
     436              : 
     437              :         // PURPOSE OF THIS SUBROUTINE:
     438              :         // Gets the input for TDD pipes and does some error checking.
     439              : 
     440              :         // METHODOLOGY EMPLOYED:
     441              :         // Standard EnergyPlus methodology.
     442              : 
     443          801 :         auto &ipsc = state.dataIPShortCut;
     444          801 :         auto &cCurrentModuleObject = ipsc->cCurrentModuleObject;
     445              : 
     446          801 :         cCurrentModuleObject = "DaylightingDevice:Tubular";
     447          801 :         int NumOfTDDPipes = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
     448              : 
     449          801 :         if (NumOfTDDPipes > 0) {
     450            1 :             state.dataDaylightingDevicesData->TDDPipe.allocate(NumOfTDDPipes);
     451              :             int IOStatus;   // Used in GetObjectItem
     452              :             int NumAlphas;  // Number of Alphas for each GetObjectItem call
     453              :             int NumNumbers; // Number of Numbers for each GetObjectItem call
     454              : 
     455            3 :             for (int PipeNum = 1; PipeNum <= NumOfTDDPipes; ++PipeNum) {
     456            4 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     457              :                                                                          cCurrentModuleObject,
     458              :                                                                          PipeNum,
     459            2 :                                                                          ipsc->cAlphaArgs,
     460              :                                                                          NumAlphas,
     461            2 :                                                                          ipsc->rNumericArgs,
     462              :                                                                          NumNumbers,
     463              :                                                                          IOStatus,
     464            2 :                                                                          ipsc->lNumericFieldBlanks,
     465            2 :                                                                          ipsc->lAlphaFieldBlanks,
     466            2 :                                                                          ipsc->cAlphaFieldNames,
     467            2 :                                                                          ipsc->cNumericFieldNames);
     468              :                 // Pipe name
     469            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name = ipsc->cAlphaArgs(1);
     470              : 
     471              :                 // Get TDD:DOME object
     472            2 :                 int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataSurface->Surface);
     473              : 
     474            2 :                 if (SurfNum == 0) {
     475            0 :                     ShowSevereError(state, format("{} = {}:  Dome {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
     476            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     477              :                 } else {
     478            2 :                     if (FindTDDPipe(state, SurfNum) > 0) {
     479            0 :                         ShowSevereError(state,
     480            0 :                                         format("{} = {}:  Dome {} is referenced by more than one TDD.",
     481              :                                                cCurrentModuleObject,
     482            0 :                                                ipsc->cAlphaArgs(1),
     483            0 :                                                ipsc->cAlphaArgs(2)));
     484            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     485              :                     }
     486              : 
     487            2 :                     if (state.dataSurface->Surface(SurfNum).Class != SurfaceClass::TDD_Dome) {
     488            0 :                         ShowSevereError(state,
     489            0 :                                         format("{} = {}:  Dome {} is not of surface type TubularDaylightDome.",
     490              :                                                cCurrentModuleObject,
     491            0 :                                                ipsc->cAlphaArgs(1),
     492            0 :                                                ipsc->cAlphaArgs(2)));
     493            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     494              :                     }
     495              : 
     496            2 :                     if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TotGlassLayers > 1) {
     497            0 :                         ShowSevereError(state,
     498            0 :                                         format("{} = {}:  Dome {} construction ({}) must have only 1 glass layer.",
     499              :                                                cCurrentModuleObject,
     500            0 :                                                ipsc->cAlphaArgs(1),
     501            0 :                                                ipsc->cAlphaArgs(2),
     502            0 :                                                state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).Name));
     503            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     504              :                     }
     505              : 
     506            2 :                     if (state.dataSurface->Surface(SurfNum).HasShadeControl) {
     507            0 :                         ShowSevereError(state,
     508            0 :                                         format("{} = {}:  Dome {} must not have a shading control.",
     509              :                                                cCurrentModuleObject,
     510            0 :                                                ipsc->cAlphaArgs(1),
     511            0 :                                                ipsc->cAlphaArgs(2)));
     512            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     513              :                     }
     514              : 
     515            2 :                     if (state.dataSurface->Surface(SurfNum).FrameDivider > 0) {
     516            0 :                         ShowSevereError(
     517              :                             state,
     518            0 :                             format(
     519            0 :                                 "{} = {}:  Dome {} must not have a frame/divider.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
     520            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     521              :                     }
     522              : 
     523            2 :                     if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
     524            0 :                         ShowSevereError(state,
     525            0 :                                         format("{} = {}:  Dome {} Equivalent Layer Window is not supported.",
     526              :                                                cCurrentModuleObject,
     527            0 :                                                ipsc->cAlphaArgs(1),
     528            0 :                                                ipsc->cAlphaArgs(2)));
     529            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     530              :                     }
     531              :                     // Window multiplier is already handled in SurfaceGeometry.cc
     532              : 
     533            2 :                     if (!state.dataSurface->Surface(SurfNum).ExtSolar) {
     534            0 :                         ShowWarningError(state,
     535            0 :                                          format("{} = {}:  Dome {} is not exposed to exterior radiation.",
     536              :                                                 cCurrentModuleObject,
     537            0 :                                                 ipsc->cAlphaArgs(1),
     538            0 :                                                 ipsc->cAlphaArgs(2)));
     539              :                     }
     540              : 
     541            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome = SurfNum;
     542            2 :                     state.dataSurface->SurfWinTDDPipeNum(SurfNum) = PipeNum;
     543              :                 }
     544              : 
     545              :                 // Get TDD:DIFFUSER object
     546            2 :                 SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(3), state.dataSurface->Surface);
     547              : 
     548            2 :                 if (SurfNum == 0) {
     549            0 :                     ShowSevereError(state,
     550            0 :                                     format("{} = {}:  Diffuser {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(3)));
     551            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     552              :                 } else {
     553            2 :                     if (FindTDDPipe(state, SurfNum) > 0) {
     554            0 :                         ShowSevereError(state,
     555            0 :                                         format("{} = {}:  Diffuser {} is referenced by more than one TDD.",
     556              :                                                cCurrentModuleObject,
     557            0 :                                                ipsc->cAlphaArgs(1),
     558            0 :                                                ipsc->cAlphaArgs(3)));
     559            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     560              :                     }
     561              : 
     562            2 :                     if (state.dataSurface->Surface(SurfNum).OriginalClass != SurfaceClass::TDD_Diffuser) {
     563            0 :                         ShowSevereError(state,
     564            0 :                                         format("{} = {}:  Diffuser {} is not of surface type TubularDaylightDiffuser.",
     565              :                                                cCurrentModuleObject,
     566            0 :                                                ipsc->cAlphaArgs(1),
     567            0 :                                                ipsc->cAlphaArgs(3)));
     568            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     569              :                     }
     570              : 
     571            2 :                     if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TotGlassLayers > 1) {
     572            0 :                         ShowSevereError(state,
     573            0 :                                         format("{} = {}:  Diffuser {} construction ({}) must have only 1 glass layer.",
     574              :                                                cCurrentModuleObject,
     575            0 :                                                ipsc->cAlphaArgs(1),
     576            0 :                                                ipsc->cAlphaArgs(3),
     577            0 :                                                state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).Name));
     578            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     579              :                     }
     580              : 
     581            2 :                     if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TransDiff <= 1.0e-10) {
     582            0 :                         ShowSevereError(state,
     583            0 :                                         format("{} = {}:  Diffuser {} construction ({}) invalid value.",
     584              :                                                cCurrentModuleObject,
     585            0 :                                                ipsc->cAlphaArgs(1),
     586            0 :                                                ipsc->cAlphaArgs(3),
     587            0 :                                                state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).Name));
     588            0 :                         ShowContinueError(state,
     589            0 :                                           format("Diffuse solar transmittance of construction [{:.4R}] too small for calculations.",
     590            0 :                                                  state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TransDiff));
     591            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     592              :                     }
     593              : 
     594            4 :                     if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome > 0 &&
     595            2 :                         std::abs(state.dataSurface->Surface(SurfNum).Area -
     596            2 :                                  state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) > 0.1) {
     597            0 :                         if (General::SafeDivide(std::abs(state.dataSurface->Surface(SurfNum).Area -
     598            0 :                                                          state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area),
     599            0 :                                                 state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) >
     600              :                             0.1) { // greater than 10%
     601            0 :                             ShowSevereError(state,
     602            0 :                                             format("{} = {}:  Dome and diffuser areas are significantly different (>10%).",
     603              :                                                    cCurrentModuleObject,
     604            0 :                                                    ipsc->cAlphaArgs(1)));
     605            0 :                             ShowContinueError(state,
     606            0 :                                               format("...Diffuser Area=[{:.4R}]; Dome Area=[{:.4R}].",
     607            0 :                                                      state.dataSurface->Surface(SurfNum).Area,
     608            0 :                                                      state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
     609            0 :                             state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     610              :                         } else {
     611            0 :                             ShowWarningError(
     612            0 :                                 state, format("{} = {}:  Dome and diffuser areas differ by > .1 m2.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
     613            0 :                             ShowContinueError(state,
     614            0 :                                               format("...Diffuser Area=[{:.4R}]; Dome Area=[{:.4R}].",
     615            0 :                                                      state.dataSurface->Surface(SurfNum).Area,
     616            0 :                                                      state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
     617              :                         }
     618              :                     }
     619              : 
     620            2 :                     if (state.dataSurface->Surface(SurfNum).HasShadeControl) {
     621            0 :                         ShowSevereError(state,
     622            0 :                                         format("{} = {}:  Diffuser {} must not have a shading control.",
     623              :                                                cCurrentModuleObject,
     624            0 :                                                ipsc->cAlphaArgs(1),
     625            0 :                                                ipsc->cAlphaArgs(3)));
     626            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     627              :                     }
     628              : 
     629            2 :                     if (state.dataSurface->Surface(SurfNum).FrameDivider > 0) {
     630            0 :                         ShowSevereError(state,
     631            0 :                                         format("{} = {}:  Diffuser {} must not have a frame/divider.",
     632              :                                                cCurrentModuleObject,
     633            0 :                                                ipsc->cAlphaArgs(1),
     634            0 :                                                ipsc->cAlphaArgs(3)));
     635            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     636              :                     }
     637              : 
     638            2 :                     if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
     639            0 :                         ShowSevereError(state,
     640            0 :                                         format("{} = {}:  Diffuser {} Equivalent Layer Window is not supported.",
     641              :                                                cCurrentModuleObject,
     642            0 :                                                ipsc->cAlphaArgs(1),
     643            0 :                                                ipsc->cAlphaArgs(2)));
     644            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     645              :                     }
     646              : 
     647              :                     // Window multiplier is already handled in SurfaceGeometry.cc
     648              : 
     649            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser = SurfNum;
     650            2 :                     state.dataSurface->SurfWinTDDPipeNum(SurfNum) = PipeNum;
     651              :                 }
     652              : 
     653              :                 // Construction
     654            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction =
     655            2 :                     Util::FindItemInList(ipsc->cAlphaArgs(4), state.dataConstruction->Construct);
     656              : 
     657            2 :                 if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction == 0) {
     658            0 :                     ShowSevereError(
     659            0 :                         state, format("{} = {}:  Pipe construction {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(4)));
     660            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     661              :                 } else {
     662            2 :                     state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).IsUsed = true;
     663              :                 }
     664              : 
     665            2 :                 if (ipsc->rNumericArgs(1) > 0) {
     666            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter = ipsc->rNumericArgs(1);
     667              :                 } else {
     668            0 :                     ShowSevereError(state, format("{} = {}:  Pipe diameter must be greater than zero.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
     669            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     670              :                 }
     671              : 
     672            2 :                 Real64 PipeArea = 0.25 * Constant::Pi * pow_2(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter);
     673            4 :                 if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome > 0 &&
     674            2 :                     std::abs(PipeArea - state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) > 0.1) {
     675            0 :                     if (General::SafeDivide(
     676            0 :                             std::abs(PipeArea - state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area),
     677            0 :                             state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) > 0.1) { // greater than 10%
     678            0 :                         ShowSevereError(state,
     679            0 :                                         format("{} = {}:  Pipe and dome/diffuser areas are significantly different (>10%).",
     680              :                                                cCurrentModuleObject,
     681            0 :                                                ipsc->cAlphaArgs(1)));
     682            0 :                         ShowContinueError(state,
     683            0 :                                           format("...Pipe Area=[{:.4R}]; Dome/Diffuser Area=[{:.4R}].",
     684              :                                                  PipeArea,
     685            0 :                                                  state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
     686            0 :                         state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     687              :                     } else {
     688            0 :                         ShowWarningError(
     689            0 :                             state, format("{} = {}:  Pipe and dome/diffuser areas differ by > .1 m2.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
     690            0 :                         ShowContinueError(state,
     691            0 :                                           format("...Pipe Area=[{:.4R}]; Dome/Diffuser Area=[{:.4R}].",
     692              :                                                  PipeArea,
     693            0 :                                                  state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
     694              :                     }
     695              :                 }
     696              : 
     697            2 :                 if (ipsc->rNumericArgs(2) > 0) {
     698            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength = ipsc->rNumericArgs(2);
     699              :                 } else {
     700            0 :                     ShowSevereError(state, format("{} = {}:  Pipe length must be greater than zero.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
     701            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     702              :                 }
     703              : 
     704            2 :                 if (ipsc->rNumericArgs(3) > 0) {
     705            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).Reff = ipsc->rNumericArgs(3);
     706              :                 } else {
     707            0 :                     ShowSevereError(state,
     708            0 :                                     format("{} = {}:  Effective thermal resistance (R value) must be greater than zero.",
     709              :                                            cCurrentModuleObject,
     710            0 :                                            ipsc->cAlphaArgs(1)));
     711            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     712              :                 }
     713              : 
     714              :                 // Transition zones
     715            2 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones = NumAlphas - 4;
     716              : 
     717            2 :                 if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones < 1) {
     718            0 :                     ShowWarningError(state,
     719            0 :                                      format("{} = {}:  No transition zones specified.  All pipe absorbed solar goes to exterior.",
     720              :                                             cCurrentModuleObject,
     721            0 :                                             ipsc->cAlphaArgs(1)));
     722            2 :                 } else if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones > MaxTZones) {
     723            0 :                     ShowSevereError(state,
     724            0 :                                     format("{} = {}:  Maximum number of transition zones exceeded.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
     725            0 :                     state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     726              :                 } else {
     727            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone.allocate(state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
     728            4 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength.allocate(
     729            2 :                         state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
     730            4 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain.allocate(
     731            2 :                         state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
     732              : 
     733            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone = 0;
     734            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength = 0.0;
     735            2 :                     state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain = 0.0;
     736              : 
     737            4 :                     for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
     738            2 :                         std::string const TZoneName = ipsc->cAlphaArgs(TZoneNum + 4);
     739            2 :                         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum) = Util::FindItemInList(TZoneName, state.dataHeatBal->Zone);
     740            2 :                         if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum) == 0) {
     741            0 :                             ShowSevereError(state,
     742            0 :                                             format("{} = {}:  Transition zone {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), TZoneName));
     743            0 :                             state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     744              :                         }
     745              : 
     746            2 :                         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) = ipsc->rNumericArgs(TZoneNum + 3);
     747            2 :                         if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) < 0) {
     748            0 :                             ShowSevereError(state,
     749            0 :                                             format("{} = {}:  Transition zone length for {} must be zero or greater.",
     750              :                                                    cCurrentModuleObject,
     751            0 :                                                    ipsc->cAlphaArgs(1),
     752              :                                                    TZoneName));
     753            0 :                             state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
     754              :                         }
     755            2 :                     } // TZoneNum
     756              :                 }
     757              : 
     758              :             } // PipeNum
     759              : 
     760            1 :             if (state.dataDaylightingDevices->GetTDDInputErrorsFound) {
     761            0 :                 ShowFatalError(state, "Errors in DaylightingDevice:Tubular input.");
     762              :             }
     763            1 :             state.dataDayltg->TDDTransVisBeam.allocate(Constant::iHoursInDay, NumOfTDDPipes);
     764            1 :             state.dataDayltg->TDDFluxInc.allocate(Constant::iHoursInDay, NumOfTDDPipes);
     765            1 :             state.dataDayltg->TDDFluxTrans.allocate(Constant::iHoursInDay, NumOfTDDPipes);
     766           25 :             for (int hr = 1; hr <= Constant::iHoursInDay; ++hr) {
     767           72 :                 for (int tddNum = 1; tddNum <= NumOfTDDPipes; ++tddNum) {
     768           48 :                     state.dataDayltg->TDDTransVisBeam(hr, tddNum) = 0.0;
     769          192 :                     state.dataDayltg->TDDFluxInc(hr, tddNum) = Illums();
     770          192 :                     state.dataDayltg->TDDFluxTrans(hr, tddNum) = Illums();
     771              :                 } // for (tddNum)
     772              :             } // for (hr)
     773              :         }
     774          801 :     }
     775              : 
     776          801 :     void GetShelfInput(EnergyPlusData &state)
     777              :     {
     778              : 
     779              :         // SUBROUTINE INFORMATION:
     780              :         //       AUTHOR         Peter Graham Ellis
     781              :         //       DATE WRITTEN   August 2003
     782              : 
     783              :         // PURPOSE OF THIS SUBROUTINE:
     784              :         // Gets the input for light shelves and does some error checking.
     785              : 
     786              :         // METHODOLOGY EMPLOYED:
     787              :         // Standard EnergyPlus methodology.
     788              : 
     789          801 :         auto &ipsc = state.dataIPShortCut;
     790          801 :         auto &cCurrentModuleObject = ipsc->cCurrentModuleObject;
     791              : 
     792          801 :         cCurrentModuleObject = "DaylightingDevice:Shelf";
     793          801 :         int NumOfShelf = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
     794              : 
     795          801 :         if (NumOfShelf > 0) {
     796            1 :             state.dataDaylightingDevicesData->Shelf.allocate(NumOfShelf);
     797              :             int NumAlphas;  // Number of Alphas for each GetObjectItem call
     798              :             int NumNumbers; // Number of Numbers for each GetObjectItem call
     799              :             int IOStatus;   // Used in GetObjectItem
     800              : 
     801            2 :             for (int ShelfNum = 1; ShelfNum <= NumOfShelf; ++ShelfNum) {
     802            2 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     803              :                                                                          cCurrentModuleObject,
     804              :                                                                          ShelfNum,
     805            1 :                                                                          ipsc->cAlphaArgs,
     806              :                                                                          NumAlphas,
     807            1 :                                                                          ipsc->rNumericArgs,
     808              :                                                                          NumNumbers,
     809              :                                                                          IOStatus,
     810            1 :                                                                          ipsc->lNumericFieldBlanks,
     811            1 :                                                                          ipsc->lAlphaFieldBlanks,
     812            1 :                                                                          ipsc->cAlphaFieldNames,
     813            1 :                                                                          ipsc->cNumericFieldNames);
     814              :                 // Shelf name
     815            1 :                 state.dataDaylightingDevicesData->Shelf(ShelfNum).Name = ipsc->cAlphaArgs(1);
     816              : 
     817              :                 // Get window object
     818            1 :                 int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataSurface->Surface);
     819              : 
     820            1 :                 if (SurfNum == 0) {
     821            0 :                     ShowSevereError(state, format("{} = {}:  Window {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
     822            0 :                     state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     823              :                 } else {
     824            1 :                     if (state.dataSurface->Surface(SurfNum).Class != SurfaceClass::Window) {
     825            0 :                         ShowSevereError(state,
     826            0 :                                         format("{} = {}:  Window {} is not of surface type WINDOW.",
     827              :                                                cCurrentModuleObject,
     828            0 :                                                ipsc->cAlphaArgs(1),
     829            0 :                                                ipsc->cAlphaArgs(2)));
     830            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     831              :                     }
     832              : 
     833            1 :                     if (state.dataSurface->SurfDaylightingShelfInd(SurfNum) > 0) {
     834            0 :                         ShowSevereError(state,
     835            0 :                                         format("{} = {}:  Window {} is referenced by more than one shelf.",
     836              :                                                cCurrentModuleObject,
     837            0 :                                                ipsc->cAlphaArgs(1),
     838            0 :                                                ipsc->cAlphaArgs(2)));
     839            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     840              :                     }
     841              : 
     842            1 :                     if (state.dataSurface->Surface(SurfNum).HasShadeControl) {
     843            0 :                         ShowSevereError(state,
     844            0 :                                         format("{} = {}:  Window {} must not have a shading control.",
     845              :                                                cCurrentModuleObject,
     846            0 :                                                ipsc->cAlphaArgs(1),
     847            0 :                                                ipsc->cAlphaArgs(2)));
     848            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     849              :                     }
     850              : 
     851            1 :                     if (state.dataSurface->Surface(SurfNum).FrameDivider > 0) {
     852            0 :                         ShowSevereError(state,
     853            0 :                                         format("{} = {}:  Window {} must not have a frame/divider.",
     854              :                                                cCurrentModuleObject,
     855            0 :                                                ipsc->cAlphaArgs(1),
     856            0 :                                                ipsc->cAlphaArgs(2)));
     857            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     858              :                     }
     859              : 
     860            1 :                     if (state.dataSurface->Surface(SurfNum).Sides != 4) {
     861            0 :                         ShowSevereError(
     862            0 :                             state, format("{} = {}:  Window {} must have 4 sides.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
     863            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     864              :                     }
     865            1 :                     if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
     866            0 :                         ShowSevereError(state,
     867            0 :                                         format("{} = {}:  Window {} Equivalent Layer Window is not supported.",
     868              :                                                cCurrentModuleObject,
     869            0 :                                                ipsc->cAlphaArgs(1),
     870            0 :                                                ipsc->cAlphaArgs(2)));
     871            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     872              :                     }
     873              : 
     874            1 :                     state.dataDaylightingDevicesData->Shelf(ShelfNum).Window = SurfNum;
     875            1 :                     state.dataSurface->SurfDaylightingShelfInd(SurfNum) = ShelfNum;
     876              :                 }
     877              : 
     878              :                 // Get inside shelf heat transfer surface (optional)
     879            1 :                 if (ipsc->cAlphaArgs(3) != "") {
     880            1 :                     SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(3), state.dataSurface->Surface);
     881              : 
     882            1 :                     if (SurfNum == 0) {
     883            0 :                         ShowSevereError(
     884            0 :                             state, format("{} = {}:  Inside shelf {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(3)));
     885            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     886              :                     } else {
     887              :                         // No error if shelf belongs to more than one window, e.g. concave corners
     888              : 
     889            1 :                         if (state.dataSurface->Surface(SurfNum).ExtBoundCond != SurfNum) {
     890            0 :                             ShowSevereError(state,
     891            0 :                                             format("{} = {}:  Inside shelf {} must be its own Outside Boundary Condition Object.",
     892              :                                                    cCurrentModuleObject,
     893            0 :                                                    ipsc->cAlphaArgs(1),
     894            0 :                                                    ipsc->cAlphaArgs(3)));
     895            0 :                             state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     896              :                         }
     897              : 
     898            1 :                         if (state.dataSurface->Surface(SurfNum).Sides != 4) {
     899            0 :                             ShowSevereError(
     900              :                                 state,
     901            0 :                                 format(
     902            0 :                                     "{} = {}:  Inside shelf {} must have 4 sides.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(3)));
     903            0 :                             state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     904              :                         }
     905              : 
     906            1 :                         state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf = SurfNum;
     907              :                     }
     908              :                 }
     909              : 
     910              :                 // Get outside shelf attached shading surface (optional)
     911            1 :                 if (ipsc->cAlphaArgs(4) != "") {
     912            1 :                     SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(4), state.dataSurface->Surface);
     913              : 
     914            1 :                     if (SurfNum == 0) {
     915            0 :                         ShowSevereError(
     916            0 :                             state, format("{} = {}:  Outside shelf {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(4)));
     917            0 :                         state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     918              :                     } else {
     919              :                         // No error if shelf belongs to more than one window, e.g. concave corners
     920              : 
     921            1 :                         if (state.dataSurface->Surface(SurfNum).Class != SurfaceClass::Shading) {
     922            0 :                             ShowSevereError(state,
     923            0 :                                             format("{} = {}:  Outside shelf {} is not a Shading:Zone:Detailed object.",
     924              :                                                    cCurrentModuleObject,
     925            0 :                                                    ipsc->cAlphaArgs(1),
     926            0 :                                                    ipsc->cAlphaArgs(4)));
     927            0 :                             state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     928              :                         }
     929              : 
     930            1 :                         if (state.dataSurface->Surface(SurfNum).shadowSurfSched != nullptr) {
     931            0 :                             ShowSevereError(state,
     932            0 :                                             format("{} = {}:  Outside shelf {} must not have a transmittance schedule.",
     933              :                                                    cCurrentModuleObject,
     934            0 :                                                    ipsc->cAlphaArgs(1),
     935            0 :                                                    ipsc->cAlphaArgs(4)));
     936            0 :                             state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     937              :                         }
     938              : 
     939            1 :                         if (state.dataSurface->Surface(SurfNum).Sides != 4) {
     940            0 :                             ShowSevereError(
     941              :                                 state,
     942            0 :                                 format(
     943            0 :                                     "{} = {}:  Outside shelf {} must have 4 sides.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(4)));
     944            0 :                             state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     945              :                         }
     946              : 
     947            1 :                         int ConstrNum = 0;
     948              :                         // Get outside shelf construction (required if outside shelf is specified)
     949            1 :                         if (ipsc->cAlphaArgs(5) != "") {
     950            1 :                             ConstrNum = Util::FindItemInList(ipsc->cAlphaArgs(5), state.dataConstruction->Construct);
     951              : 
     952            1 :                             if (ConstrNum == 0) {
     953            0 :                                 ShowSevereError(state,
     954            0 :                                                 format("{} = {}:  Outside shelf construction {} not found.",
     955              :                                                        cCurrentModuleObject,
     956            0 :                                                        ipsc->cAlphaArgs(1),
     957            0 :                                                        ipsc->cAlphaArgs(5)));
     958            0 :                                 state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     959            1 :                             } else if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) {
     960            0 :                                 ShowSevereError(state,
     961            0 :                                                 format("{} = {}:  Outside shelf construction {} must not have WindowMaterial:Glazing.",
     962              :                                                        cCurrentModuleObject,
     963            0 :                                                        ipsc->cAlphaArgs(1),
     964            0 :                                                        ipsc->cAlphaArgs(5)));
     965            0 :                                 state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     966              :                             } else {
     967            1 :                                 state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction = ConstrNum;
     968            1 :                                 state.dataConstruction->Construct(ConstrNum).IsUsed = true;
     969              :                             }
     970              :                         } else {
     971            0 :                             ShowSevereError(state,
     972            0 :                                             format("{} = {}:  Outside shelf requires an outside shelf construction to be specified.",
     973              :                                                    cCurrentModuleObject,
     974            0 :                                                    ipsc->cAlphaArgs(1)));
     975            0 :                             state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
     976              :                         }
     977              : 
     978              :                         // Get view factor to outside shelf (optional)
     979            1 :                         if (NumNumbers > 0) {
     980            0 :                             state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor = ipsc->rNumericArgs(1);
     981              : 
     982            0 :                             if (ipsc->rNumericArgs(1) == 0.0) {
     983            0 :                                 ShowWarningError(state,
     984            0 :                                                  format("{} = {}:  View factor to outside shelf is zero.  Shelf does not reflect on window.",
     985              :                                                         cCurrentModuleObject,
     986            0 :                                                         ipsc->cAlphaArgs(1)));
     987              :                             }
     988              :                         } else {
     989            1 :                             state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor =
     990              :                                 -1.0; // Flag to have the view factor calculated during initialization
     991              :                         }
     992              : 
     993            1 :                         state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf = SurfNum;
     994              : 
     995              :                         // Reset some properties of the SURFACE:SHADING:ATTACHED object in order to receive radiation and shading
     996              :                         // Normally this would be done during initialization, but that's not early enough for some shading calculations
     997            1 :                         state.dataSurface->Surface(SurfNum).BaseSurf = SurfNum;
     998            1 :                         state.dataSurface->Surface(SurfNum).HeatTransSurf = true;
     999            1 :                         state.dataSurface->AllHTSurfaceList.push_back(SurfNum);
    1000              :                         // Is this needed? surfZone.ZoneHTNonWindowSurfaceList.push_back(SurfNum);
    1001            1 :                         state.dataSurface->Surface(SurfNum).Construction = ConstrNum; // Kludge to allow shading surface to be a heat transfer surface
    1002            1 :                         state.dataSurface->SurfActiveConstruction(SurfNum) = ConstrNum;
    1003            1 :                         state.dataConstruction->Construct(ConstrNum).IsUsed = true;
    1004              :                     }
    1005              :                 }
    1006              : 
    1007            1 :                 if (state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf == 0 && state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf == 0) {
    1008            0 :                     ShowWarningError(state,
    1009            0 :                                      format("{} = {}:  No inside shelf or outside shelf was specified.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
    1010              :                 }
    1011              : 
    1012              :             } // ShelfNum
    1013              : 
    1014            1 :             if (state.dataDaylightingDevices->GetShelfInputErrorsFound) {
    1015            0 :                 ShowFatalError(state, "Errors in DaylightingDevice:Shelf input.");
    1016              :             }
    1017              :         }
    1018          801 :     }
    1019              : 
    1020           34 :     Real64 CalcPipeTransBeam(Real64 const R,    // Reflectance of surface, constant (can be made R = f(theta) later)
    1021              :                              Real64 const A,    // Aspect ratio, L / d
    1022              :                              Real64 const Theta // Angle of entry in radians
    1023              :     )
    1024              :     {
    1025              : 
    1026              :         // SUBROUTINE INFORMATION:
    1027              :         //       AUTHOR         Peter Graham Ellis
    1028              :         //       DATE WRITTEN   May 2003
    1029              :         //       MODIFIED       na
    1030              :         //       RE-ENGINEERED  na
    1031              : 
    1032              :         // PURPOSE OF THIS SUBROUTINE:
    1033              :         // Calculates the numerical integral for the transmittance of a reflective cylinder with
    1034              :         // incident collimated beam radiation as described in Swift and Smith.
    1035              : 
    1036              :         // METHODOLOGY EMPLOYED:
    1037              :         // Since this integral can be slow, a table of values is calculated and stored during
    1038              :         // initialization of the TDD.  Intermediate values are calculated by interpolation.
    1039              :         // Transmittance of sky and ground diffuse radiation is done by other functions.
    1040              : 
    1041              :         // REFERENCES:
    1042              :         // Swift, P. D., and Smith, G. B.  "Cylindrical Mirror Light Pipes",
    1043              :         //   Solar Energy Materials and Solar Cells 36 (1995), pp. 159-168.
    1044              : 
    1045              :         // OTHER NOTES:
    1046              :         // The execution time of this function can be reduced by adjusting parameters N and xTol below.
    1047              :         // However, there is some penalty in accuracy for N < 100,000 and xTol > 150.
    1048              : 
    1049              :         // USE STATEMENTS: na
    1050              : 
    1051              :         // Return value
    1052              :         Real64 CalcPipeTransBeam;
    1053              : 
    1054              :         // Locals
    1055              :         // FUNCTION ARGUMENT DEFINITIONS:
    1056              : 
    1057              :         // FUNCTION PARAMETER DEFINITIONS:
    1058           34 :         Real64 constexpr N(100000.0); // Number of integration points
    1059           34 :         Real64 constexpr xTol(150.0); // Tolerance factor to skip iterations where dT is approximately 0
    1060              :         // Must be >= 1.0, increase this number to decrease the execution time
    1061           34 :         Real64 const myLocalTiny(TINY(1.0));
    1062              : 
    1063              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1064              :         Real64 i; // Integration interval between points
    1065              :         Real64 s; // Entry point
    1066              :         Real64 dT;
    1067              :         Real64 T; // Beam transmittance for collimated solar real
    1068              :         Real64 x; // Intermediate variables for speed optimization
    1069              :         Real64 c1;
    1070              :         Real64 c2;
    1071              :         Real64 xLimit; // Limiting x value to prevent floating point underflow
    1072              : 
    1073           34 :         CalcPipeTransBeam = 0.0;
    1074              : 
    1075           34 :         T = 0.0;
    1076           34 :         i = 1.0 / N;
    1077              : 
    1078           34 :         xLimit = (std::log(pow_2(N) * myLocalTiny) / std::log(R)) / xTol;
    1079              : 
    1080           34 :         c1 = A * std::tan(Theta);
    1081           34 :         c2 = 4.0 / Constant::Pi;
    1082              : 
    1083           34 :         s = i;
    1084      3400000 :         while (s < (1.0 - i)) {
    1085      3399966 :             x = c1 / s;
    1086              : 
    1087      3399966 :             if (x < xLimit) {
    1088      3007257 :                 dT = c2 * std::pow(R, int(x)) * (1.0 - (1.0 - R) * (x - int(x))) * pow_2(s) / std::sqrt(1.0 - pow_2(s));
    1089      3007257 :                 T += dT;
    1090              :             }
    1091              : 
    1092      3399966 :             s += i;
    1093              :         }
    1094              : 
    1095           34 :         T /= (N - 1.0); // - 1.0, because started on i, not 0
    1096              : 
    1097           34 :         CalcPipeTransBeam = T;
    1098              : 
    1099           34 :         return CalcPipeTransBeam;
    1100              :     }
    1101              : 
    1102            2 :     Real64 CalcTDDTransSolIso(EnergyPlusData &state, int const PipeNum) // TDD pipe object number
    1103              :     {
    1104              : 
    1105              :         // SUBROUTINE INFORMATION:
    1106              :         //       AUTHOR         Peter Graham Ellis
    1107              :         //       DATE WRITTEN   July 2003
    1108              :         //       MODIFIED       na
    1109              :         //       RE-ENGINEERED  na
    1110              : 
    1111              :         // PURPOSE OF THIS SUBROUTINE:
    1112              :         // Calculates the transmittance of sky isotropic radiation for use with the anisotropic sky transmittance.
    1113              :         // This value is also used for all ground reflected solar radiation (which is isotropic).
    1114              : 
    1115              :         // METHODOLOGY EMPLOYED:
    1116              :         // The transmittance is calculated and stored once at initialization because the value is a constant.
    1117              :         // The routine numerically integrates over the entire sky.  All radiation is isotropic, but incident
    1118              :         // angle varies over the hemisphere.
    1119              :         // Trans = Flux Transmitted / Flux Incident
    1120              :         // Not sure if shading and tilt is adequately accounted for by DifShdgRatioIsoSky later on or not...
    1121              : 
    1122              :         // REFERENCES:
    1123              :         // See AnisoSkyViewFactors in SolarShading.cc.
    1124              : 
    1125              :         // USE STATEMENTS: na
    1126              : 
    1127              :         // Return value
    1128              :         Real64 CalcTDDTransSolIso;
    1129              : 
    1130              :         // Locals
    1131              :         // FUNCTION ARGUMENT DEFINITIONS:
    1132              : 
    1133              :         // FUNCTION PARAMETER DEFINITIONS:
    1134            2 :         int constexpr NPH(1000); // Number of altitude integration points
    1135              : 
    1136              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1137            2 :         Real64 FluxInc = 0.0;   // Incident solar flux
    1138            2 :         Real64 FluxTrans = 0.0; // Transmitted solar flux
    1139              :         Real64 trans;           // Total beam solar transmittance of TDD
    1140              :         Real64 COSI;            // Cosine of incident angle
    1141              :         Real64 SINI;            // Sine of incident angle
    1142              : 
    1143            2 :         Real64 const dPH = 90.0 * Constant::DegToRad / NPH; // Altitude angle of sky element
    1144            2 :         Real64 PH = 0.5 * dPH;                              // Altitude angle increment
    1145              : 
    1146              :         // Integrate from 0 to Pi/2 altitude
    1147         2002 :         for (int N = 1; N <= NPH; ++N) {
    1148         2000 :             COSI = std::cos(Constant::PiOvr2 - PH);
    1149         2000 :             SINI = std::sin(Constant::PiOvr2 - PH);
    1150              : 
    1151         2000 :             Real64 P = COSI; // Angular distribution function: P = COS(Incident Angle) for diffuse isotropic
    1152              : 
    1153              :             // Calculate total TDD transmittance for given angle
    1154         2000 :             trans = TransTDD(state, PipeNum, COSI, RadType::SolarBeam);
    1155              : 
    1156         2000 :             FluxInc += P * SINI * dPH;
    1157         2000 :             FluxTrans += trans * P * SINI * dPH;
    1158              : 
    1159         2000 :             PH += dPH; // Increment the altitude angle
    1160              :         } // N
    1161              : 
    1162            2 :         CalcTDDTransSolIso = FluxTrans / FluxInc;
    1163              : 
    1164            2 :         return CalcTDDTransSolIso;
    1165              :     }
    1166              : 
    1167            2 :     Real64 CalcTDDTransSolHorizon(EnergyPlusData &state, int const PipeNum) // TDD pipe object number
    1168              :     {
    1169              : 
    1170              :         // SUBROUTINE INFORMATION:
    1171              :         //       AUTHOR         Peter Graham Ellis
    1172              :         //       DATE WRITTEN   July 2003
    1173              :         //       MODIFIED       na
    1174              :         //       RE-ENGINEERED  na
    1175              : 
    1176              :         // PURPOSE OF THIS SUBROUTINE:
    1177              :         // Calculates the transmittance of sky horizon radiation for use with the anisotropic sky transmittance.
    1178              : 
    1179              :         // METHODOLOGY EMPLOYED:
    1180              :         // The transmittance is calculated and stored once at initialization because the value is a constant.
    1181              :         // The routine numerically integrates over the horizon as an infinitesimally narrow strip of the sky.
    1182              :         // Horizon radiation is isotropic, but incident angle varies over the semicircle.
    1183              :         // Trans = Flux Transmitted / Flux Incident
    1184              :         // Not sure if shading is adequately accounted for by DifShdgRatioHoriz later on or not...
    1185              : 
    1186              :         // REFERENCES:
    1187              :         // See AnisoSkyViewFactors in SolarShading.cc.
    1188              : 
    1189              :         // Using/Aliasing
    1190              :         using namespace DataSurfaces;
    1191              : 
    1192              :         // Return value
    1193              :         Real64 CalcTDDTransSolHorizon;
    1194              : 
    1195              :         // Locals
    1196              :         // FUNCTION ARGUMENT DEFINITIONS:
    1197              : 
    1198              :         // FUNCTION PARAMETER DEFINITIONS:
    1199            2 :         int constexpr NTH(18); // Number of azimuth integration points
    1200              : 
    1201              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1202            2 :         Real64 FluxInc = 0.0;   // Incident solar flux
    1203            2 :         Real64 FluxTrans = 0.0; // Transmitted solar flux
    1204              :         Real64 CosPhi;          // Cosine of TDD:DOME altitude angle
    1205              :         Real64 Theta;           // TDD:DOME azimuth angle
    1206              : 
    1207            2 :         CosPhi = std::cos(Constant::PiOvr2 -
    1208            2 :                           state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Tilt * Constant::DegToRad);
    1209            2 :         Theta = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Azimuth * Constant::DegToRad;
    1210              : 
    1211            2 :         if (CosPhi > 0.01) { // Dome has a view of the horizon
    1212              :             // Integrate over the semicircle
    1213            2 :             Real64 const THMIN = Theta - Constant::PiOvr2; // Minimum azimuth integration limit
    1214              :             // Real64 const THMAX = Theta + PiOvr2; // Maximum azimuth integration limit
    1215            2 :             Real64 const dTH = 180.0 * Constant::DegToRad / NTH; // Azimuth angle increment
    1216            2 :             Real64 TH = THMIN + 0.5 * dTH;                       // Azimuth angle of sky horizon element
    1217              : 
    1218           38 :             for (int N = 1; N <= NTH; ++N) {
    1219              :                 // Calculate incident angle between dome outward normal and horizon element
    1220           36 :                 Real64 COSI = CosPhi * std::cos(TH - Theta); // Cosine of the incident angle
    1221              : 
    1222              :                 // Calculate total TDD transmittance for given angle
    1223           36 :                 Real64 trans = TransTDD(state, PipeNum, COSI, RadType::SolarBeam); // Total beam solar transmittance of TDD
    1224              : 
    1225           36 :                 FluxInc += COSI * dTH;
    1226           36 :                 FluxTrans += trans * COSI * dTH;
    1227              : 
    1228           36 :                 TH += dTH; // Increment the azimuth angle
    1229              :             } // N
    1230              : 
    1231            2 :             CalcTDDTransSolHorizon = FluxTrans / FluxInc;
    1232              : 
    1233              :         } else {                          // Dome is nearly horizontal and has almost no view of the horizon
    1234            0 :             CalcTDDTransSolHorizon = 0.0; // = TransTDD(state, PipeNum, ???, SolarBeam) ! Could change to an angle near the horizon
    1235              :         }
    1236              : 
    1237            2 :         return CalcTDDTransSolHorizon;
    1238              :     }
    1239              : 
    1240         4928 :     Real64 CalcTDDTransSolAniso(EnergyPlusData &state,
    1241              :                                 int const PipeNum, // TDD pipe object number
    1242              :                                 Real64 const COSI  // Cosine of the incident angle
    1243              :     )
    1244              :     {
    1245              : 
    1246              :         // SUBROUTINE INFORMATION:
    1247              :         //       AUTHOR         Peter Graham Ellis
    1248              :         //       DATE WRITTEN   July 2003
    1249              :         //       MODIFIED       na
    1250              :         //       RE-ENGINEERED  na
    1251              : 
    1252              :         // PURPOSE OF THIS SUBROUTINE:
    1253              :         // Calculates the transmittance of the anisotropic sky.
    1254              : 
    1255              :         // METHODOLOGY EMPLOYED:
    1256              :         // Similar to the Trans = FluxTrans/FluxInc integrations above, the anisotropic sky can be decomposed
    1257              :         // and have a different transmittance applied to each component.
    1258              :         //   FluxInc = IsoSkyRad + CircumSolarRad + HorizonRad
    1259              :         //   FluxTrans = T1*IsoSkyRad + T2*CircumSolarRad + T3*HorizonRad
    1260              :         // It turns out that FluxTrans/FluxInc is equivalent to AnisoSkyTDDMult/SurfAnisoSkyMult.
    1261              :         // SurfAnisoSkyMult has been conveniently calculated already in AnisoSkyViewFactors in SolarShading.cc.
    1262              :         // SurfAnisoSkyMult = MultIsoSky*DifShdgRatioIsoSky + MultCircumSolar*SunlitFrac + MultHorizonZenith*DifShdgRatioHoriz
    1263              :         // In this routine a similar AnisoSkyTDDMult is calculated that applies the appropriate transmittance to each
    1264              :         // of the components above.  The result is Trans = AnisoSkyTDDMult/SurfAnisoSkyMult.
    1265              :         // Shading and orientation are already taken care of by DifShdgRatioIsoSky and DifShdgRatioHoriz.
    1266              : 
    1267              :         // REFERENCES:
    1268              :         // See AnisoSkyViewFactors in SolarShading.cc.
    1269              : 
    1270              :         // Return value
    1271              :         Real64 CalcTDDTransSolAniso;
    1272              : 
    1273              :         // Locals
    1274              :         // FUNCTION ARGUMENT DEFINITIONS:
    1275              : 
    1276              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1277              :         int DomeSurf;           // TDD:DOME surface number
    1278              :         Real64 IsoSkyRad;       // Isotropic sky radiation component
    1279              :         Real64 CircumSolarRad;  // Circumsolar sky radiation component
    1280              :         Real64 HorizonRad;      // Horizon sky radiation component
    1281              :         Real64 AnisoSkyTDDMult; // Anisotropic sky multiplier for TDD
    1282              : 
    1283         4928 :         DomeSurf = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
    1284              : 
    1285         4928 :         if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
    1286            0 :             state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    1287         4928 :             IsoSkyRad = state.dataSolarShading->SurfMultIsoSky(DomeSurf) * state.dataSolarShading->SurfDifShdgRatioIsoSky(DomeSurf);
    1288         4928 :             HorizonRad = state.dataSolarShading->SurfMultHorizonZenith(DomeSurf) * state.dataSolarShading->SurfDifShdgRatioHoriz(DomeSurf);
    1289              :         } else {
    1290            0 :             IsoSkyRad = state.dataSolarShading->SurfMultIsoSky(DomeSurf) * state.dataSolarShading->SurfCurDifShdgRatioIsoSky(DomeSurf);
    1291            0 :             HorizonRad = state.dataSolarShading->SurfMultHorizonZenith(DomeSurf) *
    1292            0 :                          state.dataSolarShading->SurfDifShdgRatioHorizHRTS(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay, DomeSurf);
    1293              :         }
    1294         4928 :         CircumSolarRad = state.dataSolarShading->SurfMultCircumSolar(DomeSurf) *
    1295         4928 :                          state.dataHeatBal->SurfSunlitFrac(state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep, DomeSurf);
    1296              : 
    1297         4928 :         AnisoSkyTDDMult = state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso * IsoSkyRad +
    1298         4928 :                           TransTDD(state, PipeNum, COSI, RadType::SolarBeam) * CircumSolarRad +
    1299         4928 :                           state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolHorizon * HorizonRad;
    1300              : 
    1301         4928 :         if (state.dataSolarShading->SurfAnisoSkyMult(DomeSurf) > 0.0) {
    1302         4928 :             CalcTDDTransSolAniso = AnisoSkyTDDMult / state.dataSolarShading->SurfAnisoSkyMult(DomeSurf);
    1303              :         } else {
    1304            0 :             CalcTDDTransSolAniso = 0.0;
    1305              :         }
    1306              : 
    1307         4928 :         return CalcTDDTransSolAniso;
    1308              :     }
    1309              : 
    1310       802540 :     Real64 TransTDD(EnergyPlusData &state,
    1311              :                     int const PipeNum,          // TDD pipe object number
    1312              :                     Real64 const COSI,          // Cosine of the incident angle
    1313              :                     RadType const RadiationType // Radiation type flag
    1314              :     )
    1315              :     {
    1316              : 
    1317              :         // SUBROUTINE INFORMATION:
    1318              :         //       AUTHOR         Peter Graham Ellis
    1319              :         //       DATE WRITTEN   May 2003
    1320              :         //       MODIFIED       na
    1321              :         //       RE-ENGINEERED  na
    1322              : 
    1323              :         // PURPOSE OF THIS SUBROUTINE:
    1324              :         // Calculates the total transmittance of the TDD for specified radiation type.
    1325              : 
    1326              :         // METHODOLOGY EMPLOYED:
    1327              :         // The transmittances for each component (i.e. TDD:DIFFUSER, TDD:DOME, and pipe) are calculated.
    1328              :         // All transmittances are multiplied to get the total for the TDD:
    1329              :         //   TransTDD = transDome * transPipe * transDiff
    1330              :         // Transmittance of beam radiation is calculated by interpolating the values in a
    1331              :         // table created during initialization.  The table values are from Swift and Smith's
    1332              :         // numerical integral for collimated beam radiation.
    1333              :         // Transmittances of isotropic and anisotropic diffuse radiation are more complicated and call
    1334              :         // other subroutines in this module.
    1335              :         // All light reaching the TDD:DIFFUSER is assumed to be diffuse.
    1336              :         // NOTE: Dome transmittance could be improved by taking into account curvature of the dome.
    1337              : 
    1338              :         // REFERENCES:
    1339              :         // Swift, P. D., and Smith, G. B.  "Cylindrical Mirror Light Pipes",
    1340              :         //   Solar Energy Materials and Solar Cells 36 (1995), pp. 159-168.
    1341              : 
    1342              :         // Return value
    1343              :         Real64 TransTDD;
    1344              : 
    1345              :         // Locals
    1346              :         // FUNCTION ARGUMENT DEFINITIONS:
    1347              : 
    1348              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1349              :         int constDome; // Construction object number for TDD:DOME
    1350              :         int constDiff; // Construction object number for TDD:DIFFUSER
    1351              :         Real64 transDome;
    1352              :         Real64 transPipe;
    1353              :         Real64 transDiff;
    1354              : 
    1355       802540 :         TransTDD = 0.0;
    1356              : 
    1357              :         // Get constructions of each TDD component
    1358       802540 :         constDome = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Construction;
    1359       802540 :         constDiff = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser).Construction;
    1360              : 
    1361              :         // Get the transmittance of each component and of total TDD
    1362       802540 :         switch (RadiationType) {
    1363       788268 :         case RadType::VisibleBeam: {
    1364       788268 :             transDome = Window::POLYF(COSI, state.dataConstruction->Construct(constDome).TransVisBeamCoef);
    1365       788268 :             transPipe = InterpolatePipeTransBeam(state, COSI, state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransVisBeam);
    1366       788268 :             transDiff = state.dataConstruction->Construct(constDiff).TransDiffVis; // May want to change to POLYF also!
    1367              : 
    1368       788268 :             TransTDD = transDome * transPipe * transDiff;
    1369              : 
    1370       788268 :         } break;
    1371         9344 :         case RadType::SolarBeam: {
    1372         9344 :             transDome = Window::POLYF(COSI, state.dataConstruction->Construct(constDome).TransSolBeamCoef);
    1373         9344 :             transPipe = InterpolatePipeTransBeam(state, COSI, state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransSolBeam);
    1374         9344 :             transDiff = state.dataConstruction->Construct(constDiff).TransDiff; // May want to change to POLYF also!
    1375              : 
    1376         9344 :             TransTDD = transDome * transPipe * transDiff;
    1377              : 
    1378         9344 :         } break;
    1379         4928 :         case RadType::SolarAniso: {
    1380         4928 :             TransTDD = CalcTDDTransSolAniso(state, PipeNum, COSI);
    1381         4928 :         } break;
    1382            0 :         case RadType::SolarIso: {
    1383            0 :             TransTDD = state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso;
    1384            0 :         } break;
    1385            0 :         default:
    1386            0 :             break;
    1387              :         }
    1388              : 
    1389       802540 :         return TransTDD;
    1390              :     }
    1391              : 
    1392       797612 :     Real64 InterpolatePipeTransBeam(EnergyPlusData &state,
    1393              :                                     Real64 const COSI,               // Cosine of the incident angle
    1394              :                                     const Array1D<Real64> &transBeam // Table of beam transmittance vs. cosine angle
    1395              :     )
    1396              :     {
    1397              : 
    1398              :         // SUBROUTINE INFORMATION:
    1399              :         //       AUTHOR         Peter Graham Ellis
    1400              :         //       DATE WRITTEN   July 2003
    1401              :         //       MODIFIED       na
    1402              :         //       RE-ENGINEERED  na
    1403              : 
    1404              :         // PURPOSE OF THIS SUBROUTINE:
    1405              :         // Interpolates the beam transmittance vs. cosine angle table.
    1406              : 
    1407              :         // METHODOLOGY EMPLOYED: na
    1408              :         // REFERENCES: na
    1409              : 
    1410              :         // Using/Aliasing
    1411              :         using Fluid::FindArrayIndex; // USEd code could be copied here to eliminate dependence on FluidProperties
    1412              : 
    1413              :         // Return value
    1414              :         Real64 InterpolatePipeTransBeam;
    1415              : 
    1416              :         // Argument array dimensioning
    1417       797612 :         EP_SIZE_CHECK(transBeam, NumOfAngles);
    1418              : 
    1419              :         // Locals
    1420              :         // FUNCTION ARGUMENT DEFINITIONS:
    1421              : 
    1422              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1423              :         int Lo;
    1424              :         int Hi;
    1425              :         Real64 m;
    1426              :         Real64 b;
    1427              : 
    1428       797612 :         InterpolatePipeTransBeam = 0.0;
    1429              : 
    1430              :         // Linearly interpolate transBeam/COSAngle table to get value at current cosine of the angle
    1431       797612 :         Lo = FindArrayIndex(COSI, state.dataDaylightingDevices->COSAngle);
    1432       797612 :         Hi = Lo + 1;
    1433              : 
    1434       797612 :         if (Lo > 0 && Hi <= NumOfAngles) {
    1435       797192 :             m = (transBeam(Hi) - transBeam(Lo)) / (state.dataDaylightingDevices->COSAngle(Hi) - state.dataDaylightingDevices->COSAngle(Lo));
    1436       797192 :             b = transBeam(Lo) - m * state.dataDaylightingDevices->COSAngle(Lo);
    1437              : 
    1438       797192 :             InterpolatePipeTransBeam = m * COSI + b;
    1439              :         } else {
    1440          420 :             InterpolatePipeTransBeam = 0.0;
    1441              :         }
    1442              : 
    1443       797612 :         return InterpolatePipeTransBeam;
    1444              :     }
    1445              : 
    1446            4 :     int FindTDDPipe(EnergyPlusData &state, int const WinNum)
    1447              :     {
    1448              : 
    1449              :         // SUBROUTINE INFORMATION:
    1450              :         //       AUTHOR         Peter Graham Ellis
    1451              :         //       DATE WRITTEN   May 2003
    1452              :         //       MODIFIED       na
    1453              :         //       RE-ENGINEERED  na
    1454              : 
    1455              :         // PURPOSE OF THIS SUBROUTINE:
    1456              :         // Given the TDD:DOME or TDD:DIFFUSER object number, returns TDD pipe number.
    1457              : 
    1458              :         // Return value
    1459              :         int FindTDDPipe;
    1460              : 
    1461              :         // FUNCTION LOCAL VARIABLE DECLARATIONS:
    1462              :         int PipeNum; // TDD pipe object number
    1463              : 
    1464            4 :         FindTDDPipe = 0;
    1465              : 
    1466            4 :         if ((int)state.dataDaylightingDevicesData->TDDPipe.size() <= 0) {
    1467            0 :             ShowFatalError(state,
    1468            0 :                            format("FindTDDPipe: Surface={}, TDD:Dome object does not reference a valid Diffuser object....needs "
    1469              :                                   "DaylightingDevice:Tubular of same name as Surface.",
    1470            0 :                                   state.dataSurface->Surface(WinNum).Name));
    1471              :         }
    1472              : 
    1473           12 :         for (PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
    1474           16 :             if ((WinNum == state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome) ||
    1475            8 :                 (WinNum == state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser)) {
    1476            0 :                 FindTDDPipe = PipeNum;
    1477            0 :                 break;
    1478              :             }
    1479              :         } // PipeNum
    1480              : 
    1481            4 :         return FindTDDPipe;
    1482              :     }
    1483              : 
    1484      2828408 :     void DistributeTDDAbsorbedSolar(EnergyPlusData &state)
    1485              :     {
    1486              : 
    1487              :         // SUBROUTINE INFORMATION:
    1488              :         //       AUTHOR         Peter Graham Ellis
    1489              :         //       DATE WRITTEN   July 2003
    1490              :         //       MODIFIED       na
    1491              :         //       RE-ENGINEERED  na
    1492              : 
    1493              :         // PURPOSE OF THIS SUBROUTINE:
    1494              :         // Sums the absorbed solar gains from TDD pipes that pass through transition zones.
    1495              : 
    1496              :         // METHODOLOGY EMPLOYED:
    1497              :         // The total absorbed solar gain is a sum of the following gains:
    1498              :         //   1. Inward bound solar absorbed by multiple pipe reflections (solar entering pipe - solar exiting pipe)
    1499              :         //   2. Outward bound solar absorbed by multiple pipe reflections due to:
    1500              :         //     a. Reflection off of diffuser surface (inside of TDD)
    1501              :         //     b. Zone diffuse interior shortwave incident on the diffuser from windows, lights, etc.
    1502              :         //   3. Inward absorbed solar in dome and diffuser glass
    1503              :         // This subroutine is called by InitIntSolarDistribution in HeatBalanceSurfaceManager.cc.
    1504              : 
    1505      2832458 :         for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
    1506         4050 :             int DiffSurf = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser;
    1507         4050 :             Real64 transDiff = state.dataConstruction->Construct(state.dataSurface->Surface(DiffSurf).Construction).TransDiff;
    1508              : 
    1509              :             // Calculate diffuse solar reflected back up the pipe by the inside surface of the TDD:DIFFUSER
    1510              :             // All solar arriving at the diffuser is assumed to be isotropically diffuse by this point
    1511         4050 :             Real64 QRefl = (state.dataHeatBal->SurfQRadSWOutIncident(DiffSurf) - state.dataHeatBal->SurfWinQRadSWwinAbsTot(DiffSurf)) *
    1512         4050 :                                state.dataSurface->Surface(DiffSurf).Area -
    1513         4050 :                            state.dataSurface->SurfWinTransSolar(DiffSurf);
    1514              : 
    1515              :             // Add diffuse interior shortwave reflected from zone surfaces and from zone sources, lights, etc.
    1516         4050 :             QRefl += state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(DiffSurf).SolarEnclIndex) *
    1517         4050 :                      state.dataSurface->Surface(DiffSurf).Area * transDiff;
    1518              : 
    1519         4050 :             Real64 TotTDDPipeGain = state.dataSurface->SurfWinTransSolar(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome) -
    1520         4050 :                                     state.dataHeatBal->SurfQRadSWOutIncident(DiffSurf) * state.dataSurface->Surface(DiffSurf).Area +
    1521         4050 :                                     QRefl * (1.0 - state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso / transDiff) +
    1522         4050 :                                     state.dataHeatBal->SurfWinQRadSWwinAbs(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome, 1) *
    1523         4050 :                                         state.dataSurface->Surface(DiffSurf).Area / 2.0 +
    1524         4050 :                                     state.dataHeatBal->SurfWinQRadSWwinAbs(DiffSurf, 1) * state.dataSurface->Surface(DiffSurf).Area /
    1525         4050 :                                         2.0; // Solar entering pipe | Solar exiting pipe | Absorbed due to
    1526              :                                              // reflections on the way out | Inward absorbed solar from dome
    1527              :                                              // glass | Inward absorbed solar from diffuser glass
    1528         4050 :             state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeAbsorbedSolar = max(0.0, TotTDDPipeGain); // Report variable [W]
    1529              : 
    1530         8100 :             for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
    1531              :                 // Distribute absorbed solar gain in proportion to transition zone length
    1532         4050 :                 state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain(TZoneNum) =
    1533         4050 :                     TotTDDPipeGain * (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) /
    1534         4050 :                                       state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength);
    1535              :             } // TZoneNum
    1536              :         }
    1537      2828408 :     }
    1538              : 
    1539            1 :     void CalcViewFactorToShelf(EnergyPlusData &state, int const ShelfNum) // Daylighting shelf object number
    1540              :     {
    1541              : 
    1542              :         // SUBROUTINE INFORMATION:
    1543              :         //       AUTHOR         Peter Graham Ellis
    1544              :         //       DATE WRITTEN   August 2003
    1545              :         //       MODIFIED       na
    1546              :         //       RE-ENGINEERED  na
    1547              : 
    1548              :         // PURPOSE OF THIS SUBROUTINE:
    1549              :         // Attempts to calculate exact analytical view factor from window to outside shelf.
    1550              : 
    1551              :         // METHODOLOGY EMPLOYED:
    1552              :         // Uses a standard analytical solution.  It is required that window and shelf have the same width, i.e.
    1553              :         // one edge (or two vertices) shared in common.  An error or warning is issued if not true.
    1554              :         // A more general routine should be implemented at some point to solve for more complicated geometries.
    1555              :         // Until then, the user has the option to specify their own solution for the view factor in the input object.
    1556              : 
    1557              :         // REFERENCES:
    1558              :         // Mills, A. F.  Heat and Mass Transfer, 1995, p. 499.  (Shape factor for adjacent rectangles.)
    1559              : 
    1560              :         // USE STATEMENTS:
    1561              : 
    1562              :         // Locals
    1563              :         // SUBROUTINE ARGUMENT DEFINITIONS:
    1564              : 
    1565              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1566              :         Real64 W; // Width, height, and length of window/shelf geometry
    1567              :         Real64 H;
    1568              :         Real64 L;
    1569              :         Real64 M; // Intermediate variables
    1570              :         Real64 N;
    1571              :         Real64 E1; // Intermediate equations
    1572              :         Real64 E2;
    1573              :         Real64 E3;
    1574              :         Real64 E4;
    1575              :         int VWin; // Vertex indices
    1576              :         int VShelf;
    1577              :         int NumMatch; // Number of vertices matched
    1578              : 
    1579            1 :         W = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Width;
    1580            1 :         H = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Height;
    1581              : 
    1582              :         // Find length, i.e. projection, of outside shelf
    1583            1 :         if (state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Width == W) {
    1584            1 :             L = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Height;
    1585            0 :         } else if (state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Height == W) {
    1586            0 :             L = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Width;
    1587              :         } else {
    1588            0 :             ShowFatalError(state,
    1589            0 :                            format("DaylightingDevice:Shelf = {}:  Width of window and outside shelf do not match.",
    1590            0 :                                   state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1591              :         }
    1592              : 
    1593              :         // Error if more or less than two vertices match
    1594            1 :         NumMatch = 0;
    1595            5 :         for (VWin = 1; VWin <= 4; ++VWin) {
    1596           20 :             for (VShelf = 1; VShelf <= 4; ++VShelf) {
    1597           16 :                 if (distance(state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Vertex(VWin),
    1598           32 :                              state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Vertex(VShelf)) == 0.0) {
    1599            2 :                     ++NumMatch;
    1600              :                 }
    1601              :             }
    1602              :         }
    1603              : 
    1604            1 :         if (NumMatch < 2) {
    1605            0 :             ShowWarningError(
    1606              :                 state,
    1607            0 :                 format("DaylightingDevice:Shelf = {}:  Window and outside shelf must share two vertices.  View factor calculation may be inaccurate.",
    1608            0 :                        state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1609            1 :         } else if (NumMatch > 2) {
    1610            0 :             ShowFatalError(state,
    1611            0 :                            format("DaylightingDevice:Shelf = {}:  Window and outside shelf share too many vertices.",
    1612            0 :                                   state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1613              :         }
    1614              : 
    1615              :         // Calculate exact analytical view factor from window to outside shelf
    1616            1 :         M = H / W;
    1617            1 :         N = L / W;
    1618              : 
    1619            1 :         E1 = M * std::atan(1.0 / M) + N * std::atan(1.0 / N) - std::sqrt(pow_2(N) + pow_2(M)) * std::atan(std::pow(pow_2(N) + pow_2(M), -0.5));
    1620            1 :         E2 = ((1.0 + pow_2(M)) * (1.0 + pow_2(N))) / (1.0 + pow_2(M) + pow_2(N));
    1621            1 :         E3 = std::pow(pow_2(M) * (1.0 + pow_2(M) + pow_2(N)) / ((1.0 + pow_2(M)) * (pow_2(M) + pow_2(N))), pow_2(M));
    1622            1 :         E4 = std::pow(pow_2(N) * (1.0 + pow_2(M) + pow_2(N)) / ((1.0 + pow_2(N)) * (pow_2(M) + pow_2(N))), pow_2(N));
    1623              : 
    1624            1 :         state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor = (1.0 / (Constant::Pi * M)) * (E1 + 0.25 * std::log(E2 * E3 * E4));
    1625            1 :     }
    1626              : 
    1627            1 :     void adjustViewFactorsWithShelf(
    1628              :         EnergyPlusData &state, Real64 &viewFactorToShelf, Real64 &viewFactorToSky, Real64 &viewFactorToGround, int WinSurf, int ShelfNum)
    1629              :     {
    1630              :         // First, make sure none of the view factors are less than zero and return if there isn't a problem or if
    1631              :         // view factor to shelf greater than one.  Both cases together would also eliminate if other views are zero
    1632              :         // which means nothing would need to be done.
    1633            1 :         if (viewFactorToSky <= 0.0) {
    1634            0 :             viewFactorToSky = 0.0;
    1635              :         }
    1636            1 :         if (viewFactorToGround <= 0.0) {
    1637            0 :             viewFactorToGround = 0.0;
    1638              :         }
    1639            1 :         if (viewFactorToShelf <= 0.0) { // No shelf impact for which to account
    1640            0 :             ShowWarningError(state,
    1641            0 :                              format("DaylightingDevice:Shelf = {}:  Window view factor to shelf was less than 0.  This should not happen.",
    1642            0 :                                     state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1643            0 :             ShowContinueError(state, "The view factor has been reset to zero.");
    1644            0 :             viewFactorToShelf = 0.0;
    1645            0 :             if ((viewFactorToGround + viewFactorToSky) > 1.0) { // This data came in incorrect, fix by proportional reduction
    1646            0 :                 viewFactorToGround = viewFactorToGround / (viewFactorToGround + viewFactorToSky);
    1647            0 :                 viewFactorToSky = 1.0 - viewFactorToGround;
    1648            0 :                 ShowWarningError(state,
    1649            0 :                                  format("DaylightingDevice:Shelf = {}:  The sum of the window view factors to ground and sky were greater than 1.  "
    1650              :                                         "This should not happen.",
    1651            0 :                                         state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1652            0 :                 ShowContinueError(
    1653              :                     state, "The view factors have been reset to so that they do not exceed 1.  Check/fix your input file data to avoid this issue.");
    1654              :             }
    1655            0 :             return;
    1656              :         }
    1657            1 :         if (viewFactorToShelf + viewFactorToSky + viewFactorToGround <= 1.0) {
    1658            1 :             return; // nothing wrong here
    1659              :         }
    1660            0 :         if (viewFactorToShelf >= 1.0) { // Don't allow shelf view of greater than 1 (zero out other views)
    1661            0 :             ShowWarningError(state,
    1662            0 :                              format("DaylightingDevice:Shelf = {}:  Window view factor to shelf was greater than 1.  This should not happen.",
    1663            0 :                                     state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1664            0 :             ShowContinueError(state, "The view factor has been reset to 1 and the other view factors to sky and ground have been set to 0.");
    1665            0 :             viewFactorToShelf = 1.0;
    1666            0 :             viewFactorToGround = 0.0;
    1667            0 :             viewFactorToSky = 0.0;
    1668            0 :             return;
    1669              :         }
    1670              : 
    1671              :         // If the flow is still here, there is something that needs to be adjusted so set the maximum shelf height and the minimum window height
    1672            0 :         int ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf;
    1673            0 :         Real64 zShelfMax = state.dataSurface->Surface(ShelfSurf).Vertex(1).z;
    1674            0 :         Real64 zShelfMin = zShelfMax;
    1675            0 :         for (int vertex = 2; vertex <= state.dataSurface->Surface(ShelfSurf).Sides; ++vertex) {
    1676            0 :             if (state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z > zShelfMax) {
    1677            0 :                 zShelfMax = state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z;
    1678              :             }
    1679            0 :             if (state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z < zShelfMin) {
    1680            0 :                 zShelfMin = state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z;
    1681              :             }
    1682              :         }
    1683            0 :         Real64 zWinMax = state.dataSurface->Surface(WinSurf).Vertex(1).z;
    1684            0 :         Real64 zWinMin = zWinMax;
    1685            0 :         for (int vertex = 2; vertex <= state.dataSurface->Surface(WinSurf).Sides; ++vertex) {
    1686            0 :             if (state.dataSurface->Surface(WinSurf).Vertex(vertex).z > zWinMax) {
    1687            0 :                 zWinMax = state.dataSurface->Surface(WinSurf).Vertex(vertex).z;
    1688              :             }
    1689            0 :             if (state.dataSurface->Surface(WinSurf).Vertex(vertex).z < zWinMin) {
    1690            0 :                 zWinMin = state.dataSurface->Surface(WinSurf).Vertex(vertex).z;
    1691              :             }
    1692              :         }
    1693              : 
    1694              :         Real64 leftoverViewFactor;
    1695              :         // Now correct the view factors based on the location of the shelf with respect to the window
    1696            0 :         ShowWarningError(
    1697              :             state,
    1698            0 :             format("DaylightingDevice:Shelf = {}:  Window view factor to shelf [{:.2R}] results in a sum of view factors greater than 1.",
    1699            0 :                    state.dataDaylightingDevicesData->Shelf(ShelfNum).Name,
    1700            0 :                    state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor));
    1701            0 :         if (zWinMin >= zShelfMax) { // Shelf is fully below window, reduce view to ground first based on view to shelf
    1702            0 :             ShowContinueError(
    1703              :                 state,
    1704              :                 "Since the light shelf is below the window to which it is associated, the view factor of the window to the ground was reduced");
    1705            0 :             ShowContinueError(
    1706              :                 state, "and possibly also the view factor to the sky. Check you input and/or consider turning off autosizing of the view factors.");
    1707            0 :             leftoverViewFactor = 1.0 - viewFactorToShelf - viewFactorToSky;
    1708            0 :             if (leftoverViewFactor >= 0.0) {
    1709            0 :                 viewFactorToGround = leftoverViewFactor; // Other view factors okay
    1710              :             } else {
    1711            0 :                 viewFactorToGround = 0.0;
    1712            0 :                 viewFactorToSky = 1.0 - viewFactorToShelf;
    1713            0 :                 if (viewFactorToSky < 0.0) {
    1714            0 :                     viewFactorToSky = 0.0;
    1715            0 :                     viewFactorToShelf = 1.0;
    1716              :                 }
    1717              :             }
    1718              : 
    1719            0 :         } else if (zShelfMin >= zWinMax) { // Shelf is fully above window, reduce view to sky first based on view to shelf
    1720            0 :             ShowContinueError(
    1721              :                 state, "Since the light shelf is above the window to which it is associated, the view factor of the window to the sky was reduced");
    1722            0 :             ShowContinueError(
    1723              :                 state,
    1724              :                 "and possibly also the view factor to the ground. Check you input and/or consider turning off autosizing of the view factors.");
    1725            0 :             leftoverViewFactor = 1.0 - viewFactorToShelf - viewFactorToGround;
    1726            0 :             if (leftoverViewFactor >= 0.0) {
    1727            0 :                 viewFactorToSky = leftoverViewFactor;
    1728              :             } else {
    1729            0 :                 viewFactorToSky = 0.0;
    1730            0 :                 viewFactorToGround = 1.0 - viewFactorToShelf;
    1731            0 :                 if (viewFactorToGround < 0.0) {
    1732            0 :                     viewFactorToGround = 0.0;
    1733            0 :                     viewFactorToShelf = 1.0;
    1734              :                 }
    1735              :             }
    1736              :         } else { // At least part of the shelf is somewhere in the middle of the window so we need to split out the view factors
    1737            0 :             ShowContinueError(
    1738              :                 state,
    1739              :                 "Since the light shelf is neither fully above or fully below the window to which it is associated, the view factor of the window");
    1740            0 :             ShowContinueError(
    1741              :                 state,
    1742              :                 "to the ground and sky were both potentially reduced. Check you input and/or consider turning off autosizing of the view factors.");
    1743              :             Real64 zShelfAvg;
    1744            0 :             if (((zShelfMin >= zWinMin) && (zShelfMax <= zWinMax)) || // Shelf does not go above or below the window
    1745            0 :                 ((zShelfMin < zWinMin) && (zShelfMax > zWinMax))) {   // Shelf goes both above AND below the window
    1746            0 :                 zShelfAvg = 0.5 * (zShelfMin + zShelfMax);
    1747            0 :             } else if (zShelfMin < zWinMin) { // Shelf goes partially below the window only
    1748            0 :                 Real64 fracAbove = 0.0;
    1749            0 :                 if (zShelfMax > zShelfMin) {
    1750            0 :                     fracAbove = (zShelfMax - zWinMin) / (zShelfMax - zShelfMin);
    1751            0 :                     if (fracAbove > 1.0) {
    1752            0 :                         fracAbove = 1.0;
    1753              :                     }
    1754              :                 }
    1755            0 :                 zShelfAvg = zWinMin + fracAbove * (zShelfMax - zWinMin);
    1756              :             } else { // (zShelfMax > zWinMax): Shelf goes partially above window
    1757            0 :                 Real64 fracBelow = 0.0;
    1758            0 :                 if (zShelfMax > zShelfMin) {
    1759            0 :                     fracBelow = (zWinMax - zShelfMin) / (zShelfMax - zShelfMin);
    1760              :                 }
    1761            0 :                 zShelfAvg = zWinMax - fracBelow * (zWinMax - zShelfMin);
    1762              :             }
    1763              : 
    1764              :             // Find height ratio based on shelf average height
    1765              :             Real64 heightRatio;
    1766            0 :             if (zWinMax > zWinMin) { // Window has a positive height
    1767            0 :                 heightRatio = (zShelfAvg - zWinMin) / (zWinMax - zWinMin);
    1768            0 :                 heightRatio = min(heightRatio, 1.0);
    1769            0 :                 heightRatio = max(heightRatio, 0.0);
    1770              :             } else { // Window does not have a positive height (not realistic) so set height ratio based on shelf location
    1771            0 :                 if (zShelfAvg > zWinMax) {
    1772            0 :                     heightRatio = 1.0;
    1773              :                 } else {
    1774            0 :                     heightRatio = 0.0;
    1775              :                 }
    1776              :             }
    1777              : 
    1778              :             // Take what is left over after the view to shelf is subtracted and then distribute/adjust that proportionally
    1779              :             // for the views to ground and sky based on their original weights.  Finally, account for the location of the shelf
    1780              :             // with respect to the shelf and reset the values of the actual variables used in the rest of the simulation.
    1781            0 :             leftoverViewFactor = 1.0 - viewFactorToShelf; // By previous logic above, leftover is greater than zero and less than one
    1782              :             Real64 vfGroundAdjustMax;
    1783              :             Real64 vfGroundAdjustMin;
    1784            0 :             if (viewFactorToGround > viewFactorToShelf) { // How much view to ground could be reduced potentially if shelf at bottom
    1785            0 :                 vfGroundAdjustMin = viewFactorToGround - viewFactorToShelf;
    1786              :             } else {
    1787            0 :                 vfGroundAdjustMin = 0.0;
    1788              :             }
    1789            0 :             if (viewFactorToGround > leftoverViewFactor) { // How much view to ground could be reduced potentially if shelf at top
    1790            0 :                 vfGroundAdjustMax = leftoverViewFactor;
    1791              :             } else {
    1792            0 :                 vfGroundAdjustMax = viewFactorToGround;
    1793              :             }
    1794            0 :             viewFactorToGround = vfGroundAdjustMin + heightRatio * (vfGroundAdjustMax - vfGroundAdjustMin);
    1795            0 :             viewFactorToSky = leftoverViewFactor - viewFactorToGround;
    1796              :         }
    1797            0 :         ShowWarningError(state,
    1798            0 :                          format("DaylightingDevice:Shelf = {}:  As a result of user input (see previous messages), at least one view factor but "
    1799              :                                 "possibly more than one was reduced.",
    1800            0 :                                 state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
    1801            0 :         ShowContinueError(state,
    1802              :                           "These include the view factors to the ground, the sky, and the exterior light shelf.  Note that views to other exterior "
    1803              :                           "surfaces could further complicated this.");
    1804            0 :         ShowContinueError(state, "Please consider manually calculating or adjusting view factors to avoid this problem.");
    1805              :     }
    1806              : 
    1807      2828408 :     void FigureTDDZoneGains(EnergyPlusData &state)
    1808              :     {
    1809              : 
    1810              :         // SUBROUTINE INFORMATION:
    1811              :         //       AUTHOR         B. Griffith
    1812              :         //       DATE WRITTEN   Dec 2011
    1813              : 
    1814              :         // PURPOSE OF THIS SUBROUTINE:
    1815              :         // initialize zone gains at begin new environment
    1816              : 
    1817              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1818              : 
    1819      2828408 :         if ((int)state.dataDaylightingDevicesData->TDDPipe.size() == 0) {
    1820      2826383 :             return;
    1821              :         }
    1822              : 
    1823         2025 :         if (state.dataGlobal->BeginEnvrnFlag && state.dataDaylightingDevices->MyEnvrnFlag) {
    1824           15 :             for (int Loop = 1; Loop <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++Loop) {
    1825           10 :                 state.dataDaylightingDevicesData->TDDPipe(Loop).TZoneHeatGain = 0.0;
    1826              :             }
    1827            5 :             state.dataDaylightingDevices->MyEnvrnFlag = false;
    1828              :         }
    1829         2025 :         if (!state.dataGlobal->BeginEnvrnFlag) {
    1830         2020 :             state.dataDaylightingDevices->MyEnvrnFlag = true;
    1831              :         }
    1832              :     }
    1833              : 
    1834              : } // namespace Dayltg
    1835              : 
    1836              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1