LCOV - code coverage report
Current view: top level - EnergyPlus - WeatherManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 58.1 % 4633 2691
Test Date: 2025-06-02 12:03:30 Functions: 90.4 % 83 75

            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 <array>
      50              : #include <cmath>
      51              : #include <cstdio>
      52              : #include <map>
      53              : 
      54              : // ObjexxFCL Headers
      55              : #include <ObjexxFCL/Array.functions.hh>
      56              : #include <ObjexxFCL/ArrayS.functions.hh>
      57              : #include <ObjexxFCL/string.functions.hh>
      58              : #include <ObjexxFCL/time.hh>
      59              : 
      60              : // EnergyPlus Headers
      61              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      62              : #include <EnergyPlus/DataEnvironment.hh>
      63              : #include <EnergyPlus/DataHeatBalance.hh>
      64              : #include <EnergyPlus/DataIPShortCuts.hh>
      65              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      66              : #include <EnergyPlus/DataReportingFlags.hh>
      67              : #include <EnergyPlus/DataSurfaces.hh>
      68              : #include <EnergyPlus/DataSystemVariables.hh>
      69              : #include <EnergyPlus/DataWater.hh>
      70              : #include <EnergyPlus/DisplayRoutines.hh>
      71              : #include <EnergyPlus/EMSManager.hh>
      72              : #include <EnergyPlus/FileSystem.hh>
      73              : #include <EnergyPlus/General.hh>
      74              : #include <EnergyPlus/GlobalNames.hh>
      75              : #include <EnergyPlus/GroundTemperatureModeling/BaseGroundTemperatureModel.hh>
      76              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      77              : #include <EnergyPlus/OutputProcessor.hh>
      78              : #include <EnergyPlus/OutputReportPredefined.hh>
      79              : #include <EnergyPlus/OutputReportTabular.hh>
      80              : #include <EnergyPlus/Psychrometrics.hh>
      81              : #include <EnergyPlus/ScheduleManager.hh>
      82              : #include <EnergyPlus/StringUtilities.hh>
      83              : #include <EnergyPlus/SurfaceGeometry.hh>
      84              : #include <EnergyPlus/ThermalComfort.hh>
      85              : #include <EnergyPlus/UtilityRoutines.hh>
      86              : #include <EnergyPlus/Vectors.hh>
      87              : #include <EnergyPlus/WaterManager.hh>
      88              : #include <EnergyPlus/WeatherManager.hh>
      89              : 
      90              : namespace EnergyPlus {
      91              : 
      92              : namespace Weather {
      93              : 
      94              :     // MODULE INFORMATION:
      95              :     //       AUTHOR         Rick Strand
      96              :     //       DATE WRITTEN   May 1997
      97              :     //       MODIFIED       December 1998, FW; December 1999, LKL.
      98              : 
      99              :     // PURPOSE OF THIS MODULE:
     100              :     // This module contains all of the weather handling routines for
     101              :     // EnergyPlus.  That includes getting user input, defining design day
     102              :     // weather, retrieving data from weather files, and supplying the
     103              :     // outdoor environment for each time step.
     104              : 
     105              :     constexpr std::array<std::string_view, (int)EpwHeaderType::Num> epwHeaders = {"LOCATION",
     106              :                                                                                   "DESIGN CONDITIONS",
     107              :                                                                                   "TYPICAL/EXTREME PERIODS",
     108              :                                                                                   "GROUND TEMPERATURES",
     109              :                                                                                   "HOLIDAYS/DAYLIGHT SAVING",
     110              :                                                                                   "COMMENTS 1",
     111              :                                                                                   "COMMENTS 2",
     112              :                                                                                   "DATA PERIODS"};
     113              : 
     114              :     static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNames{
     115              :         "Schedule", "Correlation", "CorrelationFromWeatherFile", "FixedDefault"};
     116              : 
     117              :     static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNamesUC{
     118              :         "SCHEDULE", "CORRELATION", "CORRELATIONFROMWEATHERFILE", "FIXEDDEFAULT"};
     119              : 
     120              :     static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNamesUC{
     121              :         "CLARKALLEN", "SCHEDULEVALUE", "DIFFERENCESCHEDULEDRYBULBVALUE", "DIFFERENCESCHEDULEDEWPOINTVALUE", "BRUNT", "IDSO", "BERDAHLMARTIN"};
     122              : 
     123              :     static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNames{"Clark and Allen",
     124              :                                                                                             "Schedule Value",
     125              :                                                                                             "DryBulb Difference Schedule Value",
     126              :                                                                                             "Dewpoint Difference Schedule Value",
     127              :                                                                                             "Brunt",
     128              :                                                                                             "Idso",
     129              :                                                                                             "Berdahl and Martin"};
     130              : 
     131       326115 :     void ManageWeather(EnergyPlusData &state)
     132              :     {
     133              : 
     134              :         // SUBROUTINE INFORMATION:
     135              :         //       AUTHOR         Rick Strand
     136              :         //       DATE WRITTEN   May 1997
     137              :         //       MODIFIED       June 1997 (general clean-up)
     138              : 
     139              :         // PURPOSE OF THIS SUBROUTINE:
     140              :         // This subroutine is the main driver of the weather manager module.
     141              :         // It controls the assignment of weather related global variables as
     142              :         // well as the reads and writes for weather information.
     143              : 
     144       326115 :         InitializeWeather(state, state.dataWeather->PrintEnvrnStamp);
     145              : 
     146              :         // Cannot call this during sizing, because EMS will not initialize properly until after simulation kickoff
     147       326115 :         if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
     148       255311 :             bool anyEMSRan = false;
     149       255311 :             EMSManager::ManageEMS(state,
     150              :                                   EMSManager::EMSCallFrom::BeginZoneTimestepBeforeSetCurrentWeather,
     151              :                                   anyEMSRan,
     152       510622 :                                   ObjexxFCL::Optional_int_const()); // calling point
     153              :         }
     154       326115 :         SetCurrentWeather(state);
     155              : 
     156       326115 :         ReportWeatherAndTimeInformation(state, state.dataWeather->PrintEnvrnStamp);
     157       326115 :     }
     158              : 
     159          292 :     void ResetEnvironmentCounter(EnergyPlusData &state)
     160              :     {
     161          292 :         state.dataWeather->Envrn = 0;
     162          292 :     }
     163              : 
     164           67 :     bool CheckIfAnyUnderwaterBoundaries(EnergyPlusData &state)
     165              :     {
     166           67 :         bool errorsFound = false;
     167           67 :         int NumAlpha = 0, NumNumber = 0, IOStat = 0;
     168              : 
     169           67 :         constexpr std::string_view routineName = "CheckIfAnyUnderwaterBoundaries";
     170              : 
     171           67 :         auto const &ipsc = state.dataIPShortCut;
     172           67 :         ipsc->cCurrentModuleObject = "SurfaceProperty:Underwater";
     173           67 :         int Num = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
     174           69 :         for (int i = 1; i <= Num; i++) {
     175            4 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     176            2 :                                                                      ipsc->cCurrentModuleObject,
     177              :                                                                      i,
     178            2 :                                                                      ipsc->cAlphaArgs,
     179              :                                                                      NumAlpha,
     180            2 :                                                                      ipsc->rNumericArgs,
     181              :                                                                      NumNumber,
     182              :                                                                      IOStat,
     183            2 :                                                                      ipsc->lNumericFieldBlanks,
     184            2 :                                                                      ipsc->lAlphaFieldBlanks,
     185            2 :                                                                      ipsc->cAlphaFieldNames,
     186            2 :                                                                      ipsc->cNumericFieldNames);
     187            2 :             state.dataWeather->underwaterBoundaries.emplace_back();
     188            2 :             auto &underwaterBoundary = state.dataWeather->underwaterBoundaries[i - 1];
     189            2 :             underwaterBoundary.Name = ipsc->cAlphaArgs(1);
     190              : 
     191            2 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, underwaterBoundary.Name};
     192              : 
     193            2 :             underwaterBoundary.distanceFromLeadingEdge = ipsc->rNumericArgs(1);
     194            2 :             underwaterBoundary.OSCMIndex = Util::FindItemInList(underwaterBoundary.Name, state.dataSurface->OSCM);
     195            2 :             if (underwaterBoundary.OSCMIndex <= 0) {
     196            0 :                 ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
     197            0 :                 errorsFound = true;
     198              :             }
     199              : 
     200            2 :             if (ipsc->lAlphaFieldBlanks(2)) {
     201            0 :                 ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
     202            0 :                 errorsFound = true;
     203            2 :             } else if ((underwaterBoundary.waterTempSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
     204            0 :                 ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
     205            0 :                 errorsFound = true;
     206              :             }
     207              : 
     208            2 :             if (ipsc->lAlphaFieldBlanks(3)) {
     209              :                 // that's OK, we can have a blank schedule, the water will just have no free stream velocity
     210            1 :             } else if ((underwaterBoundary.velocitySched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
     211            0 :                 ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
     212            0 :                 errorsFound = true;
     213              :             }
     214            2 :             if (errorsFound) {
     215            0 :                 break;
     216              :             }
     217              :         }
     218           67 :         if (errorsFound) {
     219            0 :             ShowFatalError(state, "Previous input problems cause program termination");
     220              :         }
     221           67 :         return (Num > 0);
     222              :     }
     223              : 
     224              :     Real64
     225            5 :     calculateWaterBoundaryConvectionCoefficient(Real64 const curWaterTemp, Real64 const freeStreamVelocity, Real64 const distanceFromLeadingEdge)
     226              :     {
     227            5 :         Real64 constexpr waterKinematicViscosity = 1e-6; // m2/s
     228            5 :         Real64 constexpr waterPrandtlNumber = 6;         // -
     229            5 :         Real64 constexpr waterThermalConductivity = 0.6; // W/mK
     230              :         // do some calculation for forced convection from the leading edge of the ship
     231            5 :         Real64 const localReynoldsNumber = freeStreamVelocity * distanceFromLeadingEdge / waterKinematicViscosity;
     232            5 :         Real64 const localNusseltNumber = 0.0296 * pow(localReynoldsNumber, 0.8) * pow(waterPrandtlNumber, 1.0 / 3.0);
     233            5 :         Real64 const localConvectionCoeff = localNusseltNumber * waterThermalConductivity / distanceFromLeadingEdge;
     234              : 
     235              :         // do some calculations for natural convection from the bottom of the ship
     236            5 :         Real64 constexpr distanceFromBottomOfHull = 12; // meters, assumed for now
     237              :                                                         // this Prandtl correction is from Incropera & Dewitt, Intro to HT, eq 9.20
     238            5 :         Real64 const prandtlCorrection =
     239              :             (0.75 * pow(waterPrandtlNumber, 0.5)) / pow(0.609 + 1.221 * pow(waterPrandtlNumber, 0.5) + 1.238 * waterPrandtlNumber, 0.25);
     240              :         // calculate the Grashof number
     241            5 :         Real64 constexpr gravity = 9.81;          // m/s2
     242            5 :         Real64 constexpr beta = 0.000214;         // water thermal expansion coefficient, from engineeringtoolbox.com, 1/C
     243            5 :         Real64 constexpr assumedSurfaceTemp = 25; // Grashof requires a surface temp, this should suffice
     244              :         Real64 const localGrashofNumber =
     245            5 :             (gravity * beta * std::abs(assumedSurfaceTemp - curWaterTemp) * pow(distanceFromBottomOfHull, 3)) / pow(waterKinematicViscosity, 2);
     246            5 :         Real64 const localNusseltFreeConvection = pow(localGrashofNumber / 4, 0.25) * prandtlCorrection;
     247            5 :         Real64 const localConvectionCoeffFreeConv = localNusseltFreeConvection * waterThermalConductivity / distanceFromBottomOfHull;
     248            5 :         return max(localConvectionCoeff, localConvectionCoeffFreeConv);
     249              :     }
     250              : 
     251            0 :     void UpdateUnderwaterBoundaries(EnergyPlusData &state)
     252              :     {
     253            0 :         for (auto &thisBoundary : state.dataWeather->underwaterBoundaries) {
     254            0 :             Real64 const curWaterTemp = thisBoundary.waterTempSched->getCurrentVal(); // C
     255            0 :             Real64 freeStreamVelocity = 0;
     256            0 :             if (thisBoundary.velocitySched != nullptr) {
     257            0 :                 freeStreamVelocity = thisBoundary.velocitySched->getCurrentVal(); // m/s
     258              :             }
     259            0 :             state.dataSurface->OSCM(thisBoundary.OSCMIndex).TConv = curWaterTemp;
     260            0 :             state.dataSurface->OSCM(thisBoundary.OSCMIndex).HConv =
     261            0 :                 Weather::calculateWaterBoundaryConvectionCoefficient(curWaterTemp, freeStreamVelocity, thisBoundary.distanceFromLeadingEdge);
     262            0 :             state.dataSurface->OSCM(thisBoundary.OSCMIndex).TRad = curWaterTemp;
     263            0 :             state.dataSurface->OSCM(thisBoundary.OSCMIndex).HRad = 0.0;
     264            0 :         }
     265            0 :     }
     266              : 
     267           73 :     void ReadVariableLocationOrientation(EnergyPlusData &state)
     268              :     {
     269              :         static constexpr std::string_view routineName = "ReadVariableLocationOrientation";
     270              : 
     271           73 :         int NumAlpha = 0, NumNumber = 0, IOStat = 0;
     272           73 :         auto const &ipsc = state.dataIPShortCut;
     273              : 
     274           73 :         ipsc->cCurrentModuleObject = "Site:VariableLocation";
     275           73 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject) == 0) {
     276           73 :             return;
     277              :         }
     278            0 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     279            0 :                                                                  ipsc->cCurrentModuleObject,
     280              :                                                                  1,
     281            0 :                                                                  ipsc->cAlphaArgs,
     282              :                                                                  NumAlpha,
     283            0 :                                                                  ipsc->rNumericArgs,
     284              :                                                                  NumNumber,
     285              :                                                                  IOStat,
     286            0 :                                                                  ipsc->lNumericFieldBlanks,
     287            0 :                                                                  ipsc->lAlphaFieldBlanks,
     288            0 :                                                                  ipsc->cAlphaFieldNames,
     289            0 :                                                                  ipsc->cNumericFieldNames);
     290              : 
     291            0 :         ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
     292              : 
     293            0 :         if (ipsc->lAlphaFieldBlanks(1)) {
     294            0 :         } else if ((state.dataEnvrn->varyingLocationLatSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(1))) == nullptr) {
     295            0 :             ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
     296              :         }
     297              : 
     298            0 :         if (ipsc->lAlphaFieldBlanks(2)) {
     299            0 :         } else if ((state.dataEnvrn->varyingLocationLongSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
     300            0 :             ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
     301              :         }
     302              : 
     303            0 :         if (ipsc->lAlphaFieldBlanks(3)) {
     304            0 :         } else if ((state.dataEnvrn->varyingOrientationSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
     305            0 :             ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
     306              :         }
     307              :     }
     308              : 
     309            0 :     void UpdateLocationAndOrientation(EnergyPlusData &state)
     310              :     {
     311            0 :         if (state.dataEnvrn->varyingLocationLatSched != nullptr) {
     312            0 :             state.dataEnvrn->Latitude = state.dataEnvrn->varyingLocationLatSched->getCurrentVal();
     313              :         }
     314            0 :         if (state.dataEnvrn->varyingLocationLongSched != nullptr) {
     315            0 :             state.dataEnvrn->Longitude = state.dataEnvrn->varyingLocationLongSched->getCurrentVal();
     316              :         }
     317              : 
     318            0 :         CheckLocationValidity(state);
     319            0 :         if (state.dataEnvrn->varyingOrientationSched != nullptr) {
     320            0 :             state.dataHeatBal->BuildingAzimuth = mod(state.dataEnvrn->varyingOrientationSched->getCurrentVal(), 360.0);
     321            0 :             state.dataSurfaceGeometry->CosBldgRelNorth =
     322            0 :                 std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
     323            0 :             state.dataSurfaceGeometry->SinBldgRelNorth =
     324            0 :                 std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
     325            0 :             for (size_t SurfNum = 1; SurfNum < state.dataSurface->Surface.size(); ++SurfNum) {
     326            0 :                 auto &surf = state.dataSurface->Surface(SurfNum);
     327            0 :                 for (int n = 1; n <= surf.Sides; ++n) {
     328            0 :                     Real64 Xb = surf.Vertex(n).x;
     329            0 :                     Real64 Yb = surf.Vertex(n).y;
     330            0 :                     surf.NewVertex(n).x = Xb * state.dataSurfaceGeometry->CosBldgRelNorth - Yb * state.dataSurfaceGeometry->SinBldgRelNorth;
     331            0 :                     surf.NewVertex(n).y = Xb * state.dataSurfaceGeometry->SinBldgRelNorth + Yb * state.dataSurfaceGeometry->CosBldgRelNorth;
     332            0 :                     surf.NewVertex(n).z = surf.Vertex(n).z;
     333              :                 }
     334            0 :                 Vectors::CreateNewellSurfaceNormalVector(surf.NewVertex, surf.Sides, surf.NewellSurfaceNormalVector);
     335            0 :                 Real64 SurfWorldAz = 0.0;
     336            0 :                 Real64 SurfTilt = 0.0;
     337            0 :                 Vectors::DetermineAzimuthAndTilt(
     338            0 :                     surf.NewVertex, SurfWorldAz, SurfTilt, surf.lcsx, surf.lcsy, surf.lcsz, surf.NewellSurfaceNormalVector);
     339            0 :                 surf.Azimuth = SurfWorldAz;
     340            0 :                 surf.SinAzim = std::sin(SurfWorldAz * Constant::DegToRad);
     341            0 :                 surf.CosAzim = std::cos(SurfWorldAz * Constant::DegToRad);
     342            0 :                 surf.OutNormVec = surf.NewellSurfaceNormalVector;
     343              :             }
     344              :         }
     345            0 :     }
     346              : 
     347          851 :     bool GetNextEnvironment(EnergyPlusData &state, bool &Available, bool &ErrorsFound)
     348              :     {
     349              : 
     350              :         // SUBROUTINE INFORMATION:
     351              :         //       AUTHOR         Linda Lawrie
     352              :         //       DATE WRITTEN   August 2000
     353              : 
     354              :         // PURPOSE OF THIS SUBROUTINE:
     355              :         // This subroutine is called from the outer simulation manager and determines
     356              :         // if another environment is available in the "run list" or if the end has been
     357              :         // reached.
     358              : 
     359              :         static constexpr std::string_view RoutineName("GetNextEnvironment: ");
     360              :         static constexpr std::string_view EnvNameFormat("Environment,{},{},{},{},{},{},{},{},{},{},{},{},{}\n");
     361              :         static constexpr std::string_view EnvDSTNFormat("Environment:Daylight Saving,No,{}\n");
     362              :         static constexpr std::string_view EnvDSTYFormat("Environment:Daylight Saving,Yes,{},{},{}\n");
     363              :         static constexpr std::string_view DateFormat("{:02}/{:02}");
     364              :         static constexpr std::string_view DateFormatWithYear("{:02}/{:02}/{:04}");
     365              : 
     366          851 :         if (state.dataGlobal->BeginSimFlag && state.dataWeather->GetEnvironmentFirstCall) {
     367              : 
     368          107 :             state.dataReportFlag->PrintEndDataDictionary = true;
     369              : 
     370          107 :             ReportOutputFileHeaders(state); // Write the output file header information
     371              : 
     372              :             // Setup Output Variables, CurrentModuleObject='All Simulations'
     373              : 
     374          428 :             SetupOutputVariable(state,
     375              :                                 "Site Outdoor Air Drybulb Temperature",
     376              :                                 Constant::Units::C,
     377          107 :                                 state.dataEnvrn->OutDryBulbTemp,
     378              :                                 OutputProcessor::TimeStepType::Zone,
     379              :                                 OutputProcessor::StoreType::Average,
     380              :                                 "Environment");
     381          428 :             SetupOutputVariable(state,
     382              :                                 "Site Outdoor Air Dewpoint Temperature",
     383              :                                 Constant::Units::C,
     384          107 :                                 state.dataEnvrn->OutDewPointTemp,
     385              :                                 OutputProcessor::TimeStepType::Zone,
     386              :                                 OutputProcessor::StoreType::Average,
     387              :                                 "Environment");
     388          428 :             SetupOutputVariable(state,
     389              :                                 "Site Outdoor Air Wetbulb Temperature",
     390              :                                 Constant::Units::C,
     391          107 :                                 state.dataEnvrn->OutWetBulbTemp,
     392              :                                 OutputProcessor::TimeStepType::Zone,
     393              :                                 OutputProcessor::StoreType::Average,
     394              :                                 "Environment");
     395          428 :             SetupOutputVariable(state,
     396              :                                 "Site Outdoor Air Humidity Ratio",
     397              :                                 Constant::Units::kgWater_kgDryAir,
     398          107 :                                 state.dataEnvrn->OutHumRat,
     399              :                                 OutputProcessor::TimeStepType::Zone,
     400              :                                 OutputProcessor::StoreType::Average,
     401              :                                 "Environment");
     402          428 :             SetupOutputVariable(state,
     403              :                                 "Site Outdoor Air Relative Humidity",
     404              :                                 Constant::Units::Perc,
     405          107 :                                 state.dataEnvrn->OutRelHum,
     406              :                                 OutputProcessor::TimeStepType::Zone,
     407              :                                 OutputProcessor::StoreType::Average,
     408              :                                 "Environment");
     409          428 :             SetupOutputVariable(state,
     410              :                                 "Site Outdoor Air Barometric Pressure",
     411              :                                 Constant::Units::Pa,
     412          107 :                                 state.dataEnvrn->OutBaroPress,
     413              :                                 OutputProcessor::TimeStepType::Zone,
     414              :                                 OutputProcessor::StoreType::Average,
     415              :                                 "Environment");
     416          428 :             SetupOutputVariable(state,
     417              :                                 "Site Wind Speed",
     418              :                                 Constant::Units::m_s,
     419          107 :                                 state.dataEnvrn->WindSpeed,
     420              :                                 OutputProcessor::TimeStepType::Zone,
     421              :                                 OutputProcessor::StoreType::Average,
     422              :                                 "Environment");
     423          428 :             SetupOutputVariable(state,
     424              :                                 "Site Wind Direction",
     425              :                                 Constant::Units::deg,
     426          107 :                                 state.dataEnvrn->WindDir,
     427              :                                 OutputProcessor::TimeStepType::Zone,
     428              :                                 OutputProcessor::StoreType::Average,
     429              :                                 "Environment");
     430          428 :             SetupOutputVariable(state,
     431              :                                 "Site Sky Temperature",
     432              :                                 Constant::Units::C,
     433          107 :                                 state.dataEnvrn->SkyTemp,
     434              :                                 OutputProcessor::TimeStepType::Zone,
     435              :                                 OutputProcessor::StoreType::Average,
     436              :                                 "Environment");
     437          428 :             SetupOutputVariable(state,
     438              :                                 "Site Horizontal Infrared Radiation Rate per Area",
     439              :                                 Constant::Units::W_m2,
     440          107 :                                 state.dataWeather->HorizIRSky,
     441              :                                 OutputProcessor::TimeStepType::Zone,
     442              :                                 OutputProcessor::StoreType::Average,
     443              :                                 "Environment");
     444          428 :             SetupOutputVariable(state,
     445              :                                 "Site Diffuse Solar Radiation Rate per Area",
     446              :                                 Constant::Units::W_m2,
     447          107 :                                 state.dataEnvrn->DifSolarRad,
     448              :                                 OutputProcessor::TimeStepType::Zone,
     449              :                                 OutputProcessor::StoreType::Average,
     450              :                                 "Environment");
     451          428 :             SetupOutputVariable(state,
     452              :                                 "Site Direct Solar Radiation Rate per Area",
     453              :                                 Constant::Units::W_m2,
     454          107 :                                 state.dataEnvrn->BeamSolarRad,
     455              :                                 OutputProcessor::TimeStepType::Zone,
     456              :                                 OutputProcessor::StoreType::Average,
     457              :                                 "Environment");
     458          428 :             SetupOutputVariable(state,
     459              :                                 "Liquid Precipitation Depth",
     460              :                                 Constant::Units::m,
     461          107 :                                 state.dataEnvrn->LiquidPrecipitation,
     462              :                                 OutputProcessor::TimeStepType::Zone,
     463              :                                 OutputProcessor::StoreType::Sum,
     464              :                                 "Environment");
     465          428 :             SetupOutputVariable(state,
     466              :                                 "Site Precipitation Rate",
     467              :                                 Constant::Units::m_s,
     468          107 :                                 state.dataWaterData->RainFall.CurrentRate,
     469              :                                 OutputProcessor::TimeStepType::Zone,
     470              :                                 OutputProcessor::StoreType::Average,
     471              :                                 "Environment");
     472          428 :             SetupOutputVariable(state,
     473              :                                 "Site Precipitation Depth",
     474              :                                 Constant::Units::m,
     475          107 :                                 state.dataWaterData->RainFall.CurrentAmount,
     476              :                                 OutputProcessor::TimeStepType::Zone,
     477              :                                 OutputProcessor::StoreType::Sum,
     478              :                                 "Environment");
     479          428 :             SetupOutputVariable(state,
     480              :                                 "Site Ground Reflected Solar Radiation Rate per Area",
     481              :                                 Constant::Units::W_m2,
     482          107 :                                 state.dataEnvrn->GndSolarRad,
     483              :                                 OutputProcessor::TimeStepType::Zone,
     484              :                                 OutputProcessor::StoreType::Average,
     485              :                                 "Environment");
     486          428 :             SetupOutputVariable(state,
     487              :                                 "Site Ground Temperature",
     488              :                                 Constant::Units::C,
     489          107 :                                 state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface],
     490              :                                 OutputProcessor::TimeStepType::Zone,
     491              :                                 OutputProcessor::StoreType::Average,
     492              :                                 "Environment");
     493          428 :             SetupOutputVariable(state,
     494              :                                 "Site Surface Ground Temperature",
     495              :                                 Constant::Units::C,
     496          107 :                                 state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow],
     497              :                                 OutputProcessor::TimeStepType::Zone,
     498              :                                 OutputProcessor::StoreType::Average,
     499              :                                 "Environment");
     500          428 :             SetupOutputVariable(state,
     501              :                                 "Site Deep Ground Temperature",
     502              :                                 Constant::Units::C,
     503          107 :                                 state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep],
     504              :                                 OutputProcessor::TimeStepType::Zone,
     505              :                                 OutputProcessor::StoreType::Average,
     506              :                                 "Environment");
     507          428 :             SetupOutputVariable(state,
     508              :                                 "Site Simple Factor Model Ground Temperature",
     509              :                                 Constant::Units::C,
     510          107 :                                 state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod],
     511              :                                 OutputProcessor::TimeStepType::Zone,
     512              :                                 OutputProcessor::StoreType::Average,
     513              :                                 "Environment");
     514          428 :             SetupOutputVariable(state,
     515              :                                 "Site Total Sky Cover",
     516              :                                 Constant::Units::None,
     517          107 :                                 state.dataEnvrn->TotalCloudCover,
     518              :                                 OutputProcessor::TimeStepType::Zone,
     519              :                                 OutputProcessor::StoreType::Average,
     520              :                                 "Environment");
     521          428 :             SetupOutputVariable(state,
     522              :                                 "Site Opaque Sky Cover",
     523              :                                 Constant::Units::None,
     524          107 :                                 state.dataEnvrn->OpaqueCloudCover,
     525              :                                 OutputProcessor::TimeStepType::Zone,
     526              :                                 OutputProcessor::StoreType::Average,
     527              :                                 "Environment");
     528          428 :             SetupOutputVariable(state,
     529              :                                 "Site Outdoor Air Enthalpy",
     530              :                                 Constant::Units::J_kg,
     531          107 :                                 state.dataEnvrn->OutEnthalpy,
     532              :                                 OutputProcessor::TimeStepType::Zone,
     533              :                                 OutputProcessor::StoreType::Average,
     534              :                                 "Environment");
     535          428 :             SetupOutputVariable(state,
     536              :                                 "Site Outdoor Air Density",
     537              :                                 Constant::Units::kg_m3,
     538          107 :                                 state.dataEnvrn->OutAirDensity,
     539              :                                 OutputProcessor::TimeStepType::Zone,
     540              :                                 OutputProcessor::StoreType::Average,
     541              :                                 "Environment");
     542          428 :             SetupOutputVariable(state,
     543              :                                 "Site Solar Azimuth Angle",
     544              :                                 Constant::Units::deg,
     545          107 :                                 state.dataWeather->SolarAzimuthAngle,
     546              :                                 OutputProcessor::TimeStepType::Zone,
     547              :                                 OutputProcessor::StoreType::Average,
     548              :                                 "Environment");
     549          428 :             SetupOutputVariable(state,
     550              :                                 "Site Solar Altitude Angle",
     551              :                                 Constant::Units::deg,
     552          107 :                                 state.dataWeather->SolarAltitudeAngle,
     553              :                                 OutputProcessor::TimeStepType::Zone,
     554              :                                 OutputProcessor::StoreType::Average,
     555              :                                 "Environment");
     556          428 :             SetupOutputVariable(state,
     557              :                                 "Site Solar Hour Angle",
     558              :                                 Constant::Units::deg,
     559          107 :                                 state.dataWeather->HrAngle,
     560              :                                 OutputProcessor::TimeStepType::Zone,
     561              :                                 OutputProcessor::StoreType::Average,
     562              :                                 "Environment");
     563          321 :             SetupOutputVariable(state,
     564              :                                 "Site Rain Status",
     565              :                                 Constant::Units::None,
     566          107 :                                 state.dataWeather->RptIsRain,
     567              :                                 OutputProcessor::TimeStepType::Zone,
     568              :                                 OutputProcessor::StoreType::Average,
     569              :                                 "Environment");
     570          214 :             SetupOutputVariable(state,
     571              :                                 "Site Snow on Ground Status",
     572              :                                 Constant::Units::None,
     573          107 :                                 state.dataWeather->RptIsSnow,
     574              :                                 OutputProcessor::TimeStepType::Zone,
     575              :                                 OutputProcessor::StoreType::Average,
     576              :                                 "Environment");
     577          428 :             SetupOutputVariable(state,
     578              :                                 "Site Exterior Horizontal Sky Illuminance",
     579              :                                 Constant::Units::lux,
     580          107 :                                 state.dataEnvrn->HISKF,
     581              :                                 OutputProcessor::TimeStepType::Zone,
     582              :                                 OutputProcessor::StoreType::Average,
     583              :                                 "Environment");
     584          428 :             SetupOutputVariable(state,
     585              :                                 "Site Exterior Horizontal Beam Illuminance",
     586              :                                 Constant::Units::lux,
     587          107 :                                 state.dataEnvrn->HISUNF,
     588              :                                 OutputProcessor::TimeStepType::Zone,
     589              :                                 OutputProcessor::StoreType::Average,
     590              :                                 "Environment");
     591          428 :             SetupOutputVariable(state,
     592              :                                 "Site Exterior Beam Normal Illuminance",
     593              :                                 Constant::Units::lux,
     594          107 :                                 state.dataEnvrn->HISUNFnorm,
     595              :                                 OutputProcessor::TimeStepType::Zone,
     596              :                                 OutputProcessor::StoreType::Average,
     597              :                                 "Environment");
     598          428 :             SetupOutputVariable(state,
     599              :                                 "Site Sky Diffuse Solar Radiation Luminous Efficacy",
     600              :                                 Constant::Units::lum_W,
     601          107 :                                 state.dataEnvrn->PDIFLW,
     602              :                                 OutputProcessor::TimeStepType::Zone,
     603              :                                 OutputProcessor::StoreType::Average,
     604              :                                 "Environment");
     605          428 :             SetupOutputVariable(state,
     606              :                                 "Site Beam Solar Radiation Luminous Efficacy",
     607              :                                 Constant::Units::lum_W,
     608          107 :                                 state.dataEnvrn->PDIRLW,
     609              :                                 OutputProcessor::TimeStepType::Zone,
     610              :                                 OutputProcessor::StoreType::Average,
     611              :                                 "Environment");
     612          428 :             SetupOutputVariable(state,
     613              :                                 "Site Daylighting Model Sky Clearness",
     614              :                                 Constant::Units::None,
     615          107 :                                 state.dataEnvrn->SkyClearness,
     616              :                                 OutputProcessor::TimeStepType::Zone,
     617              :                                 OutputProcessor::StoreType::Average,
     618              :                                 "Environment");
     619          428 :             SetupOutputVariable(state,
     620              :                                 "Site Daylighting Model Sky Brightness",
     621              :                                 Constant::Units::None,
     622          107 :                                 state.dataEnvrn->SkyBrightness,
     623              :                                 OutputProcessor::TimeStepType::Zone,
     624              :                                 OutputProcessor::StoreType::Average,
     625              :                                 "Environment");
     626          321 :             SetupOutputVariable(state,
     627              :                                 "Site Daylight Saving Time Status",
     628              :                                 Constant::Units::None,
     629          107 :                                 state.dataEnvrn->DSTIndicator,
     630              :                                 OutputProcessor::TimeStepType::Zone,
     631              :                                 OutputProcessor::StoreType::Average,
     632              :                                 "Environment");
     633          214 :             SetupOutputVariable(state,
     634              :                                 "Site Day Type Index",
     635              :                                 Constant::Units::None,
     636          107 :                                 state.dataWeather->RptDayType,
     637              :                                 OutputProcessor::TimeStepType::Zone,
     638              :                                 OutputProcessor::StoreType::Average,
     639              :                                 "Environment");
     640          428 :             SetupOutputVariable(state,
     641              :                                 "Site Mains Water Temperature",
     642              :                                 Constant::Units::C,
     643          107 :                                 state.dataEnvrn->WaterMainsTemp,
     644              :                                 OutputProcessor::TimeStepType::Zone,
     645              :                                 OutputProcessor::StoreType::Average,
     646              :                                 "Environment");
     647              : 
     648          107 :             if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
     649           27 :                 SetupEMSActuator(state,
     650              :                                  "Weather Data",
     651              :                                  "Environment",
     652              :                                  "Outdoor Dry Bulb",
     653              :                                  "[C]",
     654           27 :                                  state.dataEnvrn->EMSOutDryBulbOverrideOn,
     655           27 :                                  state.dataEnvrn->EMSOutDryBulbOverrideValue);
     656           27 :                 SetupEMSActuator(state,
     657              :                                  "Weather Data",
     658              :                                  "Environment",
     659              :                                  "Outdoor Dew Point",
     660              :                                  "[C]",
     661           27 :                                  state.dataEnvrn->EMSOutDewPointTempOverrideOn,
     662           27 :                                  state.dataEnvrn->EMSOutDewPointTempOverrideValue);
     663           27 :                 SetupEMSActuator(state,
     664              :                                  "Weather Data",
     665              :                                  "Environment",
     666              :                                  "Outdoor Relative Humidity",
     667              :                                  "[%]",
     668           27 :                                  state.dataEnvrn->EMSOutRelHumOverrideOn,
     669           27 :                                  state.dataEnvrn->EMSOutRelHumOverrideValue);
     670           27 :                 SetupEMSActuator(state,
     671              :                                  "Weather Data",
     672              :                                  "Environment",
     673              :                                  "Diffuse Solar",
     674              :                                  "[W/m2]",
     675           27 :                                  state.dataEnvrn->EMSDifSolarRadOverrideOn,
     676           27 :                                  state.dataEnvrn->EMSDifSolarRadOverrideValue);
     677           27 :                 SetupEMSActuator(state,
     678              :                                  "Weather Data",
     679              :                                  "Environment",
     680              :                                  "Direct Solar",
     681              :                                  "[W/m2]",
     682           27 :                                  state.dataEnvrn->EMSBeamSolarRadOverrideOn,
     683           27 :                                  state.dataEnvrn->EMSBeamSolarRadOverrideValue);
     684           27 :                 SetupEMSActuator(state,
     685              :                                  "Weather Data",
     686              :                                  "Environment",
     687              :                                  "Wind Speed",
     688              :                                  "[m/s]",
     689           27 :                                  state.dataEnvrn->EMSWindSpeedOverrideOn,
     690           27 :                                  state.dataEnvrn->EMSWindSpeedOverrideValue);
     691           27 :                 SetupEMSActuator(state,
     692              :                                  "Weather Data",
     693              :                                  "Environment",
     694              :                                  "Wind Direction",
     695              :                                  "[deg]",
     696           27 :                                  state.dataEnvrn->EMSWindDirOverrideOn,
     697           27 :                                  state.dataEnvrn->EMSWindDirOverrideValue);
     698              :             }
     699          107 :             state.dataWeather->GetEnvironmentFirstCall = false;
     700              : 
     701              :         } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
     702              : 
     703          851 :         if (state.dataWeather->GetBranchInputOneTimeFlag) {
     704              : 
     705          111 :             SetupInterpolationValues(state);
     706          111 :             state.dataWeather->TimeStepFraction = 1.0 / double(state.dataGlobal->TimeStepsInHour);
     707          111 :             state.dataEnvrn->rhoAirSTP = Psychrometrics::PsyRhoAirFnPbTdbW(
     708              :                 state, DataEnvironment::StdPressureSeaLevel, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
     709          111 :             OpenWeatherFile(state, ErrorsFound); // moved here because of possibility of special days on EPW file
     710          111 :             CloseWeatherFile(state);
     711          111 :             ReadUserWeatherInput(state);
     712          111 :             AllocateWeatherData(state);
     713          111 :             if (state.dataWeather->NumIntervalsPerHour != 1) {
     714            0 :                 if (state.dataWeather->NumIntervalsPerHour != state.dataGlobal->TimeStepsInHour) {
     715            0 :                     ShowSevereError(
     716              :                         state,
     717            0 :                         format("{}Number of intervals per hour on Weather file does not match specified number of Time Steps Per Hour", RoutineName));
     718            0 :                     ErrorsFound = true;
     719              :                 }
     720              :             }
     721          111 :             state.dataWeather->GetBranchInputOneTimeFlag = false;
     722          111 :             state.dataWeather->Envrn = 0;
     723          111 :             if (state.dataWeather->NumOfEnvrn > 0) {
     724          110 :                 ResolveLocationInformation(state, ErrorsFound); // Obtain weather related info from input file
     725          110 :                 CheckLocationValidity(state);
     726          160 :                 if ((state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
     727           50 :                     (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
     728           50 :                     CheckWeatherFileValidity(state);
     729              :                 }
     730          109 :                 if (ErrorsFound) {
     731            1 :                     ShowSevereError(state, format("{}No location specified, program will terminate.", RoutineName));
     732              :                 }
     733              :             } else {
     734            1 :                 ErrorsFound = true;
     735            1 :                 ShowSevereError(state, format("{}No Design Days or Run Period(s) specified, program will terminate.", RoutineName));
     736              :             }
     737          110 :             if (state.dataSysVars->DDOnly && state.dataEnvrn->TotDesDays == 0) {
     738            1 :                 ErrorsFound = true;
     739            2 :                 ShowSevereError(
     740              :                     state,
     741            2 :                     format("{}Requested Design Days only (DataSystemVariables::DDOnly) but no Design Days specified, program will terminate.",
     742              :                            RoutineName));
     743              :             }
     744          110 :             if (state.dataSysVars->ReverseDD && state.dataEnvrn->TotDesDays == 1) {
     745            0 :                 ErrorsFound = true;
     746            0 :                 ShowSevereError(
     747              :                     state,
     748            0 :                     format(
     749              :                         "{}Requested Reverse Design Days (DataSystemVariables::ReverseDD) but only 1 Design Day specified, program will terminate.",
     750              :                         RoutineName));
     751              :             }
     752              : 
     753              :             // Throw a Fatal now that we have said it'll terminalte
     754          110 :             if (ErrorsFound) {
     755            2 :                 CloseWeatherFile(state); // will only close if opened.
     756            4 :                 ShowFatalError(state, format("{}Errors found in Weather Data Input. Program terminates.", RoutineName));
     757              :             }
     758              : 
     759          108 :             state.dataEnvrn->CurrentOverallSimDay = 0;
     760          108 :             state.dataEnvrn->TotalOverallSimDays = 0;
     761          108 :             state.dataEnvrn->MaxNumberSimYears = 1;
     762          336 :             for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
     763          228 :                 state.dataEnvrn->TotalOverallSimDays += state.dataWeather->Environment(i).TotalDays;
     764          228 :                 if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
     765           52 :                     state.dataEnvrn->MaxNumberSimYears = max(state.dataEnvrn->MaxNumberSimYears, state.dataWeather->Environment(i).NumSimYears);
     766              :                 }
     767              :             }
     768          108 :             DisplaySimDaysProgress(state, state.dataEnvrn->CurrentOverallSimDay, state.dataEnvrn->TotalOverallSimDays);
     769              :         }
     770              : 
     771          848 :         CloseWeatherFile(state); // will only close if opened.
     772          848 :         ++state.dataWeather->Envrn;
     773          848 :         state.dataWeather->DatesShouldBeReset = false;
     774          848 :         if (state.dataWeather->Envrn > state.dataWeather->NumOfEnvrn) {
     775          208 :             Available = false;
     776          208 :             state.dataWeather->Envrn = 0;
     777          208 :             state.dataEnvrn->CurEnvirNum = 0;
     778              :         } else {
     779          640 :             auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
     780          640 :             state.dataGlobal->KindOfSim = envCurr.KindOfEnvrn;
     781          640 :             state.dataEnvrn->DayOfYear = envCurr.StartJDay;
     782          640 :             state.dataEnvrn->DayOfMonth = envCurr.StartDay;
     783          640 :             state.dataGlobal->CalendarYear = envCurr.StartYear;
     784          640 :             state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
     785          640 :             state.dataEnvrn->Month = envCurr.StartMonth;
     786          640 :             state.dataGlobal->NumOfDayInEnvrn = envCurr.TotalDays; // Set day loop maximum from DataGlobals
     787              : 
     788          865 :             if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
     789          225 :                 (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
     790            0 :                 if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) {
     791            0 :                     if (state.dataGlobal->DoDesDaySim) {
     792            0 :                         ShowWarningError(state, format("{}Adaptive Comfort being reported during design day.", RoutineName));
     793            0 :                         Real64 GrossApproxAvgDryBulb = (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb +
     794            0 :                                                         (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb -
     795            0 :                                                          state.dataWeather->DesDayInput(state.dataWeather->Envrn).DailyDBRange)) /
     796            0 :                                                        2.0;
     797            0 :                         if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
     798            0 :                             ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, false, GrossApproxAvgDryBulb);
     799              :                         }
     800            0 :                         if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
     801            0 :                             ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, false, GrossApproxAvgDryBulb);
     802              :                         }
     803              :                     }
     804              :                 } else {
     805            0 :                     if (state.dataGlobal->DoWeathSim || state.dataGlobal->DoDesDaySim) {
     806            0 :                         if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
     807            0 :                             ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, true, 0.0);
     808              :                         }
     809            0 :                         if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
     810            0 :                             ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, true, 0.0);
     811              :                         }
     812              :                     }
     813              :                 }
     814              :             }
     815              : 
     816          640 :             if (state.dataWeather->Envrn > state.dataEnvrn->TotDesDays && state.dataWeather->WeatherFileExists) {
     817           44 :                 OpenEPlusWeatherFile(state, ErrorsFound, false);
     818              :             }
     819          640 :             Available = true;
     820          762 :             if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
     821          122 :                 (!state.dataWeather->WeatherFileExists && state.dataGlobal->DoWeathSim)) {
     822            0 :                 if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
     823            0 :                     ShowSevereError(state, "Weather Simulation requested, but no weather file attached.");
     824            0 :                     ErrorsFound = true;
     825              :                 }
     826            0 :                 if (!state.dataGlobal->DoingHVACSizingSimulations) {
     827            0 :                     state.dataWeather->Envrn = 0;
     828              :                 }
     829            0 :                 Available = false;
     830          762 :             } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
     831          122 :                        (!state.dataWeather->WeatherFileExists && !state.dataGlobal->DoWeathSim)) {
     832           80 :                 Available = false;
     833           80 :                 if (!state.dataGlobal->DoingHVACSizingSimulations) {
     834           80 :                     state.dataWeather->Envrn = 0;
     835              :                 }
     836          560 :             } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoingSizing) {
     837            0 :                 Available = false;
     838            0 :                 state.dataWeather->Envrn = 0;
     839              :             }
     840              : 
     841          640 :             if (!ErrorsFound && Available && state.dataWeather->Envrn > 0) {
     842          560 :                 state.dataEnvrn->EnvironmentName = envCurr.Title;
     843          560 :                 state.dataEnvrn->CurEnvirNum = state.dataWeather->Envrn;
     844          560 :                 state.dataEnvrn->RunPeriodStartDayOfWeek = 0;
     845          665 :                 if ((state.dataGlobal->DoDesDaySim && (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather)) ||
     846          105 :                     ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoWeathSim)) {
     847          458 :                     if (state.dataWeather->PrntEnvHeaders && state.dataReportFlag->DoWeatherInitReporting) {
     848              :                         static constexpr std::string_view EnvironFormat(
     849              :                             "! <Environment>,Environment Name,Environment Type, Start Date, End Date, Start DayOfWeek, Duration {#days}, "
     850              :                             "Source:Start DayOfWeek,  Use Daylight Saving, Use Holidays, Apply Weekend Holiday Rule,  Use Rain Values, Use Snow "
     851              :                             "Values, Sky Temperature Model\n! <Environment:Special Days>, Special Day Name, Special Day Type, Source, Start Date, "
     852              :                             "Duration {#days}\n! "
     853              :                             "<Environment:Daylight Saving>, Daylight Saving Indicator, Source, Start Date, End Date\n! <Environment:WarmupDays>, "
     854              :                             "NumberofWarmupDays");
     855           65 :                         print(state.files.eio, "{}\n", EnvironFormat);
     856           65 :                         state.dataWeather->PrntEnvHeaders = false;
     857              :                     }
     858              : 
     859          458 :                     std::string StDate;
     860          458 :                     std::string EnDate;
     861          913 :                     if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) ||
     862          455 :                         (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodDesign)) {
     863            3 :                         int DSTActStMon = 0;
     864            3 :                         int DSTActStDay = 0;
     865            3 :                         int DSTActEnMon = 0;
     866            3 :                         int DSTActEnDay = 0;
     867            3 :                         std::string kindOfRunPeriod = envCurr.cKindOfEnvrn;
     868            3 :                         state.dataEnvrn->RunPeriodEnvironment = state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather;
     869            3 :                         state.dataEnvrn->CurrentYearIsLeapYear = state.dataWeather->Environment(state.dataWeather->Envrn).IsLeapYear;
     870            3 :                         if (state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears) {
     871            0 :                             state.dataWeather->LeapYearAdd = 1;
     872              :                         } else {
     873            3 :                             state.dataWeather->LeapYearAdd = 0;
     874              :                         }
     875            3 :                         if (state.dataEnvrn->CurrentYearIsLeapYear) {
     876            0 :                             state.dataWeather->EndDayOfMonthWithLeapDay(2) = state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
     877              :                         }
     878            3 :                         state.dataWeather->UseDaylightSaving = envCurr.UseDST;
     879            3 :                         state.dataWeather->UseSpecialDays = envCurr.UseHolidays;
     880            3 :                         state.dataWeather->UseRainValues = envCurr.UseRain;
     881            3 :                         state.dataWeather->UseSnowValues = envCurr.UseSnow;
     882              : 
     883            3 :                         bool missingLeap(false); // Defer acting on anything found here until after the other range checks (see below)
     884              : 
     885            3 :                         if (envCurr.ActualWeather && !state.dataWeather->WFAllowsLeapYears) {
     886            0 :                             for (int year = envCurr.StartYear; year <= envCurr.EndYear; year++) {
     887            0 :                                 if (!isLeapYear(year)) {
     888            0 :                                     continue;
     889              :                                 }
     890              : 
     891            0 :                                 ShowSevereError(
     892              :                                     state,
     893            0 :                                     format("{}Weatherfile does not support leap years but runperiod includes a leap year ({})", RoutineName, year));
     894            0 :                                 missingLeap = true;
     895              :                             }
     896              :                         }
     897              : 
     898            3 :                         bool OkRun = false;
     899              : 
     900            3 :                         if (envCurr.ActualWeather) {
     901              :                             // Actual weather
     902            0 :                             for (auto const &dataperiod : state.dataWeather->DataPeriods) {
     903            0 :                                 int runStartJulian = dataperiod.DataStJDay;
     904            0 :                                 int runEndJulian = dataperiod.DataEnJDay;
     905            0 :                                 if (!dataperiod.HasYearData) {
     906            0 :                                     ShowSevereError(state,
     907            0 :                                                     format("{}Actual weather runperiod has been entered but weatherfile DATA PERIOD does not have "
     908              :                                                            "year included in start/end date.",
     909              :                                                            RoutineName));
     910            0 :                                     ShowContinueError(state, "...to match the RunPeriod, the DATA PERIOD should be mm/dd/yyyy for both, or");
     911            0 :                                     ShowContinueError(state, "(...set \"Treat Weather as Actual\" to \"No\".)");
     912              :                                 }
     913            0 :                                 if (!General::BetweenDates(envCurr.StartDate, runStartJulian, runEndJulian)) {
     914            0 :                                     continue;
     915              :                                 }
     916            0 :                                 if (!General::BetweenDates(envCurr.EndDate, runStartJulian, runEndJulian)) {
     917            0 :                                     continue;
     918              :                                 }
     919            0 :                                 OkRun = true;
     920            0 :                                 break;
     921            0 :                             }
     922              :                         } else {
     923              :                             // Typical (or just non-actual) weather
     924            3 :                             for (auto &dataperiod : state.dataWeather->DataPeriods) {
     925              :                                 // Since this is not actual weather, there may be issues with this calculation
     926              :                                 // Assume the weather data starts the same year as the simulation, so LeapYearAdd is what
     927              :                                 // should be used.
     928            3 :                                 int runStartOrdinal = General::OrdinalDay(dataperiod.StMon, dataperiod.StDay, state.dataWeather->LeapYearAdd);
     929              :                                 // This one is harder, leave as is for now. What about multiple years of data?
     930            3 :                                 int runEndOrdinal = General::OrdinalDay(dataperiod.EnMon, dataperiod.EnDay, state.dataWeather->LeapYearAdd);
     931            3 :                                 if (runStartOrdinal == 1 && (runEndOrdinal == 366 || runEndOrdinal == 365)) {
     932              :                                     // Complete year(s) of weather data, will wrap around
     933            3 :                                     OkRun = true;
     934            3 :                                     break;
     935              :                                 }
     936            0 :                                 if (!General::BetweenDates(envCurr.StartJDay, runStartOrdinal, runEndOrdinal)) {
     937            0 :                                     continue;
     938              :                                 }
     939            0 :                                 if (!General::BetweenDates(envCurr.EndJDay, runStartOrdinal, runEndOrdinal)) {
     940            0 :                                     continue;
     941              :                                 }
     942            0 :                                 OkRun = true;
     943            3 :                             }
     944              :                         }
     945              : 
     946            3 :                         if (!OkRun) {
     947            0 :                             if (!envCurr.ActualWeather) {
     948            0 :                                 StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
     949            0 :                                 EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
     950            0 :                                 ShowSevereError(state,
     951            0 :                                                 format("{}Runperiod [mm/dd] (Start={},End={}) requested not within Data Period(s) from Weather File",
     952              :                                                        RoutineName,
     953              :                                                        StDate,
     954              :                                                        EnDate));
     955              :                             } else {
     956            0 :                                 StDate = format(DateFormatWithYear, envCurr.StartMonth, envCurr.StartDay, envCurr.StartYear);
     957            0 :                                 EnDate = format(DateFormatWithYear, envCurr.EndMonth, envCurr.EndDay, envCurr.EndYear);
     958            0 :                                 ShowSevereError(
     959              :                                     state,
     960            0 :                                     format("{}Runperiod [mm/dd/yyyy] (Start={},End={}) requested not within Data Period(s) from Weather File",
     961              :                                            RoutineName,
     962              :                                            StDate,
     963              :                                            EnDate));
     964              :                             }
     965              : 
     966            0 :                             auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
     967            0 :                             StDate = format(DateFormat, dataPeriod1.StMon, dataPeriod1.StDay);
     968            0 :                             EnDate = format(DateFormat, dataPeriod1.EnMon, dataPeriod1.EnDay);
     969            0 :                             if (dataPeriod1.StYear > 0) {
     970            0 :                                 StDate += format("/{}", dataPeriod1.StYear);
     971              :                             } else {
     972            0 :                                 StDate += "/<noyear>";
     973              :                             }
     974            0 :                             if (dataPeriod1.EnYear > 0) {
     975            0 :                                 EnDate += format("/{}", dataPeriod1.EnYear);
     976              :                             } else {
     977            0 :                                 EnDate += "/<noyear>";
     978              :                             }
     979            0 :                             if (state.dataWeather->NumDataPeriods == 1) {
     980            0 :                                 ShowContinueError(state, format("Weather Data Period (Start={},End={})", StDate, EnDate));
     981              :                             } else {
     982            0 :                                 ShowContinueError(state, format("Multiple Weather Data Periods 1st (Start={},End={})", StDate, EnDate));
     983              :                             }
     984            0 :                             ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
     985              :                         }
     986              : 
     987            3 :                         if (missingLeap) {
     988              :                             // Bail out now if we still need to
     989            0 :                             ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
     990              :                         }
     991              : 
     992              :                         // Following builds Environment start/end for ASHRAE 55 warnings
     993            3 :                         StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
     994            3 :                         EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
     995            3 :                         if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
     996            3 :                             StDate += format("/{}", envCurr.StartYear);
     997            3 :                             EnDate += format("/{}", envCurr.EndYear);
     998              :                         }
     999            3 :                         state.dataEnvrn->EnvironmentStartEnd = StDate + " - " + EnDate;
    1000            3 :                         state.dataEnvrn->StartYear = envCurr.StartYear;
    1001            3 :                         state.dataEnvrn->EndYear = envCurr.EndYear;
    1002              : 
    1003            3 :                         int TWeekDay = (envCurr.DayOfWeek == 0) ? 1 : envCurr.DayOfWeek;
    1004            3 :                         auto const &MonWeekDay = envCurr.MonWeekDay;
    1005              : 
    1006            3 :                         if (state.dataReportFlag->DoWeatherInitReporting) {
    1007            0 :                             std::string_view const AlpUseDST = (envCurr.UseDST) ? "Yes" : "No";
    1008            0 :                             std::string_view const AlpUseSpec = (envCurr.UseHolidays) ? "Yes" : "No";
    1009            0 :                             std::string_view const ApWkRule = (envCurr.ApplyWeekendRule) ? "Yes" : "No";
    1010            0 :                             std::string_view const AlpUseRain = (envCurr.UseRain) ? "Yes" : "No";
    1011            0 :                             std::string_view const AlpUseSnow = (envCurr.UseSnow) ? "Yes" : "No";
    1012              : 
    1013            0 :                             print(state.files.eio,
    1014              :                                   EnvNameFormat,
    1015            0 :                                   envCurr.Title,
    1016              :                                   kindOfRunPeriod,
    1017              :                                   StDate,
    1018              :                                   EnDate,
    1019            0 :                                   Sched::dayTypeNames[TWeekDay],
    1020            0 :                                   fmt::to_string(envCurr.TotalDays),
    1021              :                                   "Use RunPeriod Specified Day",
    1022              :                                   AlpUseDST,
    1023              :                                   AlpUseSpec,
    1024              :                                   ApWkRule,
    1025              :                                   AlpUseRain,
    1026              :                                   AlpUseSnow,
    1027            0 :                                   SkyTempModelNames[(int)envCurr.skyTempModel]);
    1028              :                         }
    1029              : 
    1030            6 :                         if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
    1031            9 :                             (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather && state.dataGlobal->DoWeathSim) &&
    1032            3 :                             (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
    1033            0 :                             if (state.dataWeather->WFAllowsLeapYears) {
    1034            0 :                                 ShowSevereError(
    1035              :                                     state,
    1036            0 :                                     format("{}AdaptiveComfort Reporting does not work correctly with leap years in weather files.", RoutineName));
    1037            0 :                                 ErrorsFound = true;
    1038              :                             }
    1039            0 :                             if (state.dataWeather->NumDataPeriods != 1) {
    1040            0 :                                 ShowSevereError(
    1041              :                                     state,
    1042            0 :                                     format("{}AdaptiveComfort Reporting does not work correctly with multiple dataperiods in weather files.",
    1043              :                                            RoutineName));
    1044            0 :                                 ErrorsFound = true;
    1045              :                             }
    1046            0 :                             auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
    1047            0 :                             if (dataPeriod1.StMon == 1 && dataPeriod1.StDay == 1) {
    1048            0 :                                 int RunStJDay = General::OrdinalDay(dataPeriod1.StMon, dataPeriod1.StDay, state.dataWeather->LeapYearAdd);
    1049            0 :                                 int RunEnJDay = General::OrdinalDay(dataPeriod1.EnMon, dataPeriod1.EnDay, state.dataWeather->LeapYearAdd);
    1050            0 :                                 if (RunEnJDay - RunStJDay + 1 != 365) {
    1051            0 :                                     ShowSevereError(state,
    1052            0 :                                                     format("{}AdaptiveComfort Reporting does not work correctly with weather files that do "
    1053              :                                                            "not contain 365 days.",
    1054              :                                                            RoutineName));
    1055            0 :                                     ErrorsFound = true;
    1056              :                                 }
    1057            0 :                             } else {
    1058            0 :                                 ShowSevereError(state,
    1059            0 :                                                 format("{}AdaptiveComfort Reporting does not work correctly with weather files that do not "
    1060              :                                                        "start on 1 January.",
    1061              :                                                        RoutineName));
    1062            0 :                                 ErrorsFound = true;
    1063              :                             }
    1064            0 :                             if (state.dataWeather->NumIntervalsPerHour != 1) {
    1065            0 :                                 ShowSevereError(state,
    1066            0 :                                                 format("{}AdaptiveComfort Reporting does not work correctly with weather files that have "
    1067              :                                                        "multiple interval records per hour.",
    1068              :                                                        RoutineName));
    1069            0 :                                 ErrorsFound = true;
    1070              :                             }
    1071              :                         } // if
    1072              : 
    1073              :                         // Only need to set Week days for Run Days
    1074            3 :                         state.dataEnvrn->RunPeriodStartDayOfWeek = TWeekDay;
    1075            3 :                         state.dataWeather->WeekDayTypes = 0;
    1076            3 :                         int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
    1077            3 :                         int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
    1078              : 
    1079            3 :                         state.dataWeather->curSimDayForEndOfRunPeriod = envCurr.TotalDays;
    1080              : 
    1081            3 :                         int i = JDay5Start;
    1082              :                         while (true) {
    1083          426 :                             state.dataWeather->WeekDayTypes(i) = TWeekDay;
    1084          426 :                             TWeekDay = mod(TWeekDay, 7) + 1;
    1085          426 :                             ++i;
    1086          426 :                             if (i > 366) {
    1087            1 :                                 i = 1;
    1088              :                             }
    1089          426 :                             if (i == JDay5End) {
    1090            3 :                                 break;
    1091              :                             }
    1092              :                         }
    1093              : 
    1094            3 :                         state.dataWeather->DaylightSavingIsActive =
    1095            3 :                             (state.dataWeather->UseDaylightSaving && state.dataWeather->EPWDaylightSaving) || state.dataWeather->IDFDaylightSaving;
    1096              : 
    1097            3 :                         envCurr.SetWeekDays = false;
    1098              : 
    1099            3 :                         if (state.dataWeather->DaylightSavingIsActive) {
    1100            0 :                             SetDSTDateRanges(state, MonWeekDay, state.dataWeather->DSTIndex, DSTActStMon, DSTActStDay, DSTActEnMon, DSTActEnDay);
    1101              :                         }
    1102              : 
    1103            3 :                         SetSpecialDayDates(state, MonWeekDay);
    1104              : 
    1105            3 :                         if (envCurr.StartMonth != 1 || envCurr.StartDay != 1) {
    1106            1 :                             state.dataWeather->StartDatesCycleShouldBeReset = true;
    1107            1 :                             state.dataWeather->Jan1DatesShouldBeReset = true;
    1108              :                         }
    1109              : 
    1110            3 :                         if (envCurr.StartMonth == 1 && envCurr.StartDay == 1) {
    1111            2 :                             state.dataWeather->StartDatesCycleShouldBeReset = false;
    1112            2 :                             state.dataWeather->Jan1DatesShouldBeReset = true;
    1113              :                         }
    1114              : 
    1115            3 :                         if (envCurr.ActualWeather) {
    1116            0 :                             state.dataWeather->StartDatesCycleShouldBeReset = false;
    1117            0 :                             state.dataWeather->Jan1DatesShouldBeReset = true;
    1118              :                         }
    1119              : 
    1120              :                         // Report Actual Dates for Daylight Saving and Special Days
    1121            3 :                         if (!state.dataGlobal->KickOffSimulation) {
    1122            3 :                             std::string Source;
    1123            3 :                             if (state.dataWeather->UseDaylightSaving) {
    1124            3 :                                 if (state.dataWeather->EPWDaylightSaving) {
    1125            0 :                                     Source = "WeatherFile";
    1126              :                                 }
    1127              :                             } else {
    1128            0 :                                 Source = "RunPeriod Object";
    1129              :                             }
    1130            3 :                             if (state.dataWeather->IDFDaylightSaving) {
    1131            0 :                                 Source = "InputFile";
    1132              :                             }
    1133            3 :                             if (state.dataWeather->DaylightSavingIsActive && state.dataReportFlag->DoWeatherInitReporting) {
    1134            0 :                                 StDate = format(DateFormat, DSTActStMon, DSTActStDay);
    1135            0 :                                 EnDate = format(DateFormat, DSTActEnMon, DSTActEnDay);
    1136            0 :                                 print(state.files.eio, EnvDSTYFormat, Source, StDate, EnDate);
    1137            3 :                             } else if (state.dataGlobal->DoOutputReporting) {
    1138            0 :                                 print(state.files.eio, EnvDSTNFormat, Source);
    1139              :                             }
    1140            3 :                             for (int k = 1; k <= state.dataWeather->NumSpecialDays; ++k) {
    1141            0 :                                 auto &specialDay = state.dataWeather->SpecialDays(k);
    1142              :                                 static constexpr std::string_view EnvSpDyFormat("Environment:Special Days,{},{},{},{},{:3}\n");
    1143            0 :                                 if (specialDay.WthrFile && state.dataWeather->UseSpecialDays && state.dataReportFlag->DoWeatherInitReporting) {
    1144            0 :                                     StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
    1145            0 :                                     print(state.files.eio,
    1146              :                                           EnvSpDyFormat,
    1147            0 :                                           specialDay.Name,
    1148            0 :                                           Sched::dayTypeNames[specialDay.DayType],
    1149              :                                           "WeatherFile",
    1150              :                                           StDate,
    1151            0 :                                           specialDay.Duration);
    1152              :                                 }
    1153            0 :                                 if (!specialDay.WthrFile && state.dataReportFlag->DoWeatherInitReporting) {
    1154            0 :                                     StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
    1155            0 :                                     print(state.files.eio,
    1156              :                                           EnvSpDyFormat,
    1157            0 :                                           specialDay.Name,
    1158            0 :                                           Sched::dayTypeNames[specialDay.DayType],
    1159              :                                           "InputFile",
    1160              :                                           StDate,
    1161            0 :                                           specialDay.Duration);
    1162              :                                 }
    1163              :                             }
    1164            3 :                         }
    1165              : 
    1166          460 :                     } else if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay ||
    1167            2 :                                state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay) { // Design Day
    1168          453 :                         auto const &desDayInput = state.dataWeather->DesDayInput(envCurr.DesignDayNum);
    1169          453 :                         state.dataEnvrn->RunPeriodEnvironment = false;
    1170          453 :                         StDate = format(DateFormat, desDayInput.Month, desDayInput.DayOfMonth);
    1171          453 :                         EnDate = StDate;
    1172          453 :                         if (state.dataReportFlag->DoWeatherInitReporting) {
    1173          112 :                             print(state.files.eio,
    1174              :                                   EnvNameFormat,
    1175          112 :                                   envCurr.Title,
    1176              :                                   "SizingPeriod:DesignDay",
    1177              :                                   StDate,
    1178              :                                   EnDate,
    1179          112 :                                   Sched::dayTypeNames[desDayInput.DayType],
    1180              :                                   "1",
    1181              :                                   "N/A",
    1182              :                                   "N/A",
    1183              :                                   "N/A",
    1184              :                                   "N/A",
    1185              :                                   "N/A",
    1186              :                                   "N/A",
    1187          112 :                                   SkyTempModelNames[(int)envCurr.skyTempModel]);
    1188              :                         }
    1189          453 :                         if (desDayInput.DSTIndicator == 0 && state.dataReportFlag->DoWeatherInitReporting) {
    1190          112 :                             print(state.files.eio, EnvDSTNFormat, "SizingPeriod:DesignDay");
    1191          341 :                         } else if (state.dataReportFlag->DoWeatherInitReporting) {
    1192            0 :                             print(state.files.eio, EnvDSTYFormat, "SizingPeriod:DesignDay", StDate, EnDate);
    1193              :                         }
    1194              :                     }
    1195          458 :                 }
    1196              :             } // ErrorsFound
    1197              :         }
    1198              : 
    1199          848 :         if (ErrorsFound && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
    1200            0 :             ShowSevereError(state, format("{}Errors found in getting a new environment", RoutineName));
    1201            0 :             Available = false;
    1202          848 :         } else if (ErrorsFound) {
    1203            0 :             Available = false;
    1204              :         }
    1205          848 :         return Available && !ErrorsFound;
    1206              :     }
    1207              : 
    1208            4 :     void AddDesignSetToEnvironmentStruct(EnergyPlusData &state, int const HVACSizingIterCount)
    1209              :     {
    1210            4 :         int OrigNumOfEnvrn = state.dataWeather->NumOfEnvrn;
    1211              : 
    1212           14 :         for (int i = 1; i <= OrigNumOfEnvrn; ++i) {
    1213              :             // Gotcha: references may no longer be valid after a redimension! Cannot declare reference to Environment(i) here.
    1214           10 :             if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
    1215            8 :                 state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
    1216            8 :                 auto &envBase = state.dataWeather->Environment(i);
    1217            8 :                 auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
    1218            8 :                 envNew = envBase; // copy over seed data from current array element
    1219            8 :                 envNew.SeedEnvrnNum = i;
    1220            8 :                 envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeDesignDay;
    1221            8 :                 envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
    1222            8 :                 envNew.HVACSizingIterationNum = HVACSizingIterCount;
    1223            2 :             } else if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
    1224            2 :                 state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
    1225            2 :                 auto &envBase = state.dataWeather->Environment(i);
    1226            2 :                 auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
    1227            2 :                 envNew = envBase; // copy over seed data
    1228            2 :                 envNew.SeedEnvrnNum = i;
    1229            2 :                 envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeRunPeriodDesign;
    1230            2 :                 envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
    1231            2 :                 envNew.HVACSizingIterationNum = HVACSizingIterCount;
    1232              :             }
    1233              :         } // for each loop over Environment data strucure
    1234            4 :     }
    1235              : 
    1236           93 :     void SetupWeekDaysByMonth(EnergyPlusData &state, int const StMon, int const StDay, int const StWeekDay, Array1D_int &WeekDays)
    1237              :     {
    1238              : 
    1239              :         // SUBROUTINE INFORMATION:
    1240              :         //       AUTHOR         Linda Lawrie
    1241              :         //       DATE WRITTEN   August 2000
    1242              : 
    1243              :         // PURPOSE OF THIS SUBROUTINE:
    1244              :         // This subroutine calculates the weekday for each month based on the start date and
    1245              :         // weekday specified for that date.
    1246              : 
    1247              :         // Argument array dimensioning
    1248           93 :         EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
    1249              : 
    1250              :         // Set 1st day of Start Month
    1251           93 :         int CurWeekDay{StWeekDay};
    1252          256 :         for (int i = 1; i <= StDay - 1; ++i) {
    1253          163 :             --CurWeekDay;
    1254          163 :             if (CurWeekDay == 0) {
    1255           23 :                 CurWeekDay = 7;
    1256              :             }
    1257              :         }
    1258              : 
    1259           93 :         WeekDays(StMon) = CurWeekDay;
    1260         1080 :         for (int i = StMon + 1; i <= 12; ++i) {
    1261              : 
    1262          987 :             if (i == 2) {
    1263           84 :                 CurWeekDay += state.dataWeather->EndDayOfMonth(1);
    1264          424 :                 while (CurWeekDay > 7) {
    1265          340 :                     CurWeekDay -= 7;
    1266              :                 }
    1267           84 :                 WeekDays(i) = CurWeekDay;
    1268          903 :             } else if (i == 3) {
    1269           88 :                 CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + state.dataWeather->LeapYearAdd;
    1270          440 :                 while (CurWeekDay > 7) {
    1271          352 :                     CurWeekDay -= 7;
    1272              :                 }
    1273           88 :                 WeekDays(i) = CurWeekDay;
    1274              :             } else {
    1275          815 :                 CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
    1276         4391 :                 while (CurWeekDay > 7) {
    1277         3576 :                     CurWeekDay -= 7;
    1278              :                 }
    1279          815 :                 WeekDays(i) = CurWeekDay;
    1280              :             }
    1281              :         }
    1282              : 
    1283           93 :         if (any_eq(WeekDays, 0)) {
    1284              :             // need to start at StMon and go backwards.
    1285              :             // EndDayOfMonth is also "days" in month.  (without leapyear day in February)
    1286            9 :             CurWeekDay = StWeekDay;
    1287          146 :             for (int i = 1; i <= StDay - 1; ++i) {
    1288          137 :                 --CurWeekDay;
    1289          137 :                 if (CurWeekDay == 0) {
    1290           19 :                     CurWeekDay = 7;
    1291              :                 }
    1292              :             }
    1293              : 
    1294           45 :             for (int i = StMon - 1; i >= 1; --i) {
    1295              : 
    1296           36 :                 if (i == 1) {
    1297            9 :                     CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
    1298           49 :                     while (CurWeekDay <= 0) {
    1299           40 :                         CurWeekDay += 7;
    1300              :                     }
    1301            9 :                     WeekDays(i) = CurWeekDay;
    1302           27 :                 } else if (i == 2) {
    1303            5 :                     CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
    1304           25 :                     while (CurWeekDay <= 0) {
    1305           20 :                         CurWeekDay += 7;
    1306              :                     }
    1307            5 :                     WeekDays(i) = CurWeekDay;
    1308           22 :                 } else if ((i >= 3) && (i <= 12)) {
    1309           22 :                     CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
    1310          119 :                     while (CurWeekDay <= 0) {
    1311           97 :                         CurWeekDay += 7;
    1312              :                     }
    1313           22 :                     WeekDays(i) = CurWeekDay;
    1314              :                 }
    1315              :             }
    1316              :         }
    1317           93 :     }
    1318              : #pragma clang diagnostic pop
    1319              : 
    1320            0 :     void ResetWeekDaysByMonth(EnergyPlusData &state,
    1321              :                               Array1D_int &WeekDays,
    1322              :                               int const AddLeapYear,
    1323              :                               int const StartMonth,
    1324              :                               int const StartMonthDay,
    1325              :                               int const EndMonth,
    1326              :                               int const EndMonthDay,
    1327              :                               bool const Rollover,
    1328              :                               bool const MidSimReset)
    1329              :     {
    1330              : 
    1331              :         // SUBROUTINE INFORMATION:
    1332              :         //       AUTHOR         Linda Lawrie
    1333              :         //       DATE WRITTEN   March 2012
    1334              : 
    1335              :         // PURPOSE OF THIS SUBROUTINE:
    1336              :         // This subroutine resets the weekday for each month based on the current weekday
    1337              :         // and previous weekdays per month.
    1338              : 
    1339            0 :         EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
    1340              : 
    1341            0 :         Array1D_int WeekDaysCopy(12);
    1342              :         int CurWeekDay;
    1343              : 
    1344            0 :         WeekDaysCopy = WeekDays;
    1345            0 :         if (!MidSimReset) {
    1346            0 :             if (Rollover) {
    1347            0 :                 if (StartMonth == 1) {
    1348            0 :                     CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
    1349              :                 } else {
    1350            0 :                     CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
    1351              :                 }
    1352              :             } else { // restart at same as before
    1353            0 :                 CurWeekDay = WeekDays(StartMonth);
    1354              :             }
    1355            0 :             while (CurWeekDay > 7) {
    1356            0 :                 CurWeekDay -= 7;
    1357              :             }
    1358              : 
    1359            0 :             WeekDays = 0;
    1360            0 :             WeekDays(StartMonth) = CurWeekDay;
    1361            0 :             for (int i = StartMonth + 1; i <= 12; ++i) {
    1362            0 :                 if (i == 2) {
    1363            0 :                     CurWeekDay += state.dataWeather->EndDayOfMonth(1);
    1364            0 :                     while (CurWeekDay > 7) {
    1365            0 :                         CurWeekDay -= 7;
    1366              :                     }
    1367            0 :                     WeekDays(i) = CurWeekDay;
    1368            0 :                 } else if (i == 3) {
    1369            0 :                     CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
    1370            0 :                     while (CurWeekDay > 7) {
    1371            0 :                         CurWeekDay -= 7;
    1372              :                     }
    1373            0 :                     WeekDays(i) = CurWeekDay;
    1374              :                 } else {
    1375            0 :                     CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
    1376            0 :                     while (CurWeekDay > 7) {
    1377            0 :                         CurWeekDay -= 7;
    1378              :                     }
    1379            0 :                     WeekDays(i) = CurWeekDay;
    1380              :                 }
    1381              :             }
    1382              : 
    1383            0 :             if (any_eq(WeekDays, 0)) {
    1384              :                 // need to start at StMon and go backwards.
    1385              :                 // EndDayOfMonth is also "days" in month.  (without leapyear day in February)
    1386            0 :                 CurWeekDay = WeekDays(StartMonth);
    1387            0 :                 for (int i = 1; i <= StartMonthDay - 1; ++i) {
    1388            0 :                     --CurWeekDay;
    1389            0 :                     if (CurWeekDay == 0) {
    1390            0 :                         CurWeekDay = 7;
    1391              :                     }
    1392              :                 }
    1393              : 
    1394            0 :                 for (int i = StartMonth - 1; i >= 1; --i) {
    1395              : 
    1396            0 :                     if (i == 1) {
    1397            0 :                         CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
    1398            0 :                         while (CurWeekDay <= 0) {
    1399            0 :                             CurWeekDay += 7;
    1400              :                         }
    1401            0 :                         WeekDays(i) = CurWeekDay;
    1402            0 :                     } else if (i == 2) {
    1403            0 :                         CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
    1404            0 :                         while (CurWeekDay <= 0) {
    1405            0 :                             CurWeekDay += 7;
    1406              :                         }
    1407            0 :                         WeekDays(i) = CurWeekDay;
    1408            0 :                     } else if ((i >= 3) && (i <= 12)) {
    1409            0 :                         CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
    1410            0 :                         while (CurWeekDay <= 0) {
    1411            0 :                             CurWeekDay += 7;
    1412              :                         }
    1413            0 :                         WeekDays(i) = CurWeekDay;
    1414              :                     }
    1415              :                 }
    1416              :             }
    1417              : 
    1418              :         } else {
    1419            0 :             if (Rollover) {
    1420            0 :                 if (StartMonth == 1) {
    1421            0 :                     CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
    1422              :                 } else {
    1423            0 :                     CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
    1424              :                 }
    1425              :             } else { // restart at same as before
    1426            0 :                 CurWeekDay = WeekDays(StartMonth);
    1427              :             }
    1428            0 :             while (CurWeekDay > 7) {
    1429            0 :                 CurWeekDay -= 7;
    1430              :             }
    1431            0 :             WeekDays = 0;
    1432            0 :             if (StartMonth != 1) {
    1433            0 :                 CurWeekDay = WeekDaysCopy(12) + state.dataWeather->EndDayOfMonth(12);
    1434            0 :                 while (CurWeekDay > 7) {
    1435            0 :                     CurWeekDay -= 7;
    1436              :                 }
    1437            0 :                 WeekDays(1) = CurWeekDay;
    1438            0 :                 CurWeekDay += state.dataWeather->EndDayOfMonth(1);
    1439            0 :                 while (CurWeekDay > 7) {
    1440            0 :                     CurWeekDay -= 7;
    1441              :                 }
    1442            0 :                 WeekDays(2) = CurWeekDay;
    1443            0 :                 CurWeekDay += state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
    1444            0 :                 while (CurWeekDay > 7) {
    1445            0 :                     CurWeekDay -= 7;
    1446              :                 }
    1447            0 :                 WeekDays(3) = CurWeekDay;
    1448            0 :                 for (int i = 4; i <= 12; ++i) {
    1449            0 :                     CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
    1450            0 :                     while (CurWeekDay > 7) {
    1451            0 :                         CurWeekDay -= 7;
    1452              :                     }
    1453            0 :                     WeekDays(i) = CurWeekDay;
    1454              :                 }
    1455              :             } else {
    1456            0 :                 WeekDays = 0;
    1457            0 :                 WeekDays(StartMonth) = CurWeekDay;
    1458            0 :                 for (int i = StartMonth + 1; i <= 12; ++i) {
    1459            0 :                     if (i == 2) {
    1460            0 :                         CurWeekDay += state.dataWeather->EndDayOfMonth(1);
    1461            0 :                         while (CurWeekDay > 7) {
    1462            0 :                             CurWeekDay -= 7;
    1463              :                         }
    1464            0 :                         WeekDays(i) = CurWeekDay;
    1465            0 :                     } else if (i == 3) {
    1466            0 :                         CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
    1467            0 :                         while (CurWeekDay > 7) {
    1468            0 :                             CurWeekDay -= 7;
    1469              :                         }
    1470            0 :                         WeekDays(i) = CurWeekDay;
    1471              :                     } else {
    1472            0 :                         CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
    1473            0 :                         while (CurWeekDay > 7) {
    1474            0 :                             CurWeekDay -= 7;
    1475              :                         }
    1476            0 :                         WeekDays(i) = CurWeekDay;
    1477              :                     }
    1478              :                 }
    1479              : 
    1480            0 :                 if (any_eq(WeekDays, 0)) {
    1481              :                     // need to start at StMon and go backwards.
    1482              :                     // EndDayOfMonth is also "days" in month.  (without leapyear day in February)
    1483            0 :                     CurWeekDay = WeekDays(StartMonth);
    1484            0 :                     for (int i = 1; i <= StartMonthDay - 1; ++i) {
    1485            0 :                         --CurWeekDay;
    1486            0 :                         if (CurWeekDay == 0) {
    1487            0 :                             CurWeekDay = 7;
    1488              :                         }
    1489              :                     }
    1490              : 
    1491            0 :                     for (int i = StartMonth - 1; i >= 1; --i) {
    1492              : 
    1493            0 :                         if (i == 1) {
    1494            0 :                             CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
    1495            0 :                             while (CurWeekDay <= 0) {
    1496            0 :                                 CurWeekDay += 7;
    1497              :                             }
    1498            0 :                             WeekDays(i) = CurWeekDay;
    1499            0 :                         } else if (i == 2) {
    1500            0 :                             CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
    1501            0 :                             while (CurWeekDay <= 0) {
    1502            0 :                                 CurWeekDay += 7;
    1503              :                             }
    1504            0 :                             WeekDays(i) = CurWeekDay;
    1505            0 :                         } else if ((i >= 3) && (i <= 12)) {
    1506            0 :                             CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
    1507            0 :                             while (CurWeekDay <= 0) {
    1508            0 :                                 CurWeekDay += 7;
    1509              :                             }
    1510            0 :                             WeekDays(i) = CurWeekDay;
    1511              :                         }
    1512              :                     }
    1513              :                 }
    1514              :             }
    1515              :         }
    1516            0 :     }
    1517              : 
    1518            0 :     void SetDSTDateRanges(EnergyPlusData &state,
    1519              :                           Array1D_int const &MonWeekDay, // Weekday of each day 1 of month
    1520              :                           Array1D_int &DSTIdx,           // DST Index for each julian day (1:366)
    1521              :                           ObjexxFCL::Optional_int DSTActStMon,
    1522              :                           ObjexxFCL::Optional_int DSTActStDay,
    1523              :                           ObjexxFCL::Optional_int DSTActEnMon,
    1524              :                           ObjexxFCL::Optional_int DSTActEnDay)
    1525              :     {
    1526              : 
    1527              :         // SUBROUTINE INFORMATION:
    1528              :         //       AUTHOR         Linda Lawrie
    1529              :         //       DATE WRITTEN   March 2012
    1530              : 
    1531              :         // PURPOSE OF THIS SUBROUTINE:
    1532              :         // With multiple year weather files (or repeating weather files that rollover day),
    1533              :         // need to set DST (Daylight Saving Time) dates at start of environment or year.
    1534              :         // DST is only projected for one year.
    1535              : 
    1536              :         static constexpr std::string_view RoutineName("SetDSTDateRanges: ");
    1537              : 
    1538              :         int ActStartMonth; // Actual Start Month
    1539              :         int ActStartDay;   // Actual Start Day of Month
    1540              :         int ActEndMonth;   // Actual End Month
    1541              :         int ActEndDay;     // Actual End Day of Month
    1542              : 
    1543            0 :         bool ErrorsFound = false;
    1544            0 :         if (state.dataWeather->DST.StDateType == DateType::MonthDay) {
    1545            0 :             ActStartMonth = state.dataWeather->DST.StMon;
    1546            0 :             ActStartDay = state.dataWeather->DST.StDay;
    1547            0 :         } else if (state.dataWeather->DST.StDateType == DateType::NthDayInMonth) {
    1548            0 :             int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
    1549            0 :             while (ThisDay <= 0) {
    1550            0 :                 ThisDay += 7;
    1551              :             }
    1552            0 :             ThisDay += 7 * (state.dataWeather->DST.StDay - 1);
    1553            0 :             if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
    1554            0 :                 ShowSevereError(state, format("{}Determining DST: DST Start Date, Nth Day of Month, not enough Nths", RoutineName));
    1555            0 :                 ErrorsFound = true;
    1556              :             } else {
    1557            0 :                 ActStartMonth = state.dataWeather->DST.StMon;
    1558            0 :                 ActStartDay = ThisDay;
    1559              :             }
    1560              :         } else { // LastWeekDayInMonth
    1561            0 :             int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
    1562            0 :             while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
    1563            0 :                 ThisDay += 7;
    1564              :             }
    1565            0 :             ActStartMonth = state.dataWeather->DST.StMon;
    1566            0 :             ActStartDay = ThisDay;
    1567              :         }
    1568              : 
    1569            0 :         if (state.dataWeather->DST.EnDateType == DateType::MonthDay) {
    1570            0 :             ActEndMonth = state.dataWeather->DST.EnMon;
    1571            0 :             ActEndDay = state.dataWeather->DST.EnDay;
    1572            0 :         } else if (state.dataWeather->DST.EnDateType == DateType::NthDayInMonth) {
    1573            0 :             int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
    1574            0 :             while (ThisDay <= 0) {
    1575            0 :                 ThisDay += 7;
    1576              :             }
    1577            0 :             ThisDay += 7 * (state.dataWeather->DST.EnDay - 1);
    1578            0 :             if (ThisDay >> state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
    1579            0 :                 ActEndMonth = 0; // Suppress uninitialized warning
    1580            0 :                 ActEndDay = 0;   // Suppress uninitialized warning
    1581            0 :                 ShowSevereError(state, format("{}Determining DST: DST End Date, Nth Day of Month, not enough Nths", RoutineName));
    1582            0 :                 ErrorsFound = true;
    1583              :             } else {
    1584            0 :                 ActEndMonth = state.dataWeather->DST.EnMon;
    1585            0 :                 ActEndDay = ThisDay;
    1586              :             }
    1587              :         } else { // LastWeekDayInMonth
    1588            0 :             int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
    1589            0 :             while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
    1590            0 :                 ThisDay += 7;
    1591              :             }
    1592            0 :             ActEndMonth = state.dataWeather->DST.EnMon;
    1593            0 :             ActEndDay = ThisDay;
    1594              :         }
    1595              : 
    1596            0 :         if (ErrorsFound) {
    1597            0 :             ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
    1598              :         }
    1599              : 
    1600            0 :         if (present(DSTActStMon)) {
    1601            0 :             DSTActStMon = ActStartMonth;
    1602            0 :             DSTActStDay = ActStartDay;
    1603            0 :             DSTActEnMon = ActEndMonth;
    1604            0 :             DSTActEnDay = ActEndDay;
    1605              :         }
    1606              : 
    1607            0 :         DSTIdx = 0;
    1608            0 :         int JDay = General::OrdinalDay(ActStartMonth, ActStartDay, state.dataWeather->LeapYearAdd);
    1609            0 :         int JDay1 = General::OrdinalDay(ActEndMonth, ActEndDay, state.dataWeather->LeapYearAdd);
    1610            0 :         if (JDay1 >= JDay) {
    1611            0 :             DSTIdx({JDay, JDay1}) = 1;
    1612              :         } else {
    1613            0 :             DSTIdx({JDay, 366}) = 1;
    1614            0 :             DSTIdx({1, JDay1}) = 1;
    1615              :         }
    1616            0 :     }
    1617              : 
    1618            9 :     void SetSpecialDayDates(EnergyPlusData &state, Array1D_int const &MonWeekDay) // Weekday of each day 1 of month
    1619              :     {
    1620              : 
    1621              :         // SUBROUTINE INFORMATION:
    1622              :         //       AUTHOR         Linda Lawrie
    1623              :         //       DATE WRITTEN   March 2012
    1624              : 
    1625              :         // PURPOSE OF THIS SUBROUTINE:
    1626              :         // With multiple year weather files (or repeating weather files that rollover day),
    1627              :         // need to set Special Day dates at start of environment or year.
    1628              :         // Special Days are only projected for one year.
    1629              : 
    1630              :         static constexpr std::string_view RoutineName("SetSpecialDayDates: ");
    1631              : 
    1632              :         int JDay;
    1633              : 
    1634            9 :         bool ErrorsFound = false;
    1635            9 :         state.dataWeather->SpecialDayTypes = 0;
    1636            9 :         for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
    1637            0 :             auto &specialDay = state.dataWeather->SpecialDays(i);
    1638            0 :             if (specialDay.WthrFile && !state.dataWeather->UseSpecialDays) {
    1639            0 :                 continue;
    1640              :             }
    1641            0 :             if (specialDay.dateType <= DateType::MonthDay) {
    1642            0 :                 JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd);
    1643            0 :                 if (specialDay.Duration == 1 && state.dataWeather->Environment(state.dataWeather->Envrn).ApplyWeekendRule) {
    1644            0 :                     if (state.dataWeather->WeekDayTypes(JDay) == static_cast<int>(Sched::DayType::Sunday)) {
    1645              :                         // Sunday, must go to Monday
    1646            0 :                         ++JDay;
    1647            0 :                         if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
    1648            0 :                             JDay = 1;
    1649              :                         }
    1650            0 :                     } else if (state.dataWeather->WeekDayTypes(JDay) == (int)Sched::DayType::Saturday) {
    1651            0 :                         ++JDay;
    1652            0 :                         if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
    1653            0 :                             JDay = 1;
    1654              :                         }
    1655            0 :                         ++JDay;
    1656            0 :                         if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
    1657            0 :                             JDay = 1;
    1658              :                         }
    1659              :                     }
    1660              :                 }
    1661            0 :                 General::InvOrdinalDay(JDay, specialDay.ActStMon, specialDay.ActStDay, state.dataWeather->LeapYearAdd);
    1662            0 :             } else if (specialDay.dateType == DateType::NthDayInMonth) {
    1663            0 :                 int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
    1664            0 :                 if (specialDay.WeekDay < MonWeekDay(specialDay.Month)) {
    1665            0 :                     ThisDay += 7;
    1666              :                 }
    1667            0 :                 ThisDay += 7 * (specialDay.Day - 1);
    1668            0 :                 if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
    1669            0 :                     ShowSevereError(state,
    1670            0 :                                     format("{}Special Day Date, Nth Day of Month, not enough Nths, for SpecialDay={}", RoutineName, specialDay.Name));
    1671            0 :                     ErrorsFound = true;
    1672            0 :                     continue;
    1673              :                 }
    1674            0 :                 specialDay.ActStMon = specialDay.Month;
    1675            0 :                 specialDay.ActStDay = ThisDay;
    1676            0 :                 JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
    1677              :             } else { // LastWeekDayInMonth
    1678            0 :                 int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
    1679            0 :                 while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
    1680            0 :                     ThisDay += 7;
    1681              :                 }
    1682            0 :                 specialDay.ActStMon = specialDay.Month;
    1683            0 :                 specialDay.ActStDay = ThisDay;
    1684            0 :                 JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
    1685              :             }
    1686            0 :             if (state.dataWeather->SpecialDayTypes(JDay) != 0) {
    1687            0 :                 ShowWarningError(
    1688              :                     state,
    1689            0 :                     format("{}Special Day definition ({}) is overwriting previously entered special day period", RoutineName, specialDay.Name));
    1690            0 :                 if (state.dataWeather->UseSpecialDays) {
    1691            0 :                     ShowContinueError(state, "...This could be caused by definitions on the Weather File.");
    1692              :                 }
    1693            0 :                 ShowContinueError(state, "...This could be caused by duplicate definitions in the Input File.");
    1694              :             }
    1695            0 :             int JDay1 = JDay - 1;
    1696            0 :             for (int j = 0; j <= specialDay.Duration - 1; ++j) {
    1697            0 :                 ++JDay1;
    1698            0 :                 if (JDay1 == 366 && state.dataWeather->LeapYearAdd == 0) {
    1699            0 :                     JDay1 = 1;
    1700              :                 }
    1701            0 :                 if (JDay1 == 367) {
    1702            0 :                     JDay1 = 1;
    1703              :                 }
    1704            0 :                 state.dataWeather->SpecialDayTypes(JDay1) = specialDay.DayType;
    1705              :             }
    1706              :         }
    1707              : 
    1708            9 :         if (ErrorsFound) {
    1709            0 :             ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
    1710              :         }
    1711            9 :     }
    1712              : 
    1713       326115 :     void InitializeWeather(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
    1714              :     {
    1715              : 
    1716              :         // SUBROUTINE INFORMATION:
    1717              :         //       AUTHOR         Rick Strand
    1718              :         //       DATE WRITTEN   June 1997
    1719              : 
    1720              :         // PURPOSE OF THIS SUBROUTINE:
    1721              :         // This subroutine is the main driver of the weather initializations.
    1722              :         // Most of the weather handling can be described as "initializations"
    1723              :         // so most of the work is done via this subroutine.
    1724              : 
    1725       326115 :         if (state.dataGlobal->BeginSimFlag && state.dataWeather->FirstCall) {
    1726              : 
    1727          103 :             state.dataWeather->FirstCall = false;
    1728          103 :             state.dataEnvrn->EndMonthFlag = false;
    1729              : 
    1730              :         } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
    1731              : 
    1732       326115 :         auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
    1733       326115 :         if (state.dataGlobal->BeginEnvrnFlag) {
    1734              : 
    1735              :             // Call and setup the Design Day environment
    1736          538 :             if (envCurr.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
    1737          518 :                 if (envCurr.DesignDayNum > 0) {
    1738          516 :                     SetUpDesignDay(state, envCurr.DesignDayNum);
    1739          516 :                     state.dataEnvrn->EnvironmentName = envCurr.Title;
    1740              :                 }
    1741              :             }
    1742              : 
    1743              :             // Only used in Weather file environments
    1744              :             // Start over missing values with each environment
    1745          538 :             state.dataWeather->wvarsMissing.OutBaroPress = state.dataEnvrn->StdBaroPress; // Initial "missing" value
    1746          538 :             state.dataWeather->wvarsMissing.OutDryBulbTemp = 6.0;                         // Initial "missing" value
    1747          538 :             state.dataWeather->wvarsMissing.OutDewPointTemp = 3.0;                        // Initial "missing" value
    1748          538 :             state.dataWeather->wvarsMissing.OutRelHum = 50.0;                             // Initial "missing" value
    1749          538 :             state.dataWeather->wvarsMissing.WindSpeed = 2.5;                              // Initial "missing" value
    1750          538 :             state.dataWeather->wvarsMissing.WindDir = 180;                                // Initial "missing" value
    1751          538 :             state.dataWeather->wvarsMissing.TotalSkyCover = 5;                            // Initial "missing" value
    1752          538 :             state.dataWeather->wvarsMissing.OpaqueSkyCover = 5;                           // Initial "missing" value
    1753          538 :             state.dataWeather->wvarsMissing.Visibility = 777.7;                           // Initial "missing" value
    1754          538 :             state.dataWeather->wvarsMissing.Ceiling = 77777;                              // Initial "missing" value
    1755          538 :             state.dataWeather->wvarsMissing.AerOptDepth = 0.0;                            // Initial "missing" value
    1756          538 :             state.dataWeather->wvarsMissing.SnowDepth = 0;                                // Initial "missing" value
    1757          538 :             state.dataWeather->wvarsMissing.DaysLastSnow = 88;                            // Initial "missing" value
    1758          538 :             state.dataWeather->wvarsMissing.Albedo = 0.0;                                 // Initial "missing" value
    1759          538 :             state.dataWeather->wvarsMissing.LiquidPrecip = 0.0;                           // Initial "missing" value
    1760              :             // Counts set to 0 for each environment
    1761          538 :             state.dataWeather->wvarsMissedCounts = Weather::WeatherVarCounts();
    1762              : 
    1763              :             // Counts set to 0 for each environment
    1764          538 :             state.dataWeather->wvarsOutOfRangeCounts = Weather::WeatherVarCounts();
    1765              : 
    1766          538 :             state.dataWeather->IsRainThreshold = 0.8 / double(state.dataGlobal->TimeStepsInHour); // [mm]
    1767              : 
    1768          538 :             if (!state.dataWeather->RPReadAllWeatherData) {
    1769          536 :                 printEnvrnStamp = true; // Set this to true so that on first non-warmup day (only) the environment header will print out
    1770              :             }
    1771              : 
    1772          538 :             for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
    1773            0 :                 state.dataWeather->SpecialDays(i).Used = false;
    1774              :             }
    1775              : 
    1776          560 :             if ((state.dataGlobal->KindOfSim != Constant::KindOfSim::DesignDay) &&
    1777           22 :                 (state.dataGlobal->KindOfSim != Constant::KindOfSim::HVACSizeDesignDay)) {
    1778           22 :                 ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read first day's weather
    1779              :             } else {
    1780          516 :                 state.dataWeather->TomorrowVariables = state.dataWeather->DesignDay(envCurr.DesignDayNum);
    1781              :             }
    1782              : 
    1783              :         } // ... end of DataGlobals::BeginEnvrnFlag IF-THEN block.
    1784              : 
    1785       326115 :         if (state.dataGlobal->BeginDayFlag) {
    1786              : 
    1787              :             // Check Holidays, Daylight Saving Time, Ground Temperatures, etc.
    1788              : 
    1789         3083 :             UpdateWeatherData(state); // Update daily weather info
    1790              : 
    1791              :             // Read tomorrow's weather only if necessary.  This means that the
    1792              :             // simulation is out of warmup, is using a weather tape for this
    1793              :             // environment, and is not on the last day (day after last day is
    1794              :             // assumed to be equal to last day).
    1795              : 
    1796              :             // Following code checks whether the present day of simulation matches the start month and start day.
    1797              :             // In a multi year simulation with run period less than 365, we need to position the weather line
    1798              :             // appropriately.
    1799              : 
    1800         4084 :             if ((!state.dataGlobal->WarmupFlag) &&
    1801         1001 :                 ((envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) && (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay))) {
    1802          730 :                 if (state.dataGlobal->DayOfSim < state.dataGlobal->NumOfDayInEnvrn) {
    1803          728 :                     if (state.dataGlobal->DayOfSim == state.dataWeather->curSimDayForEndOfRunPeriod) {
    1804            1 :                         state.dataWeather->curSimDayForEndOfRunPeriod += envCurr.RawSimDays;
    1805            1 :                         if (state.dataWeather->StartDatesCycleShouldBeReset) {
    1806            0 :                             ResetWeekDaysByMonth(state,
    1807            0 :                                                  envCurr.MonWeekDay,
    1808            0 :                                                  state.dataWeather->LeapYearAdd,
    1809              :                                                  envCurr.StartMonth,
    1810              :                                                  envCurr.StartDay,
    1811              :                                                  envCurr.EndMonth,
    1812              :                                                  envCurr.EndDay,
    1813            0 :                                                  envCurr.RollDayTypeOnRepeat);
    1814            0 :                             if (state.dataWeather->DaylightSavingIsActive) {
    1815            0 :                                 SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
    1816              :                             }
    1817            0 :                             SetSpecialDayDates(state, envCurr.MonWeekDay);
    1818              :                         }
    1819            1 :                         ++state.dataWeather->YearOfSim;
    1820            1 :                         ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
    1821              :                     } else {
    1822          727 :                         ReadWeatherForDay(state, state.dataGlobal->DayOfSim + 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
    1823              :                     }
    1824              :                 }
    1825              :             }
    1826              : 
    1827         3083 :             state.dataEnvrn->EndYearFlag = false;
    1828         3083 :             if (state.dataEnvrn->DayOfMonth == state.dataWeather->EndDayOfMonthWithLeapDay(state.dataEnvrn->Month)) {
    1829           24 :                 state.dataEnvrn->EndMonthFlag = true;
    1830           24 :                 state.dataEnvrn->EndYearFlag = (state.dataEnvrn->Month == 12);
    1831              :             }
    1832              : 
    1833              :             // Set Tomorrow's date data
    1834         3083 :             state.dataEnvrn->MonthTomorrow = state.dataWeather->TomorrowVariables.Month;
    1835         3083 :             state.dataEnvrn->DayOfMonthTomorrow = state.dataWeather->TomorrowVariables.DayOfMonth;
    1836         3083 :             state.dataEnvrn->DayOfWeekTomorrow = state.dataWeather->TomorrowVariables.DayOfWeek;
    1837         3083 :             state.dataEnvrn->HolidayIndexTomorrow = state.dataWeather->TomorrowVariables.HolidayIndex;
    1838         3083 :             state.dataEnvrn->YearTomorrow = state.dataWeather->TomorrowVariables.Year;
    1839              : 
    1840         3083 :             if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
    1841           20 :                 if (state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1 && envCurr.ActualWeather) {
    1842            0 :                     if (state.dataWeather->DatesShouldBeReset) {
    1843            0 :                         if (envCurr.TreatYearsAsConsecutive) {
    1844            0 :                             ++envCurr.CurrentYear;
    1845            0 :                             envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
    1846            0 :                             state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
    1847            0 :                             state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
    1848              : 
    1849              :                             // need to reset MonWeekDay and WeekDayTypes
    1850            0 :                             int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
    1851            0 :                             int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
    1852            0 :                             if (!envCurr.ActualWeather) {
    1853            0 :                                 state.dataWeather->curSimDayForEndOfRunPeriod =
    1854            0 :                                     state.dataGlobal->DayOfSim + envCurr.RawSimDays + state.dataWeather->LeapYearAdd - 1;
    1855              :                             }
    1856              : 
    1857              :                             {
    1858            0 :                                 int i = JDay5Start;
    1859            0 :                                 int TWeekDay = state.dataEnvrn->DayOfWeek;
    1860              :                                 while (true) {
    1861            0 :                                     state.dataWeather->WeekDayTypes(i) = TWeekDay;
    1862            0 :                                     TWeekDay = mod(TWeekDay, 7) + 1;
    1863            0 :                                     ++i;
    1864            0 :                                     if (i > 366) {
    1865            0 :                                         i = 1;
    1866              :                                     }
    1867            0 :                                     if (i == JDay5End) {
    1868            0 :                                         break;
    1869              :                                     }
    1870              :                                 }
    1871              :                             }
    1872            0 :                             ResetWeekDaysByMonth(state,
    1873            0 :                                                  envCurr.MonWeekDay,
    1874            0 :                                                  state.dataWeather->LeapYearAdd,
    1875              :                                                  envCurr.StartMonth,
    1876              :                                                  envCurr.StartDay,
    1877              :                                                  envCurr.EndMonth,
    1878              :                                                  envCurr.EndDay,
    1879            0 :                                                  envCurr.RollDayTypeOnRepeat);
    1880            0 :                             if (state.dataWeather->DaylightSavingIsActive) {
    1881            0 :                                 SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
    1882              :                             }
    1883            0 :                             SetSpecialDayDates(state, envCurr.MonWeekDay);
    1884              :                         }
    1885              :                     }
    1886           20 :                 } else if ((state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1) && state.dataWeather->DatesShouldBeReset &&
    1887            0 :                            (state.dataWeather->Jan1DatesShouldBeReset)) {
    1888            0 :                     if (envCurr.TreatYearsAsConsecutive) {
    1889            0 :                         ++envCurr.CurrentYear;
    1890            0 :                         envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
    1891            0 :                         state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
    1892            0 :                         if (state.dataEnvrn->CurrentYearIsLeapYear && !state.dataWeather->WFAllowsLeapYears) {
    1893            0 :                             state.dataEnvrn->CurrentYearIsLeapYear = false;
    1894              :                         }
    1895            0 :                         if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod && state.dataEnvrn->CurrentYearIsLeapYear) {
    1896            0 :                             ++state.dataWeather->curSimDayForEndOfRunPeriod;
    1897              :                         }
    1898              :                     }
    1899              : 
    1900            0 :                     state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
    1901              : 
    1902            0 :                     if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod) {
    1903            0 :                         ResetWeekDaysByMonth(state,
    1904            0 :                                              envCurr.MonWeekDay,
    1905            0 :                                              state.dataWeather->LeapYearAdd,
    1906              :                                              envCurr.StartMonth,
    1907              :                                              envCurr.StartDay,
    1908              :                                              envCurr.EndMonth,
    1909              :                                              envCurr.EndDay,
    1910            0 :                                              envCurr.RollDayTypeOnRepeat,
    1911            0 :                                              envCurr.RollDayTypeOnRepeat || state.dataEnvrn->CurrentYearIsLeapYear);
    1912            0 :                         if (state.dataWeather->DaylightSavingIsActive) {
    1913            0 :                             SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
    1914              :                         }
    1915            0 :                         SetSpecialDayDates(state, envCurr.MonWeekDay);
    1916              :                     }
    1917              :                 }
    1918              :             }
    1919              : 
    1920              :             // at the end of each day find the min/max weather used for DOAS sizing
    1921         3083 :             if (state.dataGlobal->AirLoopHVACDOASUsedInSim) {
    1922           68 :                 if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign || envCurr.KindOfEnvrn == Constant::KindOfSim::DesignDay) {
    1923         1700 :                     for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
    1924        11424 :                         for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
    1925         9792 :                             Real64 Tdb = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDryBulbTemp;
    1926         9792 :                             Real64 Tdp = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDewPointTemp;
    1927         9792 :                             if (Tdb > envCurr.maxCoolingOATSizing) {
    1928           84 :                                 envCurr.maxCoolingOATSizing = Tdb;
    1929           84 :                                 envCurr.maxCoolingOADPSizing = Tdp;
    1930              :                             }
    1931         9792 :                             if (Tdb < envCurr.minHeatingOATSizing) {
    1932           62 :                                 envCurr.minHeatingOATSizing = Tdb;
    1933           62 :                                 envCurr.minHeatingOADPSizing = Tdp;
    1934              :                             }
    1935              :                         } // for (iTS)
    1936              :                     } // for (iHr)
    1937              :                 }
    1938              :             }
    1939              : 
    1940              :         } // ... end of DataGlobals::BeginDayFlag IF-THEN block.
    1941              : 
    1942       649147 :         if (!state.dataGlobal->BeginDayFlag && !state.dataGlobal->WarmupFlag &&
    1943       103763 :             (state.dataEnvrn->Month != envCurr.StartMonth || state.dataEnvrn->DayOfMonth != envCurr.StartDay) &&
    1944       649147 :             !state.dataWeather->DatesShouldBeReset && envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
    1945            0 :             state.dataWeather->DatesShouldBeReset = true;
    1946              :         }
    1947              : 
    1948       326137 :         if (state.dataGlobal->EndEnvrnFlag && (envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
    1949           22 :             (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
    1950           22 :             state.files.inputWeatherFile.rewind();
    1951           22 :             SkipEPlusWFHeader(state);
    1952           22 :             ReportMissing_RangeData(state);
    1953              :         }
    1954              : 
    1955              :         // set the EndDesignDayEnvrnsFlag (dataGlobal)
    1956              :         // True at the end of the last design day environment (last time step of last hour of last day of environ which is a design day)
    1957       326115 :         state.dataGlobal->EndDesignDayEnvrnsFlag = false;
    1958       326115 :         if (state.dataGlobal->EndEnvrnFlag) {
    1959          535 :             if (state.dataWeather->Envrn < state.dataWeather->NumOfEnvrn) {
    1960          341 :                 if (envCurr.KindOfEnvrn != state.dataWeather->Environment(state.dataWeather->Envrn + 1).KindOfEnvrn) {
    1961          116 :                     state.dataGlobal->EndDesignDayEnvrnsFlag = true;
    1962              :                 }
    1963              :             } else {
    1964              :                 // if the last environment set the flag to true.
    1965          194 :                 state.dataGlobal->EndDesignDayEnvrnsFlag = true;
    1966              :             }
    1967              :         }
    1968              : 
    1969       326115 :         if (state.dataWeather->WaterMainsParameterReport) {
    1970              :             // this is done only once
    1971          103 :             if (state.dataWeather->WaterMainsTempsMethod == WaterMainsTempCalcMethod::CorrelationFromWeatherFile) {
    1972            0 :                 if (!state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
    1973            0 :                     state.dataWeather->OADryBulbAverage.CalcAnnualAndMonthlyDryBulbTemp(state);
    1974              :                 }
    1975              :             }
    1976              :             // reports to eio file
    1977          103 :             ReportWaterMainsTempParameters(state);
    1978          103 :             state.dataWeather->WaterMainsParameterReport = false;
    1979              :         }
    1980       326115 :     }
    1981              : 
    1982         3083 :     void UpdateWeatherData(EnergyPlusData &state)
    1983              :     {
    1984              : 
    1985              :         // SUBROUTINE INFORMATION:
    1986              :         //       AUTHOR         Rick Strand
    1987              :         //       DATE WRITTEN   June 1997
    1988              : 
    1989              :         // PURPOSE OF THIS SUBROUTINE:
    1990              :         // This subroutine updates all of the daily weather data in the local
    1991              :         // module level variables and the global variables.
    1992              :         // This subroutine will temporarily transfer the weather data for the
    1993              :         // current day to the old data structure contained in envdat.inc until
    1994              :         // enough reengineering has taken place to eliminate the need for this
    1995              :         // include.
    1996              : 
    1997         3083 :         state.dataWeather->TodayVariables = state.dataWeather->TomorrowVariables; // Transfer Tomorrow's Daily Weather Variables to Today
    1998              : 
    1999         3083 :         if (state.dataGlobal->BeginEnvrnFlag) {
    2000          538 :             state.dataGlobal->PreviousHour = 24;
    2001              :         }
    2002              : 
    2003         3083 :         state.dataWeather->wvarsHrTsToday = state.dataWeather->wvarsHrTsTomorrow; // What a waste
    2004              : 
    2005              :         // Update Global Data
    2006              : 
    2007         3083 :         state.dataEnvrn->DayOfYear = state.dataWeather->TodayVariables.DayOfYear;
    2008         3083 :         state.dataEnvrn->Year = state.dataWeather->TodayVariables.Year;
    2009         3083 :         state.dataEnvrn->Month = state.dataWeather->TodayVariables.Month;
    2010         3083 :         state.dataEnvrn->DayOfMonth = state.dataWeather->TodayVariables.DayOfMonth;
    2011         3083 :         state.dataEnvrn->DayOfWeek = state.dataWeather->TodayVariables.DayOfWeek;
    2012         3083 :         state.dataEnvrn->HolidayIndex = state.dataWeather->TodayVariables.HolidayIndex;
    2013         3083 :         if (state.dataEnvrn->HolidayIndex > 0) {
    2014         2333 :             state.dataWeather->RptDayType = state.dataEnvrn->HolidayIndex;
    2015              :         } else {
    2016          750 :             state.dataWeather->RptDayType = state.dataEnvrn->DayOfWeek;
    2017              :         }
    2018         3083 :         state.dataEnvrn->DSTIndicator = state.dataWeather->TodayVariables.DaylightSavingIndex;
    2019         3083 :         state.dataEnvrn->EquationOfTime = state.dataWeather->TodayVariables.EquationOfTime;
    2020         3083 :         state.dataEnvrn->CosSolarDeclinAngle = state.dataWeather->TodayVariables.CosSolarDeclinAngle;
    2021         3083 :         state.dataEnvrn->SinSolarDeclinAngle = state.dataWeather->TodayVariables.SinSolarDeclinAngle;
    2022         3083 :     }
    2023              : 
    2024       326120 :     void SetCurrentWeather(EnergyPlusData &state)
    2025              :     {
    2026              : 
    2027              :         // SUBROUTINE INFORMATION:
    2028              :         //       AUTHOR         Russ Taylor
    2029              :         //       DATE WRITTEN   March 1990
    2030              :         //       MODIFIED       Aug94 (LKL) Fixed improper weighting
    2031              :         //                      Nov98 (FCW) Added call to get exterior illuminances
    2032              :         //                      Jan02 (FCW) Changed how ground reflectance for daylighting is set
    2033              :         //                      Mar12 (LKL) Changed settings for leap years/ current years.
    2034              :         //       RE-ENGINEERED  Apr97,May97 (RKS)
    2035              : 
    2036              :         // PURPOSE OF THIS SUBROUTINE:
    2037              :         // The purpose of this subroutine is to interpolate the hourly
    2038              :         // environment data for the sub-hourly time steps in EnergyPlus.  In
    2039              :         // other words, this subroutine puts the current weather conditions
    2040              :         // into the proper variables.  Rather than using the same data for
    2041              :         // each time step, environment data is interpolated as a continuum
    2042              :         // throughout the day.
    2043              : 
    2044              :         // METHODOLOGY EMPLOYED:
    2045              :         // The current hour (DataGlobals::HourOfDay) as well as the next hour are used
    2046              :         // to come up with environment data per time step interval.  Method
    2047              :         // used is to assign a weighting for the current hour's data and
    2048              :         // (1-that weighting) to the next hour's data.  Actual method is:  if
    2049              :         // the current time step is 15 minutes into hour, the interpolated dry
    2050              :         // bulb temperature should be 3/4*dry bulb temperature of current hour
    2051              :         // and 1/4*dry bulb temperature of next environment hourly data.  At
    2052              :         // day boundary (current hour = 24), the next hour is hour 1 of next
    2053              :         // weather data day (Tomorrow%).
    2054              : 
    2055              :         static constexpr std::string_view RoutineName("SetCurrentWeather");
    2056              : 
    2057       326120 :         state.dataWeather->NextHour = state.dataGlobal->HourOfDay + 1;
    2058              : 
    2059       326120 :         if (state.dataGlobal->HourOfDay == 24) { // Should investigate whether EndDayFlag is always set here and use that instead
    2060        13821 :             state.dataWeather->NextHour = 1;
    2061              :         }
    2062              : 
    2063       326120 :         if (state.dataGlobal->HourOfDay == 1) { // Should investigate whether DataGlobals::BeginDayFlag is always set here and use that instead
    2064        14085 :             state.dataEnvrn->DayOfYear_Schedule = General::OrdinalDay(state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, 1);
    2065              :         }
    2066              : 
    2067       326120 :         Sched::UpdateScheduleVals(state);
    2068              : 
    2069       326120 :         state.dataEnvrn->CurMnDyHr =
    2070       652240 :             format("{:02d}/{:02d} {:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, (unsigned short)(state.dataGlobal->HourOfDay - 1));
    2071       326120 :         state.dataEnvrn->CurMnDy = format("{:02d}/{:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth);
    2072       326120 :         state.dataEnvrn->CurMnDyYr =
    2073       652240 :             format("{:02d}/{:02d}/{:04d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->CalendarYear);
    2074              : 
    2075       326120 :         state.dataGlobal->WeightNow = state.dataWeather->Interpolation(state.dataGlobal->TimeStep);
    2076       326120 :         state.dataGlobal->WeightPreviousHour = 1.0 - state.dataGlobal->WeightNow;
    2077              : 
    2078       326120 :         state.dataGlobal->CurrentTime = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * (state.dataWeather->TimeStepFraction);
    2079       326120 :         state.dataGlobal->SimTimeSteps = (state.dataGlobal->DayOfSim - 1) * 24 * state.dataGlobal->TimeStepsInHour +
    2080       326120 :                                          (state.dataGlobal->HourOfDay - 1) * state.dataGlobal->TimeStepsInHour + state.dataGlobal->TimeStep;
    2081              : 
    2082       326120 :         state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] =
    2083       326120 :             state.dataWeather->siteBuildingSurfaceGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
    2084       326120 :         state.dataEnvrn->GroundTempKelvin = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] + Constant::Kelvin;
    2085       326120 :         state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod] =
    2086       326120 :             state.dataWeather->siteFCFactorMethodGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
    2087       326120 :         state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow] =
    2088       326120 :             state.dataWeather->siteShallowGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
    2089       326120 :         state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep] =
    2090       326120 :             state.dataWeather->siteDeepGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
    2091       326120 :         state.dataEnvrn->GndReflectance = state.dataWeather->GroundReflectances(state.dataEnvrn->Month);
    2092       326120 :         state.dataEnvrn->GndReflectanceForDayltg = state.dataEnvrn->GndReflectance;
    2093              : 
    2094       326120 :         CalcWaterMainsTemp(state);
    2095              : 
    2096              :         // Determine if Sun is up or down, set Solar Cosine values for time step.
    2097       326120 :         DetermineSunUpDown(state, state.dataEnvrn->SOLCOS);
    2098       326120 :         if (state.dataEnvrn->SunIsUp && state.dataWeather->SolarAltitudeAngle < 0.0) {
    2099            0 :             ShowFatalError(state, format("SetCurrentWeather: At {} Sun is Up but Solar Altitude Angle is < 0.0", state.dataEnvrn->CurMnDyHr));
    2100              :         }
    2101              : 
    2102       326120 :         auto const &today = state.dataWeather->wvarsHrTsToday(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
    2103       326120 :         state.dataEnvrn->OutDryBulbTemp = today.OutDryBulbTemp;
    2104       326120 :         if (state.dataEnvrn->EMSOutDryBulbOverrideOn) {
    2105            2 :             state.dataEnvrn->OutDryBulbTemp = state.dataEnvrn->EMSOutDryBulbOverrideValue;
    2106              :         }
    2107       326120 :         state.dataEnvrn->OutBaroPress = today.OutBaroPress;
    2108       326120 :         state.dataEnvrn->OutDewPointTemp = today.OutDewPointTemp;
    2109       326120 :         if (state.dataEnvrn->EMSOutDewPointTempOverrideOn) {
    2110         5760 :             state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->EMSOutDewPointTempOverrideValue;
    2111              :         }
    2112       326120 :         state.dataEnvrn->OutRelHum = today.OutRelHum;
    2113       326120 :         state.dataEnvrn->OutRelHumValue = state.dataEnvrn->OutRelHum / 100.0;
    2114       326120 :         if (state.dataEnvrn->EMSOutRelHumOverrideOn) {
    2115            2 :             state.dataEnvrn->OutRelHumValue = state.dataEnvrn->EMSOutRelHumOverrideValue / 100.0;
    2116            2 :             state.dataEnvrn->OutRelHum = state.dataEnvrn->EMSOutRelHumOverrideValue;
    2117              :         }
    2118              : 
    2119              :         // Humidity Ratio and Wet Bulb are derived
    2120       326120 :         state.dataEnvrn->OutHumRat = Psychrometrics::PsyWFnTdbRhPb(
    2121       326120 :             state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutRelHumValue, state.dataEnvrn->OutBaroPress, RoutineName);
    2122       652240 :         state.dataEnvrn->OutWetBulbTemp =
    2123       326120 :             Psychrometrics::PsyTwbFnTdbWPb(state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat, state.dataEnvrn->OutBaroPress);
    2124       326120 :         if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
    2125        29874 :             state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
    2126        59748 :             Real64 TempVal = Psychrometrics::PsyWFnTdbTwbPb(
    2127        29874 :                 state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutBaroPress);
    2128        29874 :             state.dataEnvrn->OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, TempVal, state.dataEnvrn->OutBaroPress);
    2129              :         }
    2130              : 
    2131       326120 :         if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
    2132        55473 :             state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
    2133              :         }
    2134              : 
    2135       396260 :         if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) ||
    2136        70140 :             (state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay)) {
    2137              : 
    2138       751458 :             for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
    2139       495478 :                 state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
    2140              :             }
    2141              : 
    2142       255980 :             auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
    2143       255980 :             int const envrnDayNum = envCurr.DesignDayNum;
    2144       255980 :             auto const &desDayInput = state.dataWeather->DesDayInput(envrnDayNum);
    2145       255980 :             auto &spSiteSchedule = state.dataWeather->spSiteSchedules(envrnDayNum);
    2146       255980 :             auto const &desDayMod = state.dataWeather->desDayMods(envrnDayNum)(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
    2147              : 
    2148       255980 :             if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Default) {
    2149            0 :                 spSiteSchedule.OutDryBulbTemp = desDayMod.OutDryBulbTemp;
    2150              :             }
    2151              : 
    2152       255980 :             if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
    2153       255977 :                 desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
    2154            3 :                 spSiteSchedule.OutRelHum = desDayMod.OutRelHum;
    2155              :             }
    2156       255980 :             if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
    2157            0 :                 spSiteSchedule.BeamSolarRad = desDayMod.BeamSolarRad;
    2158            0 :                 spSiteSchedule.DifSolarRad = desDayMod.DifSolarRad;
    2159              :             }
    2160              : 
    2161       255980 :             if (envCurr.skyTempModel == SkyTempModel::ScheduleValue || envCurr.skyTempModel == SkyTempModel::DryBulbDelta ||
    2162       255980 :                 envCurr.skyTempModel == SkyTempModel::DewPointDelta) {
    2163            0 :                 spSiteSchedule.SkyTemp = desDayMod.SkyTemp;
    2164              :             }
    2165        70140 :         } else if (state.dataEnvrn->TotDesDays > 0) {
    2166       105291 :             for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
    2167        70194 :                 state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
    2168              :             }
    2169              :         }
    2170              : 
    2171       326120 :         state.dataEnvrn->WindSpeed = today.WindSpeed;
    2172       326120 :         if (state.dataEnvrn->EMSWindSpeedOverrideOn) {
    2173            2 :             state.dataEnvrn->WindSpeed = state.dataEnvrn->EMSWindSpeedOverrideValue;
    2174              :         }
    2175       326120 :         state.dataEnvrn->WindDir = today.WindDir;
    2176       326120 :         if (state.dataEnvrn->EMSWindDirOverrideOn) {
    2177            2 :             state.dataEnvrn->WindDir = state.dataEnvrn->EMSWindDirOverrideValue;
    2178              :         }
    2179       326120 :         state.dataWeather->HorizIRSky = today.HorizIRSky;
    2180       326120 :         state.dataEnvrn->SkyTemp = today.SkyTemp;
    2181       326120 :         state.dataEnvrn->SkyTempKelvin = state.dataEnvrn->SkyTemp + Constant::Kelvin;
    2182       326120 :         state.dataEnvrn->DifSolarRad = today.DifSolarRad;
    2183       326120 :         if (state.dataEnvrn->EMSDifSolarRadOverrideOn) {
    2184            2 :             state.dataEnvrn->DifSolarRad = state.dataEnvrn->EMSDifSolarRadOverrideValue;
    2185              :         }
    2186       326120 :         state.dataEnvrn->BeamSolarRad = today.BeamSolarRad;
    2187       326120 :         if (state.dataEnvrn->EMSBeamSolarRadOverrideOn) {
    2188            2 :             state.dataEnvrn->BeamSolarRad = state.dataEnvrn->EMSBeamSolarRadOverrideValue;
    2189              :         }
    2190       326120 :         state.dataEnvrn->LiquidPrecipitation = today.LiquidPrecip / 1000.0; // convert from mm to m
    2191       326120 :         if ((state.dataEnvrn->RunPeriodEnvironment) && (!state.dataGlobal->WarmupFlag)) {
    2192        35042 :             int month = state.dataEnvrn->Month;
    2193        35042 :             state.dataWaterData->RainFall.MonthlyTotalPrecInWeather.at(month - 1) += state.dataEnvrn->LiquidPrecipitation * 1000.0;
    2194        35042 :             if ((state.dataEnvrn->LiquidPrecipitation > 0) && (state.dataGlobal->TimeStep == 1)) {
    2195           43 :                 state.dataWaterData->RainFall.numRainyHoursInWeather.at(month - 1) += 1;
    2196              :             }
    2197              :         }
    2198              : 
    2199       326120 :         WaterManager::UpdatePrecipitation(state);
    2200              : 
    2201       326120 :         state.dataEnvrn->TotalCloudCover = today.TotalSkyCover;
    2202       326120 :         state.dataEnvrn->OpaqueCloudCover = today.OpaqueSkyCover;
    2203              : 
    2204       326120 :         if (state.dataWeather->UseRainValues) {
    2205              :             // It is set as LiquidPrecipitation >= .8 mm here: state.dataWeather->TomorrowLiquidPrecip(ts, hour) >=
    2206              :             // state.dataWeather->IsRainThreshold;
    2207       326120 :             state.dataEnvrn->IsRain = today.IsRain;
    2208       326120 :             if (state.dataWaterData->RainFall.ModeID == DataWater::RainfallMode::RainSchedDesign && state.dataEnvrn->RunPeriodEnvironment) {
    2209              :                 // CurrentAmount unit: m
    2210            1 :                 state.dataEnvrn->IsRain = state.dataWaterData->RainFall.CurrentAmount >= (state.dataWeather->IsRainThreshold / 1000.0);
    2211              :             }
    2212              :         } else {
    2213            0 :             state.dataEnvrn->IsRain = false;
    2214              :         }
    2215       326120 :         if (state.dataWeather->UseSnowValues) {
    2216       326120 :             state.dataEnvrn->IsSnow = today.IsSnow;
    2217              :         } else {
    2218            0 :             state.dataEnvrn->IsSnow = false;
    2219              :         }
    2220              : 
    2221       326120 :         if (state.dataEnvrn->IsSnow) {
    2222            0 :             state.dataEnvrn->GndReflectance = max(min(state.dataEnvrn->GndReflectance * state.dataWeather->SnowGndRefModifier, 1.0), 0.0);
    2223            0 :             state.dataEnvrn->GndReflectanceForDayltg =
    2224            0 :                 max(min(state.dataEnvrn->GndReflectanceForDayltg * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0);
    2225              :         }
    2226              : 
    2227       326120 :         state.dataEnvrn->GndSolarRad =
    2228       326120 :             max((state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z + state.dataEnvrn->DifSolarRad) * state.dataEnvrn->GndReflectance, 0.0);
    2229              : 
    2230       326120 :         if (!state.dataEnvrn->SunIsUp) {
    2231       166429 :             state.dataEnvrn->DifSolarRad = 0.0;
    2232       166429 :             state.dataEnvrn->BeamSolarRad = 0.0;
    2233       166429 :             state.dataEnvrn->GndSolarRad = 0.0;
    2234              :         }
    2235              : 
    2236       326120 :         state.dataEnvrn->OutEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
    2237       652240 :         state.dataEnvrn->OutAirDensity =
    2238       326120 :             Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
    2239              : 
    2240       326120 :         if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
    2241            0 :             state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
    2242              :         }
    2243       326120 :         if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
    2244            0 :             state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
    2245              :         }
    2246              : 
    2247       326120 :         DayltgCurrentExtHorizIllum(state);
    2248              : 
    2249       326120 :         if (!state.dataEnvrn->IsRain) {
    2250       325913 :             state.dataWeather->RptIsRain = 0;
    2251              :         } else {
    2252          207 :             state.dataWeather->RptIsRain = 1;
    2253              :         }
    2254              : 
    2255       326120 :         if (!state.dataEnvrn->IsSnow) {
    2256       326120 :             state.dataWeather->RptIsSnow = 0;
    2257              :         } else {
    2258            0 :             state.dataWeather->RptIsSnow = 1;
    2259              :         }
    2260       326120 :     }
    2261              : 
    2262          755 :     void ReadWeatherForDay(EnergyPlusData &state,
    2263              :                            int const DayToRead,          // =1 when starting out, otherwise signifies next day
    2264              :                            int const Environ,            // Environment being simulated
    2265              :                            bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
    2266              :     )
    2267              :     {
    2268              : 
    2269              :         // SUBROUTINE INFORMATION:
    2270              :         //       AUTHOR         Linda K. Lawrie
    2271              :         //       DATE WRITTEN   April 1999
    2272              : 
    2273              :         // PURPOSE OF THIS SUBROUTINE:
    2274              :         // This subroutine is the driving routine behind reading the weather data.
    2275              :         // Theoretically, several kinds of weather files could be read here.  As
    2276              :         // distributed only EPW files are allowed.
    2277              : 
    2278          755 :         ReadEPlusWeatherForDay(state, DayToRead, Environ, BackSpaceAfterRead);
    2279          755 :     }
    2280              : 
    2281          755 :     void ReadEPlusWeatherForDay(EnergyPlusData &state,
    2282              :                                 int const DayToRead,          // =1 when starting out, otherwise signifies next day
    2283              :                                 int const Environ,            // Environment being simulated
    2284              :                                 bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
    2285              :     )
    2286              :     {
    2287              : 
    2288              :         // SUBROUTINE INFORMATION:
    2289              :         //       AUTHOR         Linda K. Lawrie
    2290              :         //       DATE WRITTEN   April 1999
    2291              :         //       MODIFIED       March 2012; add actual weather read.
    2292              : 
    2293              :         // PURPOSE OF THIS SUBROUTINE:
    2294              :         // This subroutine reads the appropriate day of EPW weather data.
    2295              : 
    2296              :         int WYear;
    2297              :         int WMonth;
    2298              :         int WDay;
    2299              :         int WHour;
    2300              :         int WMinute;
    2301              :         Real64 DryBulb;
    2302              :         Real64 DewPoint;
    2303              :         Real64 RelHum;
    2304              :         Real64 AtmPress;
    2305              :         Real64 ETHoriz;
    2306              :         Real64 ETDirect;
    2307              :         Real64 IRHoriz;
    2308              :         Real64 GLBHoriz;
    2309              :         Real64 DirectRad;
    2310              :         Real64 DiffuseRad;
    2311              :         Real64 GLBHorizIllum;
    2312              :         Real64 DirectNrmIllum;
    2313              :         Real64 DiffuseHorizIllum;
    2314              :         Real64 ZenLum;
    2315              :         Real64 WindDir;
    2316              :         Real64 WindSpeed;
    2317              :         Real64 TotalSkyCover;
    2318              :         Real64 OpaqueSkyCover;
    2319              :         Real64 Visibility;
    2320              :         Real64 CeilHeight;
    2321              :         Real64 PrecipWater;
    2322              :         Real64 AerosolOptDepth;
    2323              :         Real64 SnowDepth;
    2324              :         Real64 DaysSinceLastSnow;
    2325              :         Real64 Albedo;
    2326              :         Real64 LiquidPrecip;
    2327              :         int PresWeathObs;
    2328          755 :         Array1D_int PresWeathConds(9);
    2329              : 
    2330          755 :         constexpr std::string_view routineName = "ReadEPlusWeatherForDay";
    2331              : 
    2332          755 :         Array1D<WeatherVars> wvarsHr = Array1D<WeatherVars>(Constant::iHoursInDay);
    2333              : 
    2334          755 :         auto &thisEnviron = state.dataWeather->Environment(Environ);
    2335              : 
    2336          755 :         if (DayToRead == 1) {
    2337              : 
    2338              :             // Checks whether Weather file contains just one year of data. If yes then rewind and position to first
    2339              :             // day of weather file. The rest of code appropriately positions to the start day.
    2340              : 
    2341           27 :             bool Ready = false;
    2342           27 :             int NumRewinds = 0;
    2343              :             //     Must position file to proper day
    2344              :             //     File already position to first data record
    2345              :             //          Set Current Day of Week to "start of Data Period"
    2346           27 :             state.dataWeather->ReadEPlusWeatherCurTime = 1.0 / double(state.dataWeather->NumIntervalsPerHour);
    2347           27 :             state.dataWeather->CurDayOfWeek = state.dataWeather->DataPeriods(1).WeekDay - 1;
    2348           27 :             WYear = 0;
    2349           27 :             WMonth = 0;
    2350           27 :             WDay = 0;
    2351           27 :             WHour = 0;
    2352           27 :             WMinute = 0;
    2353           27 :             state.dataWeather->LastHourSet = false;
    2354           27 :             InputFile::ReadResult<std::string> WeatherDataLine{"", true, false};
    2355          752 :             while (!Ready) {
    2356          725 :                 WeatherDataLine.update(state.files.inputWeatherFile.readLine());
    2357          725 :                 if (WeatherDataLine.good) {
    2358              :                     bool ErrorFound;
    2359          724 :                     InterpretWeatherDataLine(state,
    2360              :                                              WeatherDataLine.data,
    2361              :                                              ErrorFound,
    2362              :                                              WYear,
    2363              :                                              WMonth,
    2364              :                                              WDay,
    2365              :                                              WHour,
    2366              :                                              WMinute,
    2367              :                                              DryBulb,
    2368              :                                              DewPoint,
    2369              :                                              RelHum,
    2370              :                                              AtmPress,
    2371              :                                              ETHoriz,
    2372              :                                              ETDirect,
    2373              :                                              IRHoriz,
    2374              :                                              GLBHoriz,
    2375              :                                              DirectRad,
    2376              :                                              DiffuseRad,
    2377              :                                              GLBHorizIllum,
    2378              :                                              DirectNrmIllum,
    2379              :                                              DiffuseHorizIllum,
    2380              :                                              ZenLum,
    2381              :                                              WindDir,
    2382              :                                              WindSpeed,
    2383              :                                              TotalSkyCover,
    2384              :                                              OpaqueSkyCover,
    2385              :                                              Visibility,
    2386              :                                              CeilHeight,
    2387              :                                              PresWeathObs,
    2388              :                                              PresWeathConds,
    2389              :                                              PrecipWater,
    2390              :                                              AerosolOptDepth,
    2391              :                                              SnowDepth,
    2392              :                                              DaysSinceLastSnow,
    2393              :                                              Albedo,
    2394              :                                              LiquidPrecip);
    2395            1 :                 } else if (WeatherDataLine.eof) {
    2396            1 :                     if (NumRewinds > 0) {
    2397            0 :                         std::string date = fmt::to_string(thisEnviron.StartMonth) + '/' + fmt::to_string(thisEnviron.StartDay);
    2398            0 :                         if (thisEnviron.MatchYear) {
    2399            0 :                             date += '/' + fmt::to_string(thisEnviron.StartYear);
    2400              :                         }
    2401            0 :                         ShowSevereError(state, format("Multiple rewinds on EPW while searching for first day {}", date));
    2402            0 :                     } else {
    2403            1 :                         state.files.inputWeatherFile.rewind();
    2404            1 :                         ++NumRewinds;
    2405            1 :                         SkipEPlusWFHeader(state);
    2406            1 :                         WeatherDataLine.update(state.files.inputWeatherFile.readLine());
    2407              :                         bool ErrorFound;
    2408            1 :                         InterpretWeatherDataLine(state,
    2409              :                                                  WeatherDataLine.data,
    2410              :                                                  ErrorFound,
    2411              :                                                  WYear,
    2412              :                                                  WMonth,
    2413              :                                                  WDay,
    2414              :                                                  WHour,
    2415              :                                                  WMinute,
    2416              :                                                  DryBulb,
    2417              :                                                  DewPoint,
    2418              :                                                  RelHum,
    2419              :                                                  AtmPress,
    2420              :                                                  ETHoriz,
    2421              :                                                  ETDirect,
    2422              :                                                  IRHoriz,
    2423              :                                                  GLBHoriz,
    2424              :                                                  DirectRad,
    2425              :                                                  DiffuseRad,
    2426              :                                                  GLBHorizIllum,
    2427              :                                                  DirectNrmIllum,
    2428              :                                                  DiffuseHorizIllum,
    2429              :                                                  ZenLum,
    2430              :                                                  WindDir,
    2431              :                                                  WindSpeed,
    2432              :                                                  TotalSkyCover,
    2433              :                                                  OpaqueSkyCover,
    2434              :                                                  Visibility,
    2435              :                                                  CeilHeight,
    2436              :                                                  PresWeathObs,
    2437              :                                                  PresWeathConds,
    2438              :                                                  PrecipWater,
    2439              :                                                  AerosolOptDepth,
    2440              :                                                  SnowDepth,
    2441              :                                                  DaysSinceLastSnow,
    2442              :                                                  Albedo,
    2443              :                                                  LiquidPrecip);
    2444              :                     }
    2445              :                 }
    2446          725 :                 if (!WeatherDataLine.good) {
    2447            0 :                     ShowFatalError(state,
    2448            0 :                                    format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
    2449              :                                           WYear,
    2450              :                                           WMonth,
    2451              :                                           WDay,
    2452              :                                           WHour,
    2453              :                                           WMinute,
    2454            0 :                                           state.files.inputWeatherFile.error_state_to_string()),
    2455            0 :                                    OptionalOutputFileRef{state.files.eso});
    2456              :                 }
    2457          725 :                 if (state.dataWeather->CurDayOfWeek <= 7) {
    2458          725 :                     state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
    2459              :                 }
    2460          725 :                 bool RecordDateMatch =
    2461         1423 :                     (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && !thisEnviron.MatchYear) ||
    2462          698 :                     (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && thisEnviron.MatchYear && WYear == thisEnviron.StartYear);
    2463          725 :                 if (RecordDateMatch) {
    2464           27 :                     state.files.inputWeatherFile.backspace();
    2465           27 :                     Ready = true;
    2466           27 :                     if (state.dataWeather->CurDayOfWeek <= 7) {
    2467           27 :                         --state.dataWeather->CurDayOfWeek;
    2468              :                     }
    2469              :                     // Do the range checks on the first set of fields -- no others.
    2470           27 :                     bool ErrorsFound = false;
    2471           27 :                     if (DryBulb < 99.9 && (DryBulb < -90.0 || DryBulb > 70.0)) {
    2472            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2473            0 :                         ShowContinueError(state, format("DryBulb Temperature ({:.2R}) is out of range [-90.0, 70.0]", DryBulb));
    2474            0 :                         ErrorsFound = true;
    2475              :                     }
    2476              : 
    2477           27 :                     if (DewPoint < 99.9 && (DewPoint < -90.0 || DewPoint > 70.0)) {
    2478            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2479            0 :                         ShowContinueError(state, format("DewPoint Temperature ({:.2R}) is out of range [-90.0, 70.0]", DewPoint));
    2480            0 :                         ErrorsFound = true;
    2481              :                     }
    2482              : 
    2483           27 :                     if (RelHum < 999.0 && (RelHum < 0.0 || RelHum > 110.0)) {
    2484            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2485            0 :                         ShowContinueError(state, format("Relative Humidity ({:.2R}) is out of range [0.0, 100.0]", RelHum));
    2486            0 :                         ErrorsFound = true;
    2487              :                     }
    2488              : 
    2489           27 :                     if (AtmPress < 999999.0 && (AtmPress <= 31000.0 || AtmPress > 120000.0)) {
    2490            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2491            0 :                         ShowContinueError(state, format("Atmospheric Pressure ({:.0R}) is out of range [31000, 120000]", AtmPress));
    2492            0 :                         ErrorsFound = true;
    2493              :                     }
    2494              : 
    2495           27 :                     if (DirectRad < 0.0) {
    2496            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2497            0 :                         ShowContinueError(state, format("Direct Radiation ({:.2R}) is out of range [0.0, -]", DirectRad));
    2498            0 :                         ErrorsFound = true;
    2499              :                     }
    2500              : 
    2501           27 :                     if (DiffuseRad < 0.0) {
    2502            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2503            0 :                         ShowContinueError(state, format("Diffuse Radiation ({:.2R}) is out of range [0.0, -]", DiffuseRad));
    2504            0 :                         ErrorsFound = true;
    2505              :                     }
    2506              : 
    2507           27 :                     if (WindDir < 999.0 && (WindDir < 0.0 || WindDir > 360.0)) {
    2508            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2509            0 :                         ShowContinueError(state, format("Wind Direction ({:.2R}) is out of range [0.0, 360.0]", WindDir));
    2510            0 :                         ErrorsFound = true;
    2511              :                     }
    2512              : 
    2513           27 :                     if (WindSpeed < 999.0 && (WindSpeed < 0.0 || WindSpeed > 40.0)) {
    2514            0 :                         ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
    2515            0 :                         ShowContinueError(state, format("Wind Speed ({:.2R}) is out of range [0.0, 40.0]", WindSpeed));
    2516            0 :                         ErrorsFound = true;
    2517              :                     }
    2518              : 
    2519           27 :                     if (ErrorsFound) {
    2520            0 :                         ShowSevereError(state, "Out of Range errors found with initial day of WeatherFile");
    2521              :                     }
    2522              :                 } else {
    2523              :                     //  Must skip this day
    2524          698 :                     for (int i = 2; i <= state.dataWeather->NumIntervalsPerHour; ++i) {
    2525            0 :                         WeatherDataLine.update(state.files.inputWeatherFile.readLine());
    2526            0 :                         if (!WeatherDataLine.good) {
    2527            0 :                             readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
    2528            0 :                             ShowFatalError(state,
    2529            0 :                                            format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
    2530              :                                                   WYear,
    2531              :                                                   WMonth,
    2532              :                                                   WDay,
    2533              :                                                   WHour,
    2534              :                                                   WMinute,
    2535            0 :                                                   state.files.inputWeatherFile.error_state_to_string()),
    2536            0 :                                            OptionalOutputFileRef{state.files.eso});
    2537              :                         }
    2538              :                     }
    2539        16752 :                     for (int i = 1; i <= 23 * state.dataWeather->NumIntervalsPerHour; ++i) {
    2540        16054 :                         WeatherDataLine.update(state.files.inputWeatherFile.readLine());
    2541        16054 :                         if (!WeatherDataLine.good) {
    2542            0 :                             readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
    2543            0 :                             ShowFatalError(state,
    2544            0 :                                            format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
    2545              :                                                   WYear,
    2546              :                                                   WMonth,
    2547              :                                                   WDay,
    2548              :                                                   WHour,
    2549              :                                                   WMinute,
    2550            0 :                                                   state.files.inputWeatherFile.error_state_to_string()),
    2551            0 :                                            OptionalOutputFileRef{state.files.eso});
    2552              :                         }
    2553              :                     }
    2554              :                 }
    2555              :             }
    2556              : 
    2557           27 :             auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
    2558              :             // Why do some things here use state.dataWeather->Envrn and some the parameter Environ?
    2559              : 
    2560              :             // Positioned to proper day
    2561           36 :             if (!state.dataGlobal->KickOffSimulation && !state.dataGlobal->DoingSizing &&
    2562            9 :                 thisEnviron.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
    2563            6 :                 ++thisEnviron.CurrentCycle;
    2564            6 :                 if (!thisEnviron.RollDayTypeOnRepeat) {
    2565            0 :                     SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
    2566            0 :                     if (state.dataWeather->DaylightSavingIsActive) {
    2567            0 :                         SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
    2568              :                     }
    2569            0 :                     SetSpecialDayDates(state, envCurr.MonWeekDay);
    2570            6 :                 } else if (thisEnviron.CurrentCycle == 1) {
    2571            6 :                     SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
    2572            6 :                     thisEnviron.SetWeekDays = true;
    2573            6 :                     if (state.dataWeather->DaylightSavingIsActive) {
    2574            0 :                         SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
    2575              :                     }
    2576            6 :                     SetSpecialDayDates(state, envCurr.MonWeekDay);
    2577              :                 } else {
    2578            0 :                     state.dataWeather->CurDayOfWeek = state.dataEnvrn->DayOfWeekTomorrow;
    2579              :                 }
    2580              :             } else {
    2581           21 :                 SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
    2582              :             }
    2583           27 :         }
    2584              : 
    2585          755 :         bool TryAgain = true;
    2586          755 :         bool SkipThisDay = false;
    2587              : 
    2588         1510 :         while (TryAgain) {
    2589              : 
    2590          755 :             TryAgain = false;
    2591              : 
    2592        18875 :             for (int hour = 1; hour <= 24; ++hour) {
    2593        36240 :                 for (int CurTimeStep = 1; CurTimeStep <= state.dataWeather->NumIntervalsPerHour; ++CurTimeStep) {
    2594        18120 :                     state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour) = WeatherVars();
    2595        18120 :                     auto WeatherDataLine = state.files.inputWeatherFile.readLine();
    2596        18120 :                     if (!WeatherDataLine.good) {
    2597            0 :                         WeatherDataLine.data.clear();
    2598              :                     }
    2599        18120 :                     if (WeatherDataLine.data.empty()) {
    2600            0 :                         if (hour == 1) {
    2601            0 :                             WeatherDataLine.eof = true;
    2602            0 :                             WeatherDataLine.good = false;
    2603              :                         } else {
    2604            0 :                             WeatherDataLine.good = false;
    2605              :                         }
    2606              :                     }
    2607        18120 :                     if (WeatherDataLine.good) {
    2608              :                         bool ErrorFound;
    2609        18120 :                         InterpretWeatherDataLine(state,
    2610              :                                                  WeatherDataLine.data,
    2611              :                                                  ErrorFound,
    2612              :                                                  WYear,
    2613              :                                                  WMonth,
    2614              :                                                  WDay,
    2615              :                                                  WHour,
    2616              :                                                  WMinute,
    2617              :                                                  DryBulb,
    2618              :                                                  DewPoint,
    2619              :                                                  RelHum,
    2620              :                                                  AtmPress,
    2621              :                                                  ETHoriz,
    2622              :                                                  ETDirect,
    2623              :                                                  IRHoriz,
    2624              :                                                  GLBHoriz,
    2625              :                                                  DirectRad,
    2626              :                                                  DiffuseRad,
    2627              :                                                  GLBHorizIllum,
    2628              :                                                  DirectNrmIllum,
    2629              :                                                  DiffuseHorizIllum,
    2630              :                                                  ZenLum,
    2631              :                                                  WindDir,
    2632              :                                                  WindSpeed,
    2633              :                                                  TotalSkyCover,
    2634              :                                                  OpaqueSkyCover,
    2635              :                                                  Visibility,
    2636              :                                                  CeilHeight,
    2637              :                                                  PresWeathObs,
    2638              :                                                  PresWeathConds,
    2639              :                                                  PrecipWater,
    2640              :                                                  AerosolOptDepth,
    2641              :                                                  SnowDepth,
    2642              :                                                  DaysSinceLastSnow,
    2643              :                                                  Albedo,
    2644              :                                                  LiquidPrecip);
    2645              :                     } else { // ReadStatus /=0
    2646            0 :                         if (WeatherDataLine.eof &&
    2647            0 :                             state.dataWeather->NumDataPeriods == 1) { // Standard End-of-file, rewind and position to first day...
    2648            0 :                             if (state.dataWeather->DataPeriods(1).NumDays >= state.dataWeather->NumDaysInYear) {
    2649            0 :                                 state.files.inputWeatherFile.rewind();
    2650            0 :                                 SkipEPlusWFHeader(state);
    2651            0 :                                 WeatherDataLine.update(state.files.inputWeatherFile.readLine());
    2652              :                                 bool ErrorFound;
    2653            0 :                                 InterpretWeatherDataLine(state,
    2654              :                                                          WeatherDataLine.data,
    2655              :                                                          ErrorFound,
    2656              :                                                          WYear,
    2657              :                                                          WMonth,
    2658              :                                                          WDay,
    2659              :                                                          WHour,
    2660              :                                                          WMinute,
    2661              :                                                          DryBulb,
    2662              :                                                          DewPoint,
    2663              :                                                          RelHum,
    2664              :                                                          AtmPress,
    2665              :                                                          ETHoriz,
    2666              :                                                          ETDirect,
    2667              :                                                          IRHoriz,
    2668              :                                                          GLBHoriz,
    2669              :                                                          DirectRad,
    2670              :                                                          DiffuseRad,
    2671              :                                                          GLBHorizIllum,
    2672              :                                                          DirectNrmIllum,
    2673              :                                                          DiffuseHorizIllum,
    2674              :                                                          ZenLum,
    2675              :                                                          WindDir,
    2676              :                                                          WindSpeed,
    2677              :                                                          TotalSkyCover,
    2678              :                                                          OpaqueSkyCover,
    2679              :                                                          Visibility,
    2680              :                                                          CeilHeight,
    2681              :                                                          PresWeathObs,
    2682              :                                                          PresWeathConds,
    2683              :                                                          PrecipWater,
    2684              :                                                          AerosolOptDepth,
    2685              :                                                          SnowDepth,
    2686              :                                                          DaysSinceLastSnow,
    2687              :                                                          Albedo,
    2688              :                                                          LiquidPrecip);
    2689              :                             } else {
    2690            0 :                                 ShowFatalError(state,
    2691            0 :                                                format("End-of-File encountered after {}/{}/{} {}:{}, starting from first day of Weather File would "
    2692              :                                                       "not be \"next day\"",
    2693              :                                                       WYear,
    2694              :                                                       WMonth,
    2695              :                                                       WDay,
    2696              :                                                       WHour,
    2697              :                                                       WMinute));
    2698              :                             }
    2699              :                         } else {
    2700            0 :                             ShowFatalError(state,
    2701            0 :                                            format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
    2702              :                                                   WYear,
    2703              :                                                   WMonth,
    2704              :                                                   WDay,
    2705              :                                                   WHour,
    2706              :                                                   WMinute),
    2707            0 :                                            OptionalOutputFileRef{state.files.eso});
    2708              :                         }
    2709              :                     }
    2710              : 
    2711        18120 :                     if (hour != WHour) {
    2712            0 :                         ShowFatalError(state,
    2713            0 :                                        format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
    2714              :                                               WYear,
    2715              :                                               WMonth,
    2716              :                                               WDay,
    2717              :                                               WHour,
    2718              :                                               WMinute),
    2719            0 :                                        OptionalOutputFileRef{state.files.eso});
    2720              :                     }
    2721              : 
    2722              :                     //         Set possible missing values
    2723        18120 :                     if (ETHoriz < 0.0) {
    2724            0 :                         ETHoriz = 9999.0;
    2725              :                     }
    2726        18120 :                     if (ETDirect < 0.0) {
    2727            0 :                         ETDirect = 9999.0;
    2728              :                     }
    2729        18120 :                     if (IRHoriz <= 0.0) {
    2730           24 :                         IRHoriz = 9999.0;
    2731              :                     }
    2732        18120 :                     if (GLBHoriz < 0.0) {
    2733            0 :                         GLBHoriz = 9999.0;
    2734              :                     }
    2735        18120 :                     if (state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
    2736            0 :                         if (DirectRad >= 9999.0) {
    2737            0 :                             ++state.dataWeather->wvarsMissedCounts.BeamSolarRad;
    2738              :                         }
    2739            0 :                         if (DiffuseRad >= 9999.0) {
    2740            0 :                             state.dataWeather->wvarsMissedCounts.DifSolarRad = state.dataWeather->wvarsMissedCounts.BeamSolarRad + 1;
    2741              :                         }
    2742            0 :                         if (DirectRad < 0.0) {
    2743            0 :                             DirectRad = 9999.0;
    2744            0 :                             ++state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad;
    2745              :                         }
    2746            0 :                         if (DiffuseRad < 0.0) {
    2747            0 :                             DiffuseRad = 9999.0;
    2748            0 :                             ++state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad;
    2749              :                         }
    2750              :                     }
    2751        18120 :                     if (GLBHorizIllum < 0.0) {
    2752            0 :                         GLBHorizIllum = 999999.0;
    2753              :                     }
    2754        18120 :                     if (DirectNrmIllum < 0.0) {
    2755            0 :                         DirectNrmIllum = 999999.0;
    2756              :                     }
    2757        18120 :                     if (DiffuseHorizIllum < 0.0) {
    2758            0 :                         DiffuseHorizIllum = 999999.0;
    2759              :                     }
    2760        18120 :                     if (ZenLum < 0.0) {
    2761            0 :                         ZenLum = 99999.0;
    2762              :                     }
    2763        18120 :                     if (AtmPress < 0.0) {
    2764            0 :                         AtmPress = 999999.0;
    2765              :                     }
    2766        18120 :                     if (WindSpeed < 0.0) {
    2767            0 :                         WindSpeed = 999.0;
    2768              :                     }
    2769        18120 :                     if (WindDir < -360.0 || WindDir > 360.0) {
    2770            0 :                         WindDir = 999.0;
    2771              :                     }
    2772        18120 :                     if (TotalSkyCover < 0.0) {
    2773            0 :                         TotalSkyCover = 99.0;
    2774              :                     }
    2775        18120 :                     if (RelHum < 0.0) {
    2776            0 :                         RelHum = 999.0;
    2777              :                     }
    2778        18120 :                     if (OpaqueSkyCover < 0.0) {
    2779            0 :                         OpaqueSkyCover = 99.0;
    2780              :                     }
    2781        18120 :                     if (Visibility < 0.0) {
    2782            0 :                         Visibility = 9999.0;
    2783              :                     }
    2784        18120 :                     if (CeilHeight < 0.0) {
    2785            0 :                         CeilHeight = 9999.0;
    2786              :                     }
    2787        18120 :                     if (PresWeathObs < 0) {
    2788            0 :                         PresWeathObs = 9;
    2789              :                     }
    2790        18120 :                     if (PrecipWater < 0.0) {
    2791            0 :                         PrecipWater = 999.0;
    2792              :                     }
    2793        18120 :                     if (AerosolOptDepth < 0.0) {
    2794            0 :                         AerosolOptDepth = 999.0;
    2795              :                     }
    2796        18120 :                     if (SnowDepth < 0.0) {
    2797            0 :                         SnowDepth = 999.0;
    2798              :                     }
    2799        18120 :                     if (DaysSinceLastSnow < 0.0) {
    2800            0 :                         DaysSinceLastSnow = 99.0;
    2801              :                     }
    2802        18120 :                     if (Albedo < 0.0) {
    2803            0 :                         Albedo = 999.0;
    2804              :                     }
    2805        18120 :                     if (LiquidPrecip < 0.0) {
    2806            0 :                         LiquidPrecip = 999.0;
    2807              :                     }
    2808              : 
    2809        18120 :                     if (hour == 1 && CurTimeStep == 1) {
    2810          755 :                         if (WMonth == 2 && WDay == 29 && (!state.dataEnvrn->CurrentYearIsLeapYear || !state.dataWeather->WFAllowsLeapYears)) {
    2811            0 :                             state.dataWeather->EndDayOfMonth(2) = 28;
    2812            0 :                             state.dataWeather->EndDayOfMonthWithLeapDay(2) = 28;
    2813            0 :                             SkipThisDay = true;
    2814            0 :                             TryAgain = true;
    2815            0 :                             ShowWarningError(state, "ReadEPlusWeatherForDay: Feb29 data encountered but will not be processed.");
    2816            0 :                             if (!state.dataWeather->WFAllowsLeapYears) {
    2817            0 :                                 ShowContinueError(
    2818              :                                     state, "...WeatherFile does not allow Leap Years. HOLIDAYS/DAYLIGHT SAVINGS header must indicate \"Yes\".");
    2819              :                             }
    2820            0 :                             continue;
    2821              :                         } else {
    2822          755 :                             TryAgain = false;
    2823          755 :                             SkipThisDay = false;
    2824              :                         }
    2825              : 
    2826          755 :                         if (thisEnviron.ActualWeather && state.dataEnvrn->CurrentYearIsLeapYear) {
    2827            0 :                             if (WMonth == 3 && WDay == 1 && state.dataEnvrn->Month == 2 && state.dataEnvrn->DayOfMonth == 28) {
    2828            0 :                                 ShowFatalError(state, "ReadEPlusWeatherForDay: Current year is a leap year, but Feb29 data is missing.");
    2829              :                             }
    2830              :                         }
    2831              : 
    2832          755 :                         state.dataWeather->TomorrowVariables.Year = WYear;
    2833          755 :                         state.dataWeather->TomorrowVariables.Month = WMonth;
    2834          755 :                         state.dataWeather->TomorrowVariables.DayOfMonth = WDay;
    2835          755 :                         state.dataWeather->TomorrowVariables.DayOfYear = General::OrdinalDay(WMonth, WDay, state.dataWeather->LeapYearAdd);
    2836          755 :                         state.dataWeather->TomorrowVariables.DayOfYear_Schedule = General::OrdinalDay(WMonth, WDay, 1);
    2837              :                         Real64 A;
    2838              :                         Real64 B;
    2839              :                         Real64 C;
    2840              :                         Real64 AVSC;
    2841          755 :                         CalculateDailySolarCoeffs(state,
    2842          755 :                                                   state.dataWeather->TomorrowVariables.DayOfYear,
    2843              :                                                   A,
    2844              :                                                   B,
    2845              :                                                   C,
    2846              :                                                   AVSC,
    2847          755 :                                                   state.dataWeather->TomorrowVariables.EquationOfTime,
    2848          755 :                                                   state.dataWeather->TomorrowVariables.SinSolarDeclinAngle,
    2849          755 :                                                   state.dataWeather->TomorrowVariables.CosSolarDeclinAngle);
    2850          755 :                         if (state.dataWeather->CurDayOfWeek <= 7) {
    2851          755 :                             state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
    2852              :                         }
    2853          755 :                         state.dataWeather->TomorrowVariables.DayOfWeek = state.dataWeather->CurDayOfWeek;
    2854         1510 :                         state.dataWeather->TomorrowVariables.DaylightSavingIndex =
    2855          755 :                             state.dataWeather->DSTIndex(state.dataWeather->TomorrowVariables.DayOfYear);
    2856          755 :                         state.dataWeather->TomorrowVariables.HolidayIndex =
    2857          755 :                             state.dataWeather->SpecialDayTypes(state.dataWeather->TomorrowVariables.DayOfYear);
    2858              :                     }
    2859              : 
    2860        18120 :                     if (SkipThisDay) {
    2861            0 :                         continue;
    2862              :                     }
    2863              : 
    2864              :                     // Check out missing values
    2865              : 
    2866        18120 :                     if (DryBulb >= 99.9) {
    2867            0 :                         DryBulb = state.dataWeather->wvarsMissing.OutDryBulbTemp;
    2868            0 :                         ++state.dataWeather->wvarsMissedCounts.OutDryBulbTemp;
    2869              :                     }
    2870        18120 :                     if (DryBulb < -90.0 || DryBulb > 70.0) {
    2871            0 :                         ++state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp;
    2872              :                     }
    2873              : 
    2874        18120 :                     if (DewPoint >= 99.9) {
    2875            0 :                         DewPoint = state.dataWeather->wvarsMissing.OutDewPointTemp;
    2876            0 :                         ++state.dataWeather->wvarsMissedCounts.OutDewPointTemp;
    2877              :                     }
    2878        18120 :                     if (DewPoint < -90.0 || DewPoint > 70.0) {
    2879            0 :                         ++state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp;
    2880              :                     }
    2881              : 
    2882        18120 :                     if (RelHum >= 999.0) {
    2883            0 :                         RelHum = state.dataWeather->wvarsMissing.OutRelHum;
    2884            0 :                         ++state.dataWeather->wvarsMissedCounts.OutRelHum;
    2885              :                     }
    2886        18120 :                     if (RelHum < 0.0 || RelHum > 110.0) {
    2887            0 :                         ++state.dataWeather->wvarsOutOfRangeCounts.OutRelHum;
    2888              :                     }
    2889              : 
    2890        18120 :                     if (AtmPress >= 999999.0) {
    2891            0 :                         AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
    2892            0 :                         ++state.dataWeather->wvarsMissedCounts.OutBaroPress;
    2893              :                     }
    2894        18120 :                     if (AtmPress <= 31000.0 || AtmPress > 120000.0) {
    2895            0 :                         ++state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress;
    2896            0 :                         AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
    2897              :                     }
    2898              : 
    2899        18120 :                     if (WindDir >= 999.0) {
    2900            0 :                         WindDir = state.dataWeather->wvarsMissing.WindDir;
    2901            0 :                         ++state.dataWeather->wvarsMissedCounts.WindDir;
    2902              :                     }
    2903        18120 :                     if (WindDir < 0.0 || WindDir > 360.0) {
    2904            0 :                         ++state.dataWeather->wvarsOutOfRangeCounts.WindDir;
    2905              :                     }
    2906              : 
    2907        18120 :                     if (WindSpeed >= 999.0) {
    2908            0 :                         WindSpeed = state.dataWeather->wvarsMissing.WindSpeed;
    2909            0 :                         ++state.dataWeather->wvarsMissedCounts.WindSpeed;
    2910              :                     }
    2911        18120 :                     if (WindSpeed < 0.0 || WindSpeed > 40.0) {
    2912            0 :                         ++state.dataWeather->wvarsOutOfRangeCounts.WindSpeed;
    2913              :                     }
    2914              : 
    2915        18120 :                     if (TotalSkyCover >= 99.0) {
    2916            0 :                         TotalSkyCover = state.dataWeather->wvarsMissing.TotalSkyCover;
    2917            0 :                         ++state.dataWeather->wvarsMissedCounts.TotalSkyCover;
    2918              :                     }
    2919              : 
    2920        18120 :                     if (OpaqueSkyCover >= 99.0) {
    2921            0 :                         OpaqueSkyCover = state.dataWeather->wvarsMissing.OpaqueSkyCover;
    2922            0 :                         ++state.dataWeather->wvarsMissedCounts.OpaqueSkyCover;
    2923              :                     }
    2924              : 
    2925        18120 :                     if (SnowDepth >= 999.0) {
    2926            0 :                         SnowDepth = state.dataWeather->wvarsMissing.SnowDepth;
    2927            0 :                         ++state.dataWeather->wvarsMissedCounts.SnowDepth;
    2928              :                     }
    2929              : 
    2930        18120 :                     if (Albedo >= 999.0) {
    2931        16608 :                         Albedo = state.dataWeather->wvarsMissing.Albedo;
    2932        16608 :                         ++state.dataWeather->wvarsMissedCounts.Albedo;
    2933              :                     }
    2934              : 
    2935        18120 :                     if (LiquidPrecip >= 999.0) {
    2936        16641 :                         LiquidPrecip = state.dataWeather->wvarsMissing.LiquidPrecip;
    2937        16641 :                         ++state.dataWeather->wvarsMissedCounts.LiquidPrecip;
    2938              :                     }
    2939              : 
    2940        18120 :                     auto &tomorrow = state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour);
    2941        18120 :                     tomorrow.OutDryBulbTemp = DryBulb;
    2942        18120 :                     tomorrow.OutDewPointTemp = DewPoint;
    2943        18120 :                     tomorrow.OutBaroPress = AtmPress;
    2944        18120 :                     tomorrow.OutRelHum = RelHum;
    2945        18120 :                     RelHum *= 0.01;
    2946        18120 :                     tomorrow.WindSpeed = WindSpeed;
    2947        18120 :                     tomorrow.WindDir = WindDir;
    2948        18120 :                     tomorrow.LiquidPrecip = LiquidPrecip;
    2949        18120 :                     tomorrow.TotalSkyCover = TotalSkyCover;
    2950        18120 :                     tomorrow.OpaqueSkyCover = OpaqueSkyCover;
    2951              : 
    2952        18120 :                     calcSky(state, tomorrow.HorizIRSky, tomorrow.SkyTemp, OpaqueSkyCover, DryBulb, DewPoint, RelHum, IRHoriz);
    2953              : 
    2954        18120 :                     if (ETHoriz >= 9999.0) {
    2955            0 :                         ETHoriz = 0.0;
    2956              :                     }
    2957        18120 :                     if (ETDirect >= 9999.0) {
    2958            0 :                         ETDirect = 0.0;
    2959              :                     }
    2960        18120 :                     if (GLBHoriz >= 9999.0) {
    2961            0 :                         GLBHoriz = 0.0;
    2962              :                     }
    2963        18120 :                     if (DirectRad >= 9999.0) {
    2964            0 :                         DirectRad = 0.0;
    2965              :                     }
    2966        18120 :                     if (DiffuseRad >= 9999.0) {
    2967            0 :                         DiffuseRad = 0.0;
    2968              :                     }
    2969        18120 :                     if (GLBHorizIllum >= 999900.0) {
    2970            0 :                         GLBHorizIllum = 0.0;
    2971              :                     }
    2972        18120 :                     if (DirectNrmIllum >= 999900.0) {
    2973            0 :                         DirectNrmIllum = 0.0;
    2974              :                     }
    2975        18120 :                     if (DiffuseHorizIllum >= 999900.0) {
    2976            0 :                         DiffuseHorizIllum = 0.0;
    2977              :                     }
    2978        18120 :                     if (ZenLum >= 99990.0) {
    2979            0 :                         ZenLum = 0.0;
    2980              :                     }
    2981        18120 :                     if (state.dataEnvrn->IgnoreSolarRadiation) {
    2982            0 :                         GLBHoriz = 0.0;
    2983            0 :                         DirectRad = 0.0;
    2984            0 :                         DiffuseRad = 0.0;
    2985              :                     }
    2986        18120 :                     if (state.dataEnvrn->IgnoreBeamRadiation) {
    2987            0 :                         DirectRad = 0.0;
    2988              :                     }
    2989        18120 :                     if (state.dataEnvrn->IgnoreDiffuseRadiation) {
    2990            0 :                         DiffuseRad = 0.0;
    2991              :                     }
    2992              : 
    2993        18120 :                     tomorrow.BeamSolarRad = DirectRad;
    2994        18120 :                     tomorrow.DifSolarRad = DiffuseRad;
    2995              : 
    2996        18120 :                     tomorrow.IsRain = false;
    2997        18120 :                     if (PresWeathObs == 0) {
    2998            0 :                         if (PresWeathConds(1) < 9 || PresWeathConds(2) < 9 || PresWeathConds(3) < 9) {
    2999            0 :                             tomorrow.IsRain = true;
    3000              :                         }
    3001              :                     } else {
    3002        18120 :                         tomorrow.IsRain = false;
    3003              :                     }
    3004        18120 :                     tomorrow.IsSnow = (SnowDepth > 0.0);
    3005              : 
    3006              :                     // default if rain but none on weather file
    3007        18120 :                     if (tomorrow.IsRain && tomorrow.LiquidPrecip == 0.0) {
    3008            0 :                         tomorrow.LiquidPrecip = 2.0; // 2mm in an hour ~ .08 inch
    3009              :                     }
    3010              : 
    3011        18120 :                     state.dataWeather->wvarsMissing.OutDryBulbTemp = DryBulb;
    3012        18120 :                     state.dataWeather->wvarsMissing.OutDewPointTemp = DewPoint;
    3013        18120 :                     state.dataWeather->wvarsMissing.OutRelHum = static_cast<int>(std::round(RelHum * 100.0));
    3014        18120 :                     state.dataWeather->wvarsMissing.OutBaroPress = AtmPress;
    3015        18120 :                     state.dataWeather->wvarsMissing.WindDir = WindDir;
    3016        18120 :                     state.dataWeather->wvarsMissing.WindSpeed = WindSpeed;
    3017        18120 :                     state.dataWeather->wvarsMissing.TotalSkyCover = TotalSkyCover;
    3018        18120 :                     state.dataWeather->wvarsMissing.OpaqueSkyCover = OpaqueSkyCover;
    3019        18120 :                     state.dataWeather->wvarsMissing.Visibility = Visibility;
    3020        18120 :                     state.dataWeather->wvarsMissing.Ceiling = CeilHeight;
    3021        18120 :                     state.dataWeather->wvarsMissing.WaterPrecip = PrecipWater;
    3022        18120 :                     state.dataWeather->wvarsMissing.AerOptDepth = AerosolOptDepth;
    3023        18120 :                     state.dataWeather->wvarsMissing.SnowDepth = SnowDepth;
    3024        18120 :                     state.dataWeather->wvarsMissing.DaysLastSnow = DaysSinceLastSnow;
    3025        18120 :                     state.dataWeather->wvarsMissing.Albedo = Albedo;
    3026              : 
    3027        18120 :                 } // for (CurTimeStep)
    3028              : 
    3029              :             } // for (Hour)
    3030              : 
    3031              :         } // Try Again While Loop
    3032              : 
    3033          755 :         if (BackSpaceAfterRead) {
    3034            4 :             state.files.inputWeatherFile.backspace();
    3035              :         }
    3036              : 
    3037          755 :         if (state.dataWeather->NumIntervalsPerHour == 1 && state.dataGlobal->TimeStepsInHour > 1) {
    3038              :             // Create interpolated weather for timestep orientation
    3039              :             // First copy ts=1 (hourly) from data arrays to Wthr structure
    3040        18850 :             for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
    3041        18096 :                 wvarsHr(hour) = state.dataWeather->wvarsHrTsTomorrow(1, hour);
    3042              :             }
    3043              : 
    3044          754 :             if (!state.dataWeather->LastHourSet) {
    3045              :                 // For first day of weather, all time steps of the first hour will be
    3046              :                 // equal to the first hour's value.
    3047              :                 // 2021-06: An additional input is added to here to allow the user to have chosen which hour to use
    3048           27 :                 int HrUsedtoInterp = thisEnviron.firstHrInterpUseHr1 ? 1 : 24;
    3049           27 :                 state.dataWeather->wvarsLastHr = wvarsHr(HrUsedtoInterp);
    3050           27 :                 state.dataWeather->LastHourSet = true;
    3051              :             }
    3052              : 
    3053        18850 :             for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
    3054              : 
    3055        18096 :                 int NextHr = (hour == Constant::iHoursInDay) ? 1 : hour + 1;
    3056              : 
    3057        18096 :                 state.dataWeather->wvarsNextHr.BeamSolarRad = wvarsHr(NextHr).BeamSolarRad;
    3058        18096 :                 state.dataWeather->wvarsNextHr.DifSolarRad = wvarsHr(NextHr).DifSolarRad;
    3059        18096 :                 state.dataWeather->wvarsNextHr.LiquidPrecip = wvarsHr(NextHr).LiquidPrecip;
    3060              : 
    3061        90480 :                 for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
    3062              : 
    3063        72384 :                     Real64 wgtCurrHr = state.dataWeather->Interpolation(ts);
    3064        72384 :                     Real64 wgtPrevHr = 1.0 - wgtCurrHr;
    3065              : 
    3066              :                     // Do Solar "weighting"
    3067              : 
    3068        72384 :                     Real64 wgtCurrHrSolar = state.dataWeather->SolarInterpolation(ts);
    3069              :                     Real64 wgtPrevHrSolar;
    3070              :                     Real64 wgtNextHrSolar;
    3071              : 
    3072        72384 :                     if (state.dataGlobal->TimeStepsInHour == 1) {
    3073            0 :                         wgtNextHrSolar = 1.0 - wgtCurrHr;
    3074            0 :                         wgtPrevHrSolar = 0.0;
    3075        72384 :                     } else if (wgtCurrHrSolar == 1.0) {
    3076              :                         //  It's at the half hour
    3077        18096 :                         wgtPrevHrSolar = 0.0;
    3078        18096 :                         wgtNextHrSolar = 0.0;
    3079        54288 :                     } else if (ts * state.dataWeather->TimeStepFraction < 0.5) {
    3080        18096 :                         wgtPrevHrSolar = 1.0 - wgtCurrHrSolar;
    3081        18096 :                         wgtNextHrSolar = 0.0;
    3082              :                     } else { // After the half hour
    3083        36192 :                         wgtPrevHrSolar = 0.0;
    3084        36192 :                         wgtNextHrSolar = 1.0 - wgtCurrHrSolar;
    3085              :                     }
    3086              : 
    3087        72384 :                     auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
    3088        72384 :                     auto const &wvarsH = wvarsHr(hour);
    3089        72384 :                     tomorrowTs.OutDryBulbTemp = state.dataWeather->wvarsLastHr.OutDryBulbTemp * wgtPrevHr + wvarsH.OutDryBulbTemp * wgtCurrHr;
    3090        72384 :                     tomorrowTs.OutBaroPress = state.dataWeather->wvarsLastHr.OutBaroPress * wgtPrevHr + wvarsH.OutBaroPress * wgtCurrHr;
    3091        72384 :                     tomorrowTs.OutDewPointTemp = state.dataWeather->wvarsLastHr.OutDewPointTemp * wgtPrevHr + wvarsH.OutDewPointTemp * wgtCurrHr;
    3092        72384 :                     tomorrowTs.OutRelHum = state.dataWeather->wvarsLastHr.OutRelHum * wgtPrevHr + wvarsH.OutRelHum * wgtCurrHr;
    3093        72384 :                     tomorrowTs.WindSpeed = state.dataWeather->wvarsLastHr.WindSpeed * wgtPrevHr + wvarsH.WindSpeed * wgtCurrHr;
    3094        72384 :                     tomorrowTs.WindDir = interpolateWindDirection(state.dataWeather->wvarsLastHr.WindDir, wvarsH.WindDir, wgtCurrHr);
    3095        72384 :                     tomorrowTs.TotalSkyCover = state.dataWeather->wvarsLastHr.TotalSkyCover * wgtPrevHr + wvarsH.TotalSkyCover * wgtCurrHr;
    3096        72384 :                     tomorrowTs.OpaqueSkyCover = state.dataWeather->wvarsLastHr.OpaqueSkyCover * wgtPrevHr + wvarsH.OpaqueSkyCover * wgtCurrHr;
    3097              :                     // Sky emissivity now takes interpolated timestep inputs rather than interpolated calculation esky results
    3098        72384 :                     calcSky(state,
    3099        72384 :                             tomorrowTs.HorizIRSky,
    3100        72384 :                             tomorrowTs.SkyTemp,
    3101              :                             tomorrowTs.OpaqueSkyCover,
    3102              :                             tomorrowTs.OutDryBulbTemp,
    3103              :                             tomorrowTs.OutDewPointTemp,
    3104        72384 :                             tomorrowTs.OutRelHum * 0.01,
    3105        72384 :                             state.dataWeather->wvarsLastHr.HorizIRSky * wgtPrevHr + wvarsH.HorizIRSky * wgtCurrHr);
    3106              : 
    3107        72384 :                     tomorrowTs.DifSolarRad = state.dataWeather->wvarsLastHr.DifSolarRad * wgtPrevHrSolar + wvarsH.DifSolarRad * wgtCurrHrSolar +
    3108        72384 :                                              state.dataWeather->wvarsNextHr.DifSolarRad * wgtNextHrSolar;
    3109        72384 :                     tomorrowTs.BeamSolarRad = state.dataWeather->wvarsLastHr.BeamSolarRad * wgtPrevHrSolar + wvarsH.BeamSolarRad * wgtCurrHrSolar +
    3110        72384 :                                               state.dataWeather->wvarsNextHr.BeamSolarRad * wgtNextHrSolar;
    3111              : 
    3112        72384 :                     tomorrowTs.LiquidPrecip = state.dataWeather->wvarsLastHr.LiquidPrecip * wgtPrevHr + wvarsH.LiquidPrecip * wgtCurrHr;
    3113        72384 :                     tomorrowTs.LiquidPrecip /= double(state.dataGlobal->TimeStepsInHour);
    3114        72384 :                     tomorrowTs.IsRain = tomorrowTs.LiquidPrecip >= state.dataWeather->IsRainThreshold; // Wthr%IsRain
    3115        72384 :                     tomorrowTs.IsSnow = wvarsH.IsSnow;
    3116              :                 } // End of TS Loop
    3117              : 
    3118        18096 :                 state.dataWeather->wvarsLastHr = wvarsHr(hour);
    3119              :             } // End of Hour Loop
    3120              :         }
    3121              : 
    3122          755 :         if (thisEnviron.WP_Type1 != 0) {
    3123            0 :             switch (state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).skyTempModel) {
    3124            0 :             case SkyTempModel::ScheduleValue: {
    3125              :                 std::vector<Real64> const &dayVals =
    3126            0 :                     state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
    3127            0 :                         .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
    3128              : 
    3129            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3130            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3131            0 :                         state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3132              :                     }
    3133              :                 }
    3134            0 :             } break;
    3135              : 
    3136            0 :             case SkyTempModel::DryBulbDelta: {
    3137              :                 std::vector<Real64> const &dayVals =
    3138            0 :                     state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
    3139            0 :                         .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
    3140              : 
    3141            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3142            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3143            0 :                         auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3144            0 :                         tomorrowTs.SkyTemp = tomorrowTs.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3145              :                     }
    3146              :                 }
    3147            0 :             } break;
    3148              : 
    3149            0 :             case SkyTempModel::DewPointDelta: {
    3150              :                 std::vector<Real64> const &dayVals =
    3151            0 :                     state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
    3152            0 :                         .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
    3153              : 
    3154            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3155            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3156            0 :                         auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3157            0 :                         tomorrowTs.SkyTemp = tomorrowTs.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3158              :                     }
    3159              :                 }
    3160            0 :             } break;
    3161              : 
    3162            0 :             default:
    3163            0 :                 break;
    3164              :             }
    3165              :         }
    3166          755 :     }
    3167              : 
    3168        72433 :     Real64 interpolateWindDirection(Real64 const prevHrWindDir, Real64 const curHrWindDir, Real64 const curHrWeight)
    3169              :     {
    3170              :         // adapted from http://stackoverflow.com/questions/2708476/rotation-interpolation
    3171        72433 :         Real64 curAng = curHrWindDir;
    3172        72433 :         Real64 prevAng = prevHrWindDir;
    3173        72433 :         Real64 diff = std::abs(curAng - prevAng);
    3174        72433 :         if (diff > 180.) {
    3175         4148 :             if (curAng > prevAng) {
    3176         1792 :                 prevAng += 360.;
    3177              :             } else {
    3178         2356 :                 curAng += 360.;
    3179              :             }
    3180              :         }
    3181        72433 :         Real64 interpAng = prevAng + (curAng - prevAng) * curHrWeight;
    3182        72433 :         return (fmod(interpAng, 360.)); // fmod is float modulus function
    3183              :     }
    3184              : 
    3185        68408 :     Real64 CalcSkyEmissivity(
    3186              :         EnergyPlusData &state, SkyTempModel const ESkyCalcType, Real64 const OSky, Real64 const DryBulb, Real64 const DewPoint, Real64 const RelHum)
    3187              :     {
    3188              :         // Calculate Sky Emissivity
    3189              :         // References:
    3190              :         // M. Li, Y. Jiang and C. F. M. Coimbra,
    3191              :         // "On the determination of atmospheric longwave irradiance under all-sky conditions,"
    3192              :         // Solar Energy 144, 2017, pp. 40–48,
    3193              :         // G. Clark and C. Allen, "The Estimation of Atmospheric Radiation for Clear and
    3194              :         // Cloudy Skies," Proc. 2nd National Passive Solar Conference (AS/ISES), 1978, pp. 675-678.
    3195              : 
    3196              :         Real64 ESky;
    3197              : 
    3198        68408 :         if (ESkyCalcType == SkyTempModel::Brunt) {
    3199            2 :             double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
    3200            2 :             ESky = 0.618 + 0.056 * pow(PartialPress, 0.5);
    3201        68406 :         } else if (ESkyCalcType == SkyTempModel::Idso) {
    3202            2 :             double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
    3203            2 :             ESky = 0.685 + 0.000032 * PartialPress * exp(1699 / (DryBulb + Constant::Kelvin));
    3204        68404 :         } else if (ESkyCalcType == SkyTempModel::BerdahlMartin) {
    3205            2 :             double const TDewC = min(DryBulb, DewPoint);
    3206            2 :             ESky = 0.758 + 0.521 * (TDewC / 100) + 0.625 * pow_2(TDewC / 100);
    3207              :         } else {
    3208        68402 :             ESky = 0.787 + 0.764 * std::log((min(DryBulb, DewPoint) + Constant::Kelvin) / Constant::Kelvin);
    3209              :         }
    3210        68408 :         return ESky * (1.0 + 0.0224 * OSky - 0.0035 * pow_2(OSky) + 0.00028 * pow_3(OSky));
    3211              :     }
    3212              : 
    3213           27 :     void SetDayOfWeekInitialValues(int const EnvironDayOfWeek, // Starting Day of Week for the (Weather) RunPeriod (User Input)
    3214              :                                    int &currentDayOfWeek       // Current Day of Week
    3215              :     )
    3216              :     {
    3217              : 
    3218              :         // SUBROUTINE INFORMATION:
    3219              :         //       AUTHOR         Linda Lawrie
    3220              :         //       DATE WRITTEN   March 2012
    3221              : 
    3222              :         // PURPOSE OF THIS SUBROUTINE:
    3223              :         // Set of begin day of week for an environment.  Similar sets but slightly different
    3224              :         // conditions.  Improve code readability by having three routine calls instead of three
    3225              :         // IF blocks.
    3226              : 
    3227           27 :         if (EnvironDayOfWeek != 0) {
    3228           27 :             if (EnvironDayOfWeek <= 7) {
    3229           27 :                 currentDayOfWeek = EnvironDayOfWeek - 1;
    3230              :             } else {
    3231            0 :                 currentDayOfWeek = EnvironDayOfWeek;
    3232              :             }
    3233              :         }
    3234           27 :     }
    3235              : 
    3236            0 :     void ErrorInterpretWeatherDataLine(EnergyPlusData &state,
    3237              :                                        int const WYear,
    3238              :                                        int const WMonth,
    3239              :                                        int const WDay,
    3240              :                                        int const WHour,
    3241              :                                        int const WMinute,
    3242              :                                        std::string_view SaveLine,
    3243              :                                        std::string_view Line)
    3244              :     {
    3245            0 :         ShowSevereError(state, fmt::format("Invalid Weather Line at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
    3246            0 :         ShowContinueError(state, fmt::format("Full Data Line={}", SaveLine));
    3247            0 :         ShowContinueError(state, fmt::format("Remainder of line={}", Line));
    3248            0 :         ShowFatalError(state, "Error in Reading Weather Data");
    3249            0 :     }
    3250              : 
    3251        27632 :     void InterpretWeatherDataLine(EnergyPlusData &state,
    3252              :                                   std::string_view Line,
    3253              :                                   bool &ErrorFound, // True if an error is found, false otherwise
    3254              :                                   int &WYear,
    3255              :                                   int &WMonth,
    3256              :                                   int &WDay,
    3257              :                                   int &WHour,
    3258              :                                   int &WMinute,
    3259              :                                   Real64 &DryBulb,
    3260              :                                   Real64 &DewPoint,
    3261              :                                   Real64 &RelHum,
    3262              :                                   Real64 &AtmPress,
    3263              :                                   Real64 &ETHoriz,
    3264              :                                   Real64 &ETDirect,
    3265              :                                   Real64 &IRHoriz,
    3266              :                                   Real64 &GLBHoriz,
    3267              :                                   Real64 &DirectRad,
    3268              :                                   Real64 &DiffuseRad,
    3269              :                                   Real64 &GLBHorizIllum,
    3270              :                                   Real64 &DirectNrmIllum,
    3271              :                                   Real64 &DiffuseHorizIllum,
    3272              :                                   Real64 &ZenLum,
    3273              :                                   Real64 &WindDir,
    3274              :                                   Real64 &WindSpeed,
    3275              :                                   Real64 &TotalSkyCover,
    3276              :                                   Real64 &OpaqueSkyCover,
    3277              :                                   Real64 &Visibility,
    3278              :                                   Real64 &CeilHeight,
    3279              :                                   int &WObs,              // PresWeathObs
    3280              :                                   Array1D_int &WCodesArr, // PresWeathConds
    3281              :                                   Real64 &PrecipWater,
    3282              :                                   Real64 &AerosolOptDepth,
    3283              :                                   Real64 &SnowDepth,
    3284              :                                   Real64 &DaysSinceLastSnow,
    3285              :                                   Real64 &Albedo,
    3286              :                                   Real64 &LiquidPrecip)
    3287              :     {
    3288              : 
    3289              :         // SUBROUTINE INFORMATION:
    3290              :         //       AUTHOR         Linda Lawrie
    3291              :         //       DATE WRITTEN   April 2001
    3292              : 
    3293              :         // PURPOSE OF THIS SUBROUTINE:
    3294              :         // This subroutine interprets the EPW weather data line because comma delimited fields
    3295              :         // may cause problems with some compilers.  (Particularly character variables in
    3296              :         // comma delimited lines.
    3297              : 
    3298              :         // METHODOLOGY EMPLOYED:
    3299              :         // Field by field interpretation, eliminating the "data source field" which is also
    3300              :         // likely to contain blanks.  Note that the "Weatherconditions" must be a 9 character
    3301              :         // alpha field with no intervening blanks.
    3302              : 
    3303        27632 :         EP_SIZE_CHECK(WCodesArr, 9); // NOLINT(misc-static-assert)
    3304              : 
    3305              :         static constexpr std::string_view ValidDigits("0123456789");
    3306              : 
    3307        27632 :         std::string_view::size_type pos = 0;
    3308        27632 :         std::string_view current_line = Line;
    3309              : 
    3310        27632 :         ErrorFound = false;
    3311              : 
    3312              :         // Do the first five.  (To get to the DataSource field)
    3313              :         {
    3314        27632 :             std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 5); // Returns the position **after** the nth occurrence of ','
    3315        27632 :             const bool succeeded = readList(current_line.substr(pos, (nth_pos - 1) - pos), WYear, WMonth, WDay, WHour, WMinute);
    3316        27632 :             if (!succeeded) {
    3317            0 :                 ShowSevereError(state, "Invalid Date info in Weather Line");
    3318            0 :                 ShowContinueError(state, fmt::format("Entire Data Line={}", Line));
    3319            0 :                 ShowFatalError(state, "Error in Reading Weather Data");
    3320              :             }
    3321              :         }
    3322              : 
    3323        27632 :         bool DateInError = false;
    3324        27632 :         if (WMonth >= 1 && WMonth <= 12) {
    3325              :             // Month number is valid
    3326        27632 :             if (WMonth != 2) {
    3327        25560 :                 if (WDay > state.dataWeather->EndDayOfMonth(WMonth)) {
    3328            0 :                     DateInError = true;
    3329              :                 }
    3330         2072 :             } else if (WDay > state.dataWeather->EndDayOfMonth(WMonth) + 1) { // Whether actually used is determined by calling routine.
    3331            0 :                 DateInError = true;
    3332              :             }
    3333              :         } else {
    3334            0 :             DateInError = true;
    3335              :         }
    3336              : 
    3337        27632 :         if (DateInError) {
    3338            0 :             ShowSevereError(state, format("Reading Weather Data Line, Invalid Date, Year={}, Month={}, Day={}", WYear, WMonth, WDay));
    3339            0 :             ShowFatalError(state, "Program terminates due to previous condition.");
    3340              :         }
    3341              : 
    3342              :         // index, unlike nth_occurrence returns the position of the search char, not the position after it
    3343        27632 :         pos = index(Line, ','); // WYear
    3344        27632 :         if (pos == std::string::npos) {
    3345            0 :             ShowSevereError(
    3346            0 :                 state, format("Invalid Weather Line (no commas) at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
    3347            0 :             ShowContinueError(state, fmt::format("Full Data Line={}", Line));
    3348            0 :             ShowFatalError(state, "Error in Reading Weather Data");
    3349              :         }
    3350        27632 :         current_line.remove_prefix(nth_occurrence(Line, ',', 6)); // remove WYear,WMonth,WDay,WHour,WMinute,Data Source/Integrity
    3351              : 
    3352              :         // Now read more numerics with List Directed I/O (note there is another "character" field lurking)
    3353              :         Real64 RField21;
    3354              :         {
    3355        27632 :             std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 21);
    3356              : 
    3357        27632 :             const bool succeeded = readList(current_line.substr(0, nth_pos - 1),
    3358              :                                             DryBulb,
    3359              :                                             DewPoint,
    3360              :                                             RelHum,
    3361              :                                             AtmPress,
    3362              :                                             ETHoriz,
    3363              :                                             ETDirect,
    3364              :                                             IRHoriz,
    3365              :                                             GLBHoriz,
    3366              :                                             DirectRad,
    3367              :                                             DiffuseRad,
    3368              :                                             GLBHorizIllum,
    3369              :                                             DirectNrmIllum,
    3370              :                                             DiffuseHorizIllum,
    3371              :                                             ZenLum,
    3372              :                                             WindDir,
    3373              :                                             WindSpeed,
    3374              :                                             TotalSkyCover,
    3375              :                                             OpaqueSkyCover,
    3376              :                                             Visibility,
    3377              :                                             CeilHeight,
    3378              :                                             RField21);
    3379              : 
    3380        27632 :             if (!succeeded) {
    3381            0 :                 ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
    3382              :             }
    3383        27632 :             current_line.remove_prefix(nth_pos);
    3384              :         }
    3385        27632 :         pos = index(current_line, ',');
    3386        27632 :         std::string PresWeathCodes;
    3387        27632 :         if (pos != std::string::npos && pos != 0) {
    3388        27632 :             PresWeathCodes = current_line.substr(0, pos);
    3389              :         } else {
    3390            0 :             PresWeathCodes = "999999999";
    3391              :         }
    3392        27632 :         current_line.remove_prefix(pos + 1);
    3393              : 
    3394        27632 :         auto readNextNumber = // (AUTO_OK_LAMBDA)
    3395       165792 :             [reachedEndOfCommands = false, &state, &WYear, &WMonth, &WDay, &WHour, &WMinute, &Line, &current_line]() mutable -> Real64 {
    3396       165792 :             if (reachedEndOfCommands) {
    3397            4 :                 return 999.0;
    3398              :             }
    3399              :             Real64 target;
    3400       165788 :             std::string_view::size_type pos = index(current_line, ',');
    3401              :             // We found a comma
    3402       165788 :             if (pos != std::string::npos) {
    3403              :                 // Content is not empty
    3404       165787 :                 if (pos != 0) {
    3405       165787 :                     bool error = false;
    3406       165787 :                     target = Util::ProcessNumber(current_line.substr(0, pos), error);
    3407       165787 :                     if (error) {
    3408            0 :                         ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
    3409              :                     }
    3410              :                 } else {
    3411            0 :                     target = 999.0;
    3412              :                 }
    3413       165787 :                 current_line.remove_prefix(pos + 1);
    3414              :             } else {
    3415              :                 // Couldn't find next comma, but we need to process the potential current number
    3416            1 :                 reachedEndOfCommands = true;
    3417            1 :                 if (current_line.empty()) {
    3418            0 :                     target = 999.0;
    3419              :                 } else {
    3420            1 :                     bool error = false;
    3421            1 :                     target = Util::ProcessNumber(current_line, error);
    3422            1 :                     if (error) {
    3423            0 :                         ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
    3424              :                     }
    3425              :                 }
    3426              :             }
    3427       165788 :             return target;
    3428        27632 :         };
    3429              : 
    3430        27632 :         PrecipWater = readNextNumber();
    3431        27632 :         AerosolOptDepth = readNextNumber();
    3432        27632 :         SnowDepth = readNextNumber();
    3433        27632 :         DaysSinceLastSnow = readNextNumber();
    3434        27632 :         Albedo = readNextNumber();
    3435        27632 :         LiquidPrecip = readNextNumber();
    3436              : 
    3437        27632 :         WObs = nint(RField21);
    3438        27632 :         if (WObs == 0) { // Obs Indicator indicates Weather Codes valid
    3439              :             // Check for miscellaneous characters
    3440            2 :             pos = index(PresWeathCodes, '\'');
    3441            2 :             while (pos != std::string::npos) {
    3442            0 :                 PresWeathCodes[pos] = ' ';
    3443            0 :                 pos = index(PresWeathCodes, '\'');
    3444              :             }
    3445            2 :             pos = index(PresWeathCodes, '"');
    3446            2 :             while (pos != std::string::npos) {
    3447            0 :                 PresWeathCodes[pos] = ' ';
    3448            0 :                 pos = index(PresWeathCodes, '"');
    3449              :             }
    3450            2 :             strip(PresWeathCodes);
    3451            2 :             if (len(PresWeathCodes) == 9) {
    3452           20 :                 for (pos = 0; pos < 9; ++pos) {
    3453           18 :                     if (!has(ValidDigits, PresWeathCodes[pos])) {
    3454            0 :                         PresWeathCodes[pos] = '9';
    3455              :                     }
    3456              :                 }
    3457              : 
    3458              :                 // we are trying to read a string of 9 integers with no spaces, each
    3459              :                 // into its own integer, like:
    3460              :                 // "123456789"
    3461              :                 // becomes
    3462              :                 // std::vector<int>{1,2,3,4,5,6,7,8,9};
    3463            2 :                 std::stringstream reader = stringReader(PresWeathCodes);
    3464           20 :                 for (auto &value : WCodesArr) {
    3465           18 :                     char c[2] = {0, 0};   // a string of 2 characters, init both to 0
    3466           18 :                     reader >> c[0];       // read next char into the first byte
    3467           18 :                     value = std::atoi(c); // convert this short string into the appropriate int to read
    3468              :                 }
    3469            2 :             } else {
    3470            0 :                 ++state.dataWeather->wvarsMissedCounts.WeathCodes;
    3471            0 :                 WCodesArr = 9;
    3472              :             }
    3473              :         } else {
    3474        27630 :             WCodesArr = 9;
    3475              :         }
    3476        27632 :     }
    3477              : 
    3478          517 :     void SetUpDesignDay(EnergyPlusData &state, int const EnvrnNum) // Environment number passed into the routine
    3479              :     {
    3480              : 
    3481              :         // SUBROUTINE INFORMATION:
    3482              :         //       AUTHOR         Linda Lawrie
    3483              :         //       DATE WRITTEN   February 1977
    3484              :         //       MODIFIED       June 1997 (RKS); May 2013 (LKL) add temperature profile for drybulb.
    3485              :         //       RE-ENGINEERED  August 2003;LKL -- to generate timestep weather for design days.
    3486              : 
    3487              :         // PURPOSE OF THIS SUBROUTINE:
    3488              :         // This purpose of this subroutine is to convert the user supplied input
    3489              :         // values for the design day parameters into an entire weather day
    3490              :         // record.  This now bypasses any file I/O by keeping all of the
    3491              :         // weather day record information in the local module level derived type
    3492              :         // called DesignDay.
    3493              : 
    3494          517 :         constexpr Real64 GlobalSolarConstant = 1367.0;
    3495          517 :         constexpr Real64 ZHGlobalSolarConstant = 1355.0;
    3496              : 
    3497          517 :         Real64 constexpr ZhangHuang_C0 = 0.5598;   // 37.6865d0
    3498          517 :         Real64 constexpr ZhangHuang_C1 = 0.4982;   // 13.9263d0
    3499          517 :         Real64 constexpr ZhangHuang_C2 = -0.6762;  // -20.2354d0
    3500          517 :         Real64 constexpr ZhangHuang_C3 = 0.02842;  // 0.9695d0
    3501          517 :         Real64 constexpr ZhangHuang_C4 = -0.00317; // -0.2046d0
    3502          517 :         Real64 constexpr ZhangHuang_C5 = 0.014;    // -0.0980d0
    3503          517 :         Real64 constexpr ZhangHuang_D = -17.853;   // -10.8568d0
    3504          517 :         Real64 constexpr ZhangHuang_K = 0.843;     // 49.3112d0
    3505              :         static constexpr std::string_view RoutineNamePsyWFnTdbTwbPb("SetUpDesignDay:PsyWFnTdbTwbPb");
    3506              :         static constexpr std::string_view RoutineNamePsyWFnTdpPb("SetUpDesignDay:PsyWFnTdpPb");
    3507              :         static constexpr std::string_view RoutineNamePsyWFnTdbH("SetUpDesignDay:PsyWFnTdbH");
    3508              :         static constexpr std::string_view WeatherManager("WeatherManager");
    3509              :         static constexpr std::string_view RoutineNameLong("WeatherManager.cc subroutine SetUpDesignDay");
    3510              : 
    3511          517 :         std::string StringOut;
    3512              :         //     For reporting purposes, set year to current system year
    3513              : 
    3514              :         struct HourlyWeatherData
    3515              :         {
    3516              :             // Members
    3517              :             Array1D<Real64> BeamSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly direct normal solar irradiance
    3518              :             Array1D<Real64> DifSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0);  // Hourly sky diffuse horizontal solar irradiance
    3519              :         };
    3520              : 
    3521              :         // Object Data
    3522          517 :         HourlyWeatherData Wthr;
    3523              : 
    3524          517 :         auto &envCurr = state.dataWeather->Environment(EnvrnNum);
    3525              : 
    3526          517 :         bool SaveWarmupFlag = state.dataGlobal->WarmupFlag;
    3527          517 :         state.dataGlobal->WarmupFlag = true;
    3528              : 
    3529          517 :         Array1D_int Date0(8);
    3530          517 :         date_and_time(_, _, _, Date0);
    3531          517 :         int CurrentYear = Date0(1);
    3532              : 
    3533          517 :         if (state.dataGlobal->BeginSimFlag) {
    3534          103 :             state.dataWeather->PrintDDHeader = true;
    3535              :         }
    3536              : 
    3537          517 :         auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
    3538          517 :         auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
    3539          517 :         designDay.Year = CurrentYear; // f90 date_and_time implemented. full 4 digit year !+ 1900
    3540          517 :         designDay.Month = desDayInput.Month;
    3541          517 :         designDay.DayOfMonth = desDayInput.DayOfMonth;
    3542          517 :         designDay.DayOfYear = General::OrdinalDay(designDay.Month, designDay.DayOfMonth, 0);
    3543              :         static constexpr std::string_view MnDyFmt("{:02}/{:02}");
    3544          517 :         state.dataEnvrn->CurMnDy = format(MnDyFmt, desDayInput.Month, desDayInput.DayOfMonth);
    3545              :         // EnvironmentName = DesDayInput( EnvrnNum ).Title;
    3546          517 :         state.dataEnvrn->RunPeriodEnvironment = false;
    3547              :         // Following builds Environment start/end for ASHRAE 55 warnings
    3548          517 :         state.dataEnvrn->EnvironmentStartEnd = state.dataEnvrn->CurMnDy + " - " + state.dataEnvrn->CurMnDy;
    3549              : 
    3550              :         // Check that barometric pressure is within range
    3551          517 :         if (desDayInput.PressureEntered) {
    3552          517 :             if (std::abs((desDayInput.PressBarom - state.dataEnvrn->StdBaroPress) / state.dataEnvrn->StdBaroPress) > 0.1) { // 10% off
    3553           68 :                 ShowWarningError(state,
    3554           68 :                                  format("SetUpDesignDay: Entered DesignDay Barometric Pressure={:.0R} differs by more than 10% from Standard "
    3555              :                                         "Barometric Pressure={:.0R}.",
    3556           34 :                                         desDayInput.PressBarom,
    3557           34 :                                         state.dataEnvrn->StdBaroPress));
    3558           68 :                 ShowContinueError(
    3559              :                     state,
    3560           68 :                     format("...occurs in DesignDay={}, Standard Pressure (based on elevation) will be used.", state.dataEnvrn->EnvironmentName));
    3561           34 :                 desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
    3562              :             }
    3563              :         } else {
    3564            0 :             desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
    3565              :         }
    3566              : 
    3567              :         // verify that design WB or DP <= design DB
    3568          517 :         if (desDayInput.HumIndType == DesDayHumIndType::DewPoint && desDayInput.DewPointNeedsSet) {
    3569              :             // dew-point
    3570            0 :             Real64 testval = Psychrometrics::PsyWFnTdbRhPb(state, desDayInput.MaxDryBulb, 1.0, desDayInput.PressBarom);
    3571            0 :             desDayInput.HumIndValue = Psychrometrics::PsyTdpFnWPb(state, testval, desDayInput.PressBarom);
    3572              :         }
    3573              : 
    3574              :         // Day of week defaults to Monday, if day type specified, then that is used.
    3575          517 :         designDay.DayOfWeek = 2;
    3576          517 :         if (desDayInput.DayType <= 7) {
    3577            0 :             designDay.DayOfWeek = desDayInput.DayType;
    3578              :         }
    3579              : 
    3580              :         // set Holiday as indicated by user input
    3581          517 :         designDay.HolidayIndex = 0;
    3582          517 :         if (desDayInput.DayType > 7) {
    3583          517 :             designDay.HolidayIndex = desDayInput.DayType;
    3584              :         }
    3585              : 
    3586          517 :         designDay.DaylightSavingIndex = desDayInput.DSTIndicator;
    3587              : 
    3588              :         //  Set up Solar parameters for day
    3589              :         Real64 A;    // Apparent solar irradiation at air mass = 0
    3590              :         Real64 B;    // Atmospheric extinction coefficient
    3591              :         Real64 C;    // ASHRAE diffuse radiation factor
    3592              :         Real64 AVSC; // Annual variation in the solar constant
    3593          517 :         CalculateDailySolarCoeffs(
    3594          517 :             state, designDay.DayOfYear, A, B, C, AVSC, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle);
    3595              : 
    3596          517 :         if (state.dataWeather->PrintDDHeader && state.dataReportFlag->DoWeatherInitReporting) {
    3597              :             static constexpr std::string_view EnvDDHdFormat(
    3598              :                 "! <Environment:Design Day Data>, Max Dry-Bulb Temp {C}, Temp Range {dC}, Temp Range Ind Type, "
    3599              :                 "Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units, Pressure {Pa}, Wind Direction {deg CW from N}, Wind "
    3600              :                 "Speed {m/s}, Clearness, Rain, Snow");
    3601           66 :             print(state.files.eio, "{}\n", EnvDDHdFormat);
    3602              :             static constexpr std::string_view DDayMiscHdFormat(
    3603              :                 "! <Environment:Design Day Misc>,DayOfYear,ASHRAE A Coeff,ASHRAE B Coeff,ASHRAE C Coeff,Solar "
    3604              :                 "Constant-Annual Variation,Eq of Time {minutes}, Solar Declination Angle {deg}, Solar Model");
    3605           66 :             print(state.files.eio, "{}\n", DDayMiscHdFormat);
    3606           66 :             state.dataWeather->PrintDDHeader = false;
    3607              :         }
    3608          517 :         if (state.dataReportFlag->DoWeatherInitReporting) {
    3609          113 :             std::string_view const AlpUseRain = (desDayInput.RainInd == 1) ? "Yes" : "No";
    3610          113 :             std::string_view const AlpUseSnow = (desDayInput.SnowInd == 1) ? "Yes" : "No";
    3611          113 :             print(state.files.eio, "Environment:Design Day Data,");
    3612          113 :             print(state.files.eio, "{:.2R},", desDayInput.MaxDryBulb);
    3613          113 :             print(state.files.eio, "{:.2R},", desDayInput.DailyDBRange);
    3614              : 
    3615              :             static constexpr std::array<std::string_view, (int)DesDayDryBulbRangeType::Num> DesDayDryBulbRangeTypeStrings = {
    3616              :                 "DefaultMultipliers,", "MultiplierSchedule,", "DifferenceSchedule,", "TemperatureProfile,"};
    3617              : 
    3618          113 :             print(state.files.eio, "{}", DesDayDryBulbRangeTypeStrings[(int)desDayInput.dryBulbRangeType]);
    3619              : 
    3620              :             static constexpr std::array<std::string_view, (int)DesDayHumIndType::Num> DesDayHumIndTypeStrings = {
    3621              :                 "Wetbulb,{:.2R},{{C}},",
    3622              :                 "Dewpoint,{:.2R},{{C}},",
    3623              :                 "Enthalpy,{:.2R},{{J/kgDryAir}},",
    3624              :                 "HumidityRatio,{:.4R},{{kgWater/kgDryAir}},",
    3625              :                 "Schedule,<schedule values from 0.0 to 100.0>,{{percent}},",
    3626              :                 "WetBulbProfileDefaultMultipliers,{:.2R},{{C}},",
    3627              :                 "WetBulbProfileDifferenceSchedule,{:.2R},{{C}},",
    3628              :                 "WetBulbProfileMultiplierSchedule,{:.2R},{{C}},"};
    3629              : 
    3630              :             // Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units
    3631          113 :             if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
    3632            0 :                 print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType]);
    3633          113 :             } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
    3634            0 :                 print(state.files.eio,
    3635            0 :                       DesDayHumIndTypeStrings[(int)desDayInput.HumIndType],
    3636            0 :                       state.dataWeather->DesDayInput(state.dataWeather->Envrn).HumIndValue);
    3637              :             } else {
    3638          113 :                 print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType], desDayInput.HumIndValue);
    3639              :             }
    3640              : 
    3641          113 :             print(state.files.eio, "{:.0R},", desDayInput.PressBarom);
    3642          113 :             print(state.files.eio, "{:.0R},", desDayInput.WindDir);
    3643          113 :             print(state.files.eio, "{:.1R},", desDayInput.WindSpeed);
    3644          113 :             print(state.files.eio, "{:.2R},", desDayInput.SkyClear);
    3645              : 
    3646          113 :             print(state.files.eio, "{},{}\n", AlpUseRain, AlpUseSnow);
    3647              : 
    3648              :             static constexpr std::string_view DDayMiscFormat("Environment:Design Day Misc,{:3},");
    3649          113 :             print(state.files.eio, DDayMiscFormat, designDay.DayOfYear);
    3650          113 :             print(state.files.eio, "{:.1R},", A);
    3651          113 :             print(state.files.eio, "{:.4R},", B);
    3652          113 :             print(state.files.eio, "{:.4R},", C);
    3653          113 :             print(state.files.eio, "{:.1R},", AVSC);
    3654          113 :             print(state.files.eio, "{:.2R},", designDay.EquationOfTime * 60.0);
    3655          113 :             print(state.files.eio, "{:.1R},", std::asin(designDay.SinSolarDeclinAngle) / Constant::DegToRad);
    3656              : 
    3657              :             // Why have a different string for "Schedule" here than the one used for input? Really, why?
    3658              :             static constexpr std::array<std::string_view, (int)DesDaySolarModel::Num> DesDaySolarModelStrings = {
    3659              :                 "ASHRAEClearSky", "ZhangHuang", "User supplied beam/diffuse from schedules", "ASHRAETau", "ASHRAETau2017"};
    3660              : 
    3661          113 :             print(state.files.eio, "{}\n", DesDaySolarModelStrings[(int)desDayInput.solarModel]);
    3662              :         }
    3663              : 
    3664              :         // Must set up weather values for Design Day.  User can specify the "humidity indicator" as
    3665              :         // Wetbulb, DewPoint or input the relative humidity schedule.  For both wetbulb and dewpoint indicators, the
    3666              :         // humidity for the day will be constant, using the drybulb (max) and humidity indicator temperature to
    3667              :         // set the values.  For the scheduled values, these are already set in the DDxxx array.
    3668              : 
    3669          517 :         state.dataGlobal->CurrentTime = 25.0;
    3670              :         Real64 HumidityRatio; // Humidity Ratio -- when constant for day
    3671              :         bool ConstantHumidityRatio;
    3672              : 
    3673          517 :         switch (desDayInput.HumIndType) {
    3674          514 :         case DesDayHumIndType::WetBulb: {
    3675          514 :             HumidityRatio = Psychrometrics::PsyWFnTdbTwbPb(
    3676              :                 state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdbTwbPb);
    3677          514 :             ConstantHumidityRatio = true;
    3678          514 :         } break;
    3679            0 :         case DesDayHumIndType::DewPoint: {
    3680            0 :             HumidityRatio = Psychrometrics::PsyWFnTdpPb(state, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdpPb);
    3681            0 :             ConstantHumidityRatio = true;
    3682            0 :         } break;
    3683            0 :         case DesDayHumIndType::HumRatio: {
    3684            0 :             HumidityRatio = desDayInput.HumIndValue;
    3685            0 :             ConstantHumidityRatio = true;
    3686            0 :         } break;
    3687            1 :         case DesDayHumIndType::Enthalpy: {
    3688              :             // HumIndValue is already in J/kg, so no conversions needed
    3689            1 :             HumidityRatio = Psychrometrics::PsyWFnTdbH(state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, RoutineNamePsyWFnTdbH);
    3690            1 :             ConstantHumidityRatio = true;
    3691            1 :         } break;
    3692            0 :         case DesDayHumIndType::RelHumSch: {
    3693              :             // nothing to do -- DDHumIndModifier already contains the scheduled Relative Humidity
    3694            0 :             ConstantHumidityRatio = false;
    3695            0 :             for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3696            0 :                 for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3697            0 :                     state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).OutRelHum =
    3698            0 :                         state.dataWeather->desDayMods(EnvrnNum)(ts + 1, hr + 1).OutRelHum;
    3699              :                 }
    3700              :             }
    3701            0 :         } break;
    3702            2 :         case DesDayHumIndType::WBProfDef:
    3703              :         case DesDayHumIndType::WBProfDif:
    3704              :         case DesDayHumIndType::WBProfMul: {
    3705            2 :             ConstantHumidityRatio = false;
    3706            2 :         } break;
    3707            0 :         default: {
    3708            0 :             ShowSevereError(state, "SetUpDesignDay: Invalid Humidity Indicator type");
    3709            0 :             ShowContinueError(state, format("Occurred in Design Day={}", desDayInput.Title));
    3710            0 :         } break;
    3711              :         } // switch
    3712              : 
    3713              :         int OSky; // Opaque Sky Cover (tenths)
    3714          517 :         if (desDayInput.RainInd != 0) {
    3715            0 :             OSky = 10;
    3716            0 :             for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3717            0 :                 for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3718            0 :                     auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3719            0 :                     wvars.IsRain = true;
    3720            0 :                     wvars.LiquidPrecip = 3.0;
    3721              :                 }
    3722              :             }
    3723              :         } else {
    3724          517 :             OSky = 0;
    3725        12925 :             for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3726        80424 :                 for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3727        68016 :                     auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3728        68016 :                     wvars.IsRain = false;
    3729        68016 :                     wvars.LiquidPrecip = 0.0;
    3730              :                 }
    3731              :             }
    3732              :         }
    3733              : 
    3734              :         Real64 GndReflet; // Ground Reflectivity
    3735          517 :         if (desDayInput.SnowInd == 0) {
    3736          517 :             GndReflet = 0.2;
    3737        12925 :             for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3738        80424 :                 for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3739        68016 :                     auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3740        68016 :                     wvars.IsSnow = false;
    3741              :                 }
    3742              :             }
    3743              :         } else { // Snow
    3744            0 :             GndReflet = 0.7;
    3745            0 :             for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3746            0 :                 for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3747            0 :                     auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3748            0 :                     wvars.IsSnow = true;
    3749              :                 }
    3750              :             }
    3751              :         }
    3752              : 
    3753              :         // Some values are constant
    3754              : 
    3755        12925 :         for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3756        80424 :             for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3757        68016 :                 auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3758        68016 :                 wvars.OutBaroPress = desDayInput.PressBarom;
    3759        68016 :                 wvars.WindSpeed = desDayInput.WindSpeed;
    3760        68016 :                 wvars.WindDir = desDayInput.WindDir;
    3761        68016 :                 wvars.Albedo = 0.0;
    3762              :             }
    3763              :         }
    3764              : 
    3765              :         // resolve daily ranges
    3766              :         Real64 DBRange; // working copy of dry-bulb daily range, C (or 1 if input is difference)
    3767          517 :         if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
    3768            0 :             DBRange = 1.0; // use unscaled multiplier values if difference
    3769          517 :         } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
    3770            0 :             DBRange = 0.0;
    3771              :         } else {
    3772          517 :             DBRange = desDayInput.DailyDBRange;
    3773              :         }
    3774              :         Real64 WBRange; // working copy of wet-bulb daily range. C (or 1 if input is difference)
    3775          517 :         if (desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
    3776            0 :             WBRange = 1.0; // use unscaled multiplier values if difference
    3777              :         } else {
    3778          517 :             WBRange = desDayInput.DailyWBRange;
    3779              :         }
    3780              : 
    3781          517 :         auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    3782        12925 :         for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
    3783        80424 :             for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
    3784        68016 :                 auto const &desDayModsTS = desDayModsEnvrn(ts, hour);
    3785        68016 :                 auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
    3786        68016 :                 if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
    3787              :                     // dry-bulb profile
    3788        68016 :                     tomorrowTs.OutDryBulbTemp = desDayInput.MaxDryBulb - desDayModsTS.OutDryBulbTemp * DBRange;
    3789              :                 } else { // DesDayInput(EnvrnNum)%DBTempRangeType == DesDayDryBulbRangeType::Profile
    3790            0 :                     tomorrowTs.OutDryBulbTemp = desDayModsTS.OutDryBulbTemp;
    3791              :                 }
    3792              : 
    3793              :                 // wet-bulb - generate from profile, humidity ratio, or dew point
    3794        68016 :                 if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
    3795        67824 :                     desDayInput.HumIndType == DesDayHumIndType::WBProfMul) {
    3796          192 :                     Real64 WetBulb = desDayInput.HumIndValue - desDayModsTS.OutRelHum * WBRange;
    3797          192 :                     WetBulb = min(WetBulb, tomorrowTs.OutDryBulbTemp); // WB must be <= DB
    3798          192 :                     Real64 OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
    3799          192 :                     tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
    3800          192 :                     tomorrowTs.OutRelHum =
    3801          192 :                         Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
    3802        68016 :                 } else if (ConstantHumidityRatio) {
    3803              :                     //  Need Dew Point Temperature.  Use Relative Humidity to get Humidity Ratio, unless Humidity Ratio is constant
    3804              :                     // BG 9-26-07  moved following inside this IF statment; when HumIndType is 'Schedule' HumidityRatio wasn't being initialized
    3805              :                     Real64 WetBulb =
    3806        67824 :                         Psychrometrics::PsyTwbFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, RoutineNameLong);
    3807              : 
    3808        67824 :                     Real64 OutHumRat = Psychrometrics::PsyWFnTdpPb(state, tomorrowTs.OutDryBulbTemp, desDayInput.PressBarom);
    3809        67824 :                     if (HumidityRatio > OutHumRat) {
    3810        18445 :                         WetBulb = tomorrowTs.OutDryBulbTemp;
    3811              :                     } else {
    3812        49379 :                         OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
    3813              :                     }
    3814        67824 :                     tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
    3815        67824 :                     tomorrowTs.OutRelHum =
    3816        67824 :                         Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
    3817              :                 } else {
    3818              :                     HumidityRatio =
    3819            0 :                         Psychrometrics::PsyWFnTdbRhPb(state, tomorrowTs.OutDryBulbTemp, desDayModsTS.OutRelHum / 100.0, desDayInput.PressBarom);
    3820            0 :                     tomorrowTs.OutRelHum =
    3821            0 :                         Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, WeatherManager) *
    3822              :                         100.0;
    3823              :                     // TomorrowOutRelHum values set earlier
    3824            0 :                     tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, HumidityRatio, desDayInput.PressBarom);
    3825              :                 }
    3826              : 
    3827        68016 :                 double DryBulb = tomorrowTs.OutDryBulbTemp;
    3828        68016 :                 double RelHum = tomorrowTs.OutRelHum * 0.01;
    3829              :                 Real64 ESky =
    3830        68016 :                     CalcSkyEmissivity(state, envCurr.skyTempModel, OSky, DryBulb, tomorrowTs.OutDewPointTemp, RelHum); // Emissivitity of Sky
    3831        68016 :                 tomorrowTs.HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
    3832              : 
    3833        68016 :                 if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
    3834        68016 :                     envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
    3835              :                     // Design day not scheduled
    3836        68016 :                     tomorrowTs.SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
    3837              :                 }
    3838              :                 // Generate solar values for timestep
    3839              :                 //    working results = BeamRad and DiffRad
    3840              :                 //    stored to program globals at end of loop
    3841              :                 Real64 BeamRad;
    3842              :                 Real64 DiffRad;
    3843        68016 :                 if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
    3844              :                     // scheduled: set value unconditionally (whether sun up or not)
    3845            0 :                     BeamRad = desDayModsTS.BeamSolarRad;
    3846            0 :                     DiffRad = desDayModsTS.DifSolarRad;
    3847              :                 } else {
    3848              : 
    3849              :                     // calc time = fractional hour of day
    3850              :                     Real64 CurTime;
    3851        68016 :                     if (state.dataGlobal->TimeStepsInHour != 1) {
    3852        67392 :                         CurTime = double(hour - 1) + double(ts) * state.dataWeather->TimeStepFraction;
    3853              :                     } else {
    3854          624 :                         CurTime = double(hour) + state.dataEnvrn->TS1TimeOffset;
    3855              :                     }
    3856              : 
    3857        68016 :                     Vector3<Real64> SUNCOS; // Sun direction cosines
    3858        68016 :                     CalculateSunDirectionCosines(
    3859              :                         state, CurTime, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle, SUNCOS);
    3860        68016 :                     Real64 CosZenith = SUNCOS.z; // Cosine of Zenith Angle of Sun
    3861        68016 :                     if (CosZenith < DataEnvironment::SunIsUpValue) {
    3862        34134 :                         BeamRad = 0.0;
    3863        34134 :                         DiffRad = 0.0;
    3864              :                     } else {
    3865        33882 :                         Real64 SinSolarAltitude = SUNCOS.z;
    3866              : 
    3867        33882 :                         switch (desDayInput.solarModel) {
    3868        31727 :                         case DesDaySolarModel::ASHRAE_ClearSky: {
    3869        31727 :                             Real64 Exponent = B / CosZenith;
    3870              :                             Real64 TotHoriz; // Total Radiation on Horizontal Surface
    3871        31727 :                             if (Exponent > 700.0) {
    3872            4 :                                 TotHoriz = 0.0;
    3873              :                             } else {
    3874        31723 :                                 TotHoriz = desDayInput.SkyClear * A * (C + CosZenith) * std::exp(-B / CosZenith);
    3875              :                             }
    3876              :                             // Radiation on an extraterrestial horizontal surface
    3877        31727 :                             Real64 HO = GlobalSolarConstant * AVSC * CosZenith;
    3878        31727 :                             Real64 KT = TotHoriz / HO; // Radiation ratio
    3879        31727 :                             KT = min(KT, 0.75);
    3880        31727 :                             DiffRad = TotHoriz * (1.0045 + KT * (0.04349 + KT * (-3.5227 + 2.6313 * KT)));
    3881        31727 :                             if (desDayInput.SkyClear > 0.70) {
    3882        19061 :                                 DiffRad = TotHoriz * C / (C + CosZenith);
    3883              :                             }
    3884        31727 :                             BeamRad = (TotHoriz - DiffRad) / CosZenith;
    3885        31727 :                             DiffRad = max(0.0, DiffRad);
    3886        31727 :                             BeamRad = max(0.0, BeamRad);
    3887              : 
    3888        31727 :                         } break;
    3889         2155 :                         case DesDaySolarModel::ASHRAE_Tau:
    3890              :                         case DesDaySolarModel::ASHRAE_Tau2017: {
    3891         2155 :                             Real64 ETR = GlobalSolarConstant * AVSC; // radiation of an extraterrestrial normal surface, W/m2
    3892              :                             Real64 GloHorzRad;
    3893         2155 :                             ASHRAETauModel(
    3894              :                                 state, desDayInput.solarModel, ETR, CosZenith, desDayInput.TauB, desDayInput.TauD, BeamRad, DiffRad, GloHorzRad);
    3895         2155 :                         } break;
    3896            0 :                         case DesDaySolarModel::Zhang_Huang: {
    3897            0 :                             int Hour3Ago = mod(hour + 20, 24) + 1; // hour 3 hours before
    3898            0 :                             Real64 const TotSkyCover = max(1.0 - desDayInput.SkyClear, 0.0);
    3899            0 :                             Real64 GloHorzRad = (ZHGlobalSolarConstant * SinSolarAltitude *
    3900            0 :                                                      (ZhangHuang_C0 + ZhangHuang_C1 * TotSkyCover + ZhangHuang_C2 * pow_2(TotSkyCover) +
    3901            0 :                                                       ZhangHuang_C3 * (tomorrowTs.OutDryBulbTemp -
    3902            0 :                                                                        state.dataWeather->wvarsHrTsTomorrow(ts, Hour3Ago).OutDryBulbTemp) +
    3903            0 :                                                       ZhangHuang_C4 * tomorrowTs.OutRelHum + ZhangHuang_C5 * tomorrowTs.WindSpeed) +
    3904              :                                                  ZhangHuang_D) /
    3905            0 :                                                 ZhangHuang_K;
    3906            0 :                             GloHorzRad = max(GloHorzRad, 0.0);
    3907            0 :                             Real64 ClearnessIndex_kt = GloHorzRad / (GlobalSolarConstant * SinSolarAltitude);
    3908              :                             //          ClearnessIndex_kt=DesDayInput(EnvrnNum)%SkyClear
    3909            0 :                             Real64 ClearnessIndex_ktc = 0.4268 + 0.1934 * SinSolarAltitude;
    3910              :                             Real64 ClearnessIndex_kds;
    3911            0 :                             if (ClearnessIndex_kt < ClearnessIndex_ktc) {
    3912            0 :                                 ClearnessIndex_kds = (3.996 - 3.862 * SinSolarAltitude + 1.54 * pow_2(SinSolarAltitude)) * pow_3(ClearnessIndex_kt);
    3913              :                             } else {
    3914            0 :                                 ClearnessIndex_kds = ClearnessIndex_kt - (1.107 + 0.03569 * SinSolarAltitude + 1.681 * pow_2(SinSolarAltitude)) *
    3915            0 :                                                                              pow_3(1.0 - ClearnessIndex_kt);
    3916              :                             }
    3917              :                             // Calculate direct normal radiation, W/m2
    3918            0 :                             BeamRad = ZHGlobalSolarConstant * SinSolarAltitude * ClearnessIndex_kds *
    3919            0 :                                       ((1.0 - ClearnessIndex_kt) / (1.0 - ClearnessIndex_kds));
    3920              :                             // Calculation diffuse horizontal radiation, W/m2
    3921            0 :                             DiffRad =
    3922            0 :                                 ZHGlobalSolarConstant * SinSolarAltitude * ((ClearnessIndex_kt - ClearnessIndex_kds) / (1.0 - ClearnessIndex_kds));
    3923              : 
    3924            0 :                         } break;
    3925            0 :                         default:
    3926            0 :                             break;
    3927              :                         }
    3928              :                     }
    3929        68016 :                 }
    3930              : 
    3931              :                 // override result to 0 per environment var (for testing)
    3932        68016 :                 if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreBeamRadiation) {
    3933            0 :                     BeamRad = 0.0;
    3934              :                 }
    3935        68016 :                 if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreDiffuseRadiation) {
    3936            0 :                     DiffRad = 0.0;
    3937              :                 }
    3938              : 
    3939        68016 :                 tomorrowTs.BeamSolarRad = BeamRad;
    3940        68016 :                 tomorrowTs.DifSolarRad = DiffRad;
    3941              : 
    3942              :             } // Timestep (TS) Loop
    3943              :         } // Hour Loop
    3944              : 
    3945              :         // back-fill hour values from timesteps
    3946              :         // hour values = integrated over hour ending at time of hour
    3947              :         // insurance: hourly values not known to be needed
    3948        12925 :         for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
    3949        12408 :             int Hour1Ago = mod(hour + 22, Constant::iHoursInDay) + 1;
    3950        12408 :             auto const &tomorrowHr = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, hour);
    3951        12408 :             auto const &tomorrowHr1Ago = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, Hour1Ago);
    3952              : 
    3953        12408 :             Real64 BeamRad = (tomorrowHr1Ago.BeamSolarRad + tomorrowHr.BeamSolarRad) / 2.0;
    3954        12408 :             Real64 DiffRad = (tomorrowHr1Ago.DifSolarRad + tomorrowHr.DifSolarRad) / 2.0;
    3955        12408 :             if (state.dataGlobal->TimeStepsInHour > 1) {
    3956        67392 :                 for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour - 1; ++iTS) {
    3957        55608 :                     BeamRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).BeamSolarRad;
    3958        55608 :                     DiffRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).DifSolarRad;
    3959              :                 }
    3960              :             }
    3961        12408 :             Wthr.BeamSolarRad(hour) = BeamRad / state.dataGlobal->TimeStepsInHour;
    3962        12408 :             Wthr.DifSolarRad(hour) = DiffRad / state.dataGlobal->TimeStepsInHour;
    3963              :         }
    3964              : 
    3965          517 :         if (envCurr.WP_Type1 != 0) {
    3966              : 
    3967            0 :             switch (state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel) {
    3968            0 :             case SkyTempModel::ScheduleValue: {
    3969            0 :                 std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
    3970            0 :                 auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    3971            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3972            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3973            0 :                         state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = desDayModsEnvrn(ts + 1, hr + 1).SkyTemp =
    3974            0 :                             dayVals[hr * state.dataGlobal->TimeStepsInHour];
    3975              :                     }
    3976              :                 }
    3977            0 :             } break;
    3978              : 
    3979            0 :             case SkyTempModel::DryBulbDelta: {
    3980            0 :                 std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
    3981            0 :                 auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    3982            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3983            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3984            0 :                         auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3985            0 :                         desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3986            0 :                         tomorrowTS.SkyTemp = tomorrowTS.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3987              :                     }
    3988              :                 }
    3989            0 :             } break;
    3990              : 
    3991            0 :             case SkyTempModel::DewPointDelta: {
    3992            0 :                 std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
    3993            0 :                 auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    3994            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    3995            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    3996            0 :                         auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
    3997            0 :                         desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3998            0 :                         tomorrowTS.SkyTemp = tomorrowTS.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    3999              :                     }
    4000              :                 }
    4001            0 :             } break;
    4002              : 
    4003            0 :             default: {
    4004            0 :             } break;
    4005              :             } // switch (skyTempModel)
    4006              :         } // if (envCurr.WP_Type1 != 0)
    4007              : 
    4008          517 :         state.dataGlobal->WarmupFlag = SaveWarmupFlag;
    4009          517 :     }
    4010              : 
    4011         2159 :     Real64 AirMass(Real64 const CosZen) // COS( solar zenith), 0 - 1
    4012              :     {
    4013              : 
    4014              :         // SUBROUTINE INFORMATION:
    4015              :         //       AUTHOR         C Barnaby
    4016              :         //       DATE WRITTEN   Nov 2010
    4017              : 
    4018              :         // PURPOSE OF THIS SUBROUTINE:
    4019              :         // Calculate relative air mass using Kasten and Young approximation
    4020              : 
    4021              :         // METHODOLOGY EMPLOYED:
    4022              :         // Eqn (16), ASHRAE HOF 2009, p. 14.9
    4023              : 
    4024              :         // REFERENCES:
    4025              :         // ASHRAE HOF 2009 Chapter 14
    4026              :         // Kasten, F and T. Young.  1989.  Revised optical air mass tables
    4027              :         //   and approximating formula.  Applied Optics 28:4735-4738.
    4028              : 
    4029              :         Real64 AirMass;
    4030              :         Real64 SunAltD;
    4031              : 
    4032         2159 :         if (CosZen <= 0.001) {
    4033            5 :             AirMass = 37.07837343; // limit value calc'd with Excel
    4034              :                                    //  value increases little as CosZen -> 0
    4035         2154 :         } else if (CosZen >= 1.0) {
    4036            4 :             AirMass = 1.0;
    4037              :         } else {
    4038              :             // note: COS( Zen) = SIN( Alt)
    4039         2150 :             SunAltD = std::asin(CosZen) / Constant::DegToRad; // altitude, degrees
    4040         2150 :             AirMass = 1.0 / (CosZen + 0.50572 * std::pow(6.07995 + SunAltD, -1.6364));
    4041              :         }
    4042         2159 :         return AirMass;
    4043              :     }
    4044              : 
    4045              :     //------------------------------------------------------------------------------
    4046              : 
    4047         2157 :     void ASHRAETauModel([[maybe_unused]] EnergyPlusData &state,
    4048              :                         DesDaySolarModel const TauModel, // ASHRAETau solar model type ASHRAE_Tau or ASHRAE_Tau2017
    4049              :                         Real64 const ETR,                // extraterrestrial normal irradiance, W/m2
    4050              :                         Real64 const CosZen,             // COS( solar zenith angle), 0 - 1
    4051              :                         Real64 const TauB,               // beam tau factor
    4052              :                         Real64 const TauD,               // dif tau factor
    4053              :                         Real64 &IDirN,                   // returned: direct (beam) irradiance on normal surface, W/m2
    4054              :                         Real64 &IDifH,                   // returned: diffuse irradiance on horiz surface, W/m2
    4055              :                         Real64 &IGlbH                    // returned: global irradiance on horiz surface, W/m2
    4056              :     )
    4057              :     {
    4058              : 
    4059              :         // SUBROUTINE INFORMATION:
    4060              :         //       AUTHOR         C Barnaby
    4061              :         //       DATE WRITTEN   Nov 2010
    4062              : 
    4063              :         // PURPOSE OF THIS SUBROUTINE:
    4064              :         // Calculate clear-sky direct and diffuse irradiance using ASHRAE "tau" model
    4065              : 
    4066              :         // METHODOLOGY EMPLOYED:
    4067              :         // Eqns (17-18), ASHRAE HOF 2009, p. 14.9
    4068              :         // Eqns (19-20), ASHRAE HOF 2013 p. 14.9 and 2017 p. 14.10
    4069              : 
    4070              :         // REFERENCES:
    4071              :         // ASHRAE HOF 2009 Chapter 14
    4072              : 
    4073              :         Real64 AB; // air mass exponents
    4074              :         Real64 AD;
    4075              :         Real64 M; // air mass
    4076              : 
    4077         2157 :         if (CosZen < DataEnvironment::SunIsUpValue || TauB <= 0.0 || TauD <= 0.0) {
    4078            0 :             IDirN = 0.0;
    4079            0 :             IDifH = 0.0;
    4080            0 :             IGlbH = 0.0;
    4081              :         } else {
    4082         2157 :             if (TauModel == DesDaySolarModel::ASHRAE_Tau) {
    4083         2045 :                 AB = 1.219 - 0.043 * TauB - 0.151 * TauD - 0.204 * TauB * TauD;
    4084         2045 :                 AD = 0.202 + 0.852 * TauB - 0.007 * TauD - 0.357 * TauB * TauD;
    4085              :             } else {
    4086              :                 // TauModelType == ASHRAE_Tau2017
    4087          112 :                 AB = 1.454 - 0.406 * TauB - 0.268 * TauD + 0.021 * TauB * TauD;
    4088          112 :                 AD = 0.507 + 0.205 * TauB - 0.080 * TauD - 0.190 * TauB * TauD;
    4089              :             }
    4090         2157 :             M = AirMass(CosZen);
    4091         2157 :             IDirN = ETR * std::exp(-TauB * std::pow(M, AB));
    4092         2157 :             IDifH = ETR * std::exp(-TauD * std::pow(M, AD));
    4093         2157 :             IGlbH = IDirN * CosZen + IDifH;
    4094              :         }
    4095         2157 :     }
    4096              : 
    4097          117 :     void AllocateWeatherData(EnergyPlusData &state)
    4098              :     {
    4099              : 
    4100              :         // SUBROUTINE INFORMATION:
    4101              :         //       AUTHOR         Linda Lawrie
    4102              :         //       DATE WRITTEN   December 2000
    4103              : 
    4104              :         // PURPOSE OF THIS SUBROUTINE:
    4105              :         // This subroutine allocates the weather data structures (Today, Tomorrow,
    4106              :         // Design Day) to the proper number of "time steps in hour" requested by the user.
    4107              :         // Interpolation of data is done later after either setting up the design day (hourly
    4108              :         // data) or reading in hourly weather data.
    4109              : 
    4110          117 :         state.dataWeather->wvarsHrTsToday.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
    4111          117 :         state.dataWeather->wvarsHrTsTomorrow.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
    4112          117 :     }
    4113              : 
    4114         1272 :     void CalculateDailySolarCoeffs(EnergyPlusData const &state,
    4115              :                                    int const DayOfYear,           // Day of year (1 - 366)
    4116              :                                    Real64 &A,                     // ASHRAE "A" - Apparent solar irradiation at air mass = 0 [W/M**2]
    4117              :                                    Real64 &B,                     // ASHRAE "B" - Atmospheric extinction coefficient
    4118              :                                    Real64 &C,                     // ASHRAE "C" - Diffuse radiation factor
    4119              :                                    Real64 &AnnVarSolConstant,     // Annual variation in the solar constant
    4120              :                                    Real64 &EquationOfTime,        // Equation of Time
    4121              :                                    Real64 &SineSolarDeclination,  // Sine of Solar Declination
    4122              :                                    Real64 &CosineSolarDeclination // Cosine of Solar Declination
    4123              :     )
    4124              :     {
    4125              : 
    4126              :         // SUBROUTINE INFORMATION:
    4127              :         //       AUTHOR         George Walton
    4128              :         //       DATE WRITTEN   May 1985
    4129              :         //       MODIFIED       1999 for EnergyPlus
    4130              :         //       RE-ENGINEERED  2001; LKL; Remove need for English -> SI conversion
    4131              :         //                      Implement Tarp "fix" for Southern Hemisphere
    4132              : 
    4133              :         // PURPOSE OF THIS SUBROUTINE:
    4134              :         // This subroutine computes the daily solar coefficients used in other
    4135              :         // calculations.  Specifically, this routine computes values of the solar declination, equation
    4136              :         // of time, and ashrae sky coefficients a, b, and c for a given
    4137              :         // day of the year.
    4138              : 
    4139              :         // METHODOLOGY EMPLOYED:
    4140              :         // The method is the same as that recommended in the ASHRAE loads
    4141              :         // algorithms manual, except that the fourier series expressions
    4142              :         // have been extended by two terms for greater accuracy.
    4143              :         // coefficients for the new expressions were determined at USACERL
    4144              :         // using data from the cited references.
    4145              : 
    4146              :         // REFERENCES:
    4147              :         // J. L. Threlkeld, "Thermal Environmental Engineering", 1970,
    4148              :         // p.316, for declination and equation of time.
    4149              :         // "ASHRAE Handbook of Fundamentals", 1972, p.387 for sky
    4150              :         // coefficients a, b, and c.
    4151              :         // See SUN3 in SolarShading. See SUN2 in BLAST.  See SUN3 in Tarp.
    4152              : 
    4153         1272 :         Real64 const DayCorrection(Constant::Pi * 2.0 / 366.0);
    4154              : 
    4155              :         // Fitted coefficients of Fourier series | Sine of declination coefficients
    4156              :         static constexpr std::array<Real64, 9> SineSolDeclCoef = {
    4157              :             0.00561800, 0.0657911, -0.392779, 0.00064440, -0.00618495, -0.00010101, -0.00007951, -0.00011691, 0.00002096};
    4158              :         // Fitted coefficients of Fourier Series | Equation of Time coefficients
    4159              :         static constexpr std::array<Real64, 9> EqOfTimeCoef = {
    4160              :             0.00021971, -0.122649, 0.00762856, -0.156308, -0.0530028, -0.00388702, -0.00123978, -0.00270502, -0.00167992};
    4161              :         // Fitted coefficients of Fourier Series | ASHRAE A Factor coefficients
    4162              :         static constexpr std::array<Real64, 9> ASHRAE_A_Coef = {1161.6685, 1.1554, 77.3575, -0.5359, -3.7622, 0.9875, -3.3924, -1.7445, 1.1198};
    4163              :         // Fitted coefficients of Fourier Series | ASHRAE B Factor coefficients
    4164              :         static constexpr std::array<Real64, 9> ASHRAE_B_Coef = {
    4165              :             0.171631, -0.00400448, -0.0344923, 0.00000209, 0.00325428, -0.00085429, 0.00229562, 0.0009034, -0.0011867};
    4166              :         // Fitted coefficients of Fourier Series | ASHRAE C Factor coefficients
    4167              :         static constexpr std::array<Real64, 9> ASHRAE_C_Coef = {
    4168              :             0.0905151, -0.00322522, -0.0407966, 0.000104164, 0.00745899, -0.00086461, 0.0013111, 0.000808275, -0.00170515};
    4169              : 
    4170              :         // Day of Year in Radians (Computed from Input DayOfYear)
    4171         1272 :         Real64 X = DayCorrection * DayOfYear; // Convert Julian date (Day of Year) to angle X
    4172              : 
    4173              :         // Calculate sines and cosines of X
    4174         1272 :         Real64 SinX = std::sin(X);
    4175         1272 :         Real64 CosX = std::cos(X);
    4176              : 
    4177         1272 :         SineSolarDeclination = SineSolDeclCoef[0] + SineSolDeclCoef[1] * SinX + SineSolDeclCoef[2] * CosX + SineSolDeclCoef[3] * (SinX * CosX * 2.0) +
    4178         1272 :                                SineSolDeclCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
    4179         1272 :                                SineSolDeclCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
    4180         1272 :                                SineSolDeclCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
    4181         1272 :                                SineSolDeclCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
    4182         1272 :                                SineSolDeclCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
    4183         1272 :         CosineSolarDeclination = std::sqrt(1.0 - pow_2(SineSolarDeclination));
    4184              : 
    4185         1272 :         EquationOfTime = EqOfTimeCoef[0] + EqOfTimeCoef[1] * SinX + EqOfTimeCoef[2] * CosX + EqOfTimeCoef[3] * (SinX * CosX * 2.0) +
    4186         1272 :                          EqOfTimeCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
    4187         1272 :                          EqOfTimeCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
    4188         1272 :                          EqOfTimeCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
    4189         1272 :                          EqOfTimeCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
    4190         1272 :                          EqOfTimeCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
    4191              : 
    4192         1272 :         AnnVarSolConstant = 1.000047 + 0.000352615 * SinX + 0.0334454 * CosX;
    4193              : 
    4194         1272 :         A = ASHRAE_A_Coef[0] + ASHRAE_A_Coef[1] * SinX + ASHRAE_A_Coef[2] * CosX + ASHRAE_A_Coef[3] * (SinX * CosX * 2.0) +
    4195         1272 :             ASHRAE_A_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_A_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
    4196         1272 :             ASHRAE_A_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
    4197         1272 :             ASHRAE_A_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
    4198         1272 :             ASHRAE_A_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
    4199              : 
    4200              :         // Compute B and C coefficients
    4201              : 
    4202         1272 :         if (state.dataEnvrn->Latitude < 0.0) {
    4203              :             // If in southern hemisphere, compute B and C with a six month time shift.
    4204            0 :             X -= Constant::Pi;
    4205            0 :             SinX = std::sin(X);
    4206            0 :             CosX = std::cos(X);
    4207              :         }
    4208              : 
    4209         1272 :         B = ASHRAE_B_Coef[0] + ASHRAE_B_Coef[1] * SinX + ASHRAE_B_Coef[2] * CosX + ASHRAE_B_Coef[3] * (SinX * CosX * 2.0) +
    4210         1272 :             ASHRAE_B_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_B_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
    4211         1272 :             ASHRAE_B_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
    4212         1272 :             ASHRAE_B_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
    4213         1272 :             ASHRAE_B_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
    4214              : 
    4215         1272 :         C = ASHRAE_C_Coef[0] + ASHRAE_C_Coef[1] * SinX + ASHRAE_C_Coef[2] * CosX + ASHRAE_C_Coef[3] * (SinX * CosX * 2.0) +
    4216         1272 :             ASHRAE_C_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_C_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
    4217         1272 :             ASHRAE_C_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
    4218         1272 :             ASHRAE_C_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
    4219         1272 :             ASHRAE_C_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
    4220         1272 :     }
    4221              : 
    4222        68016 :     void CalculateSunDirectionCosines(EnergyPlusData const &state,
    4223              :                                       Real64 const TimeValue,    // Current Time of Day
    4224              :                                       Real64 const EqOfTime,     // Equation of Time
    4225              :                                       Real64 const SinSolDeclin, // Sine of Solar Declination
    4226              :                                       Real64 const CosSolDeclin, // Cosine of Solar Declination
    4227              :                                       Vector3<Real64> &SUNCOS)
    4228              :     {
    4229              : 
    4230              :         // SUBROUTINE INFORMATION:
    4231              :         //       AUTHOR         George Walton
    4232              :         //       DATE WRITTEN   May 1975
    4233              :         //       MODIFIED       1999 for EnergyPlus
    4234              : 
    4235              :         // PURPOSE OF THIS SUBROUTINE:
    4236              :         // This routine computes the solar direction cosines for hourly
    4237              :         // radiation calculations.
    4238              : 
    4239              :         // REFERENCES:
    4240              :         // "NECAP Engineering Manual", 1974, p.3-117
    4241              : 
    4242        68016 :         EP_SIZE_CHECK(SUNCOS, 3); // NOLINT(misc-static-assert)
    4243              : 
    4244              :         // COMPUTE THE HOUR ANGLE
    4245        68016 :         Real64 H = (15.0 * (12.0 - (TimeValue + EqOfTime)) + (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude)) * Constant::DegToRad;
    4246        68016 :         Real64 COSH = std::cos(H);
    4247              :         // COMPUTE THE COSINE OF THE SOLAR ZENITH ANGLE.
    4248              :         // This is also the Sine of the Solar Altitude Angle
    4249              : 
    4250        68016 :         SUNCOS.z = SinSolDeclin * state.dataEnvrn->SinLatitude + CosSolDeclin * state.dataEnvrn->CosLatitude * COSH;
    4251              : 
    4252        68016 :         if (SUNCOS.z >= DataEnvironment::SunIsUpValue) { // If Sun above horizon, compute other direction cosines
    4253        33882 :             SUNCOS.y = SinSolDeclin * state.dataEnvrn->CosLatitude - CosSolDeclin * state.dataEnvrn->SinLatitude * COSH;
    4254        33882 :             SUNCOS.x = CosSolDeclin * std::sin(H);
    4255              :         } else { // Sun is down, set to 0.0
    4256        34134 :             SUNCOS.x = 0.0;
    4257        34134 :             SUNCOS.y = 0.0;
    4258              :         }
    4259        68016 :     }
    4260              : 
    4261       326122 :     void DetermineSunUpDown(EnergyPlusData &state, Vector3<Real64> &SunCOS)
    4262              :     {
    4263              : 
    4264              :         // SUBROUTINE INFORMATION:
    4265              :         //       AUTHOR         Linda Lawrie
    4266              :         //       DATE WRITTEN   1999
    4267              : 
    4268              :         // PURPOSE OF THIS SUBROUTINE:
    4269              :         // This subroutine determines if the sun is up or down for the current
    4270              :         // hour/timestep.
    4271              : 
    4272              :         // REFERENCES:
    4273              :         // Sun routines from IBLAST, authored by Walton.
    4274              : 
    4275              :         // COMPUTE THE HOUR ANGLE
    4276       326122 :         if (state.dataGlobal->TimeStepsInHour != 1) {
    4277       643576 :             state.dataWeather->HrAngle = (15.0 * (12.0 - (state.dataGlobal->CurrentTime + state.dataWeather->TodayVariables.EquationOfTime)) +
    4278       321788 :                                           (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
    4279              :         } else {
    4280         4334 :             state.dataWeather->HrAngle =
    4281         4334 :                 (15.0 *
    4282         4334 :                      (12.0 - ((state.dataGlobal->CurrentTime + state.dataEnvrn->TS1TimeOffset) + state.dataWeather->TodayVariables.EquationOfTime)) +
    4283         4334 :                  (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
    4284              :         }
    4285       326122 :         Real64 H = state.dataWeather->HrAngle * Constant::DegToRad;
    4286              : 
    4287              :         // Compute the Cosine of the Solar Zenith (Altitude) Angle.
    4288       326122 :         Real64 CosZenith = state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle +
    4289       326122 :                            state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H);
    4290              : 
    4291       326122 :         Real64 SolarZenith = std::acos(CosZenith);
    4292       326122 :         Real64 SinAltitude = state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H) +
    4293       326122 :                              state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle;
    4294       326122 :         Real64 SolarAltitude = std::asin(SinAltitude);
    4295       326122 :         Real64 CosAzimuth = -(state.dataEnvrn->SinLatitude * CosZenith - state.dataWeather->TodayVariables.SinSolarDeclinAngle) /
    4296       326122 :                             (state.dataEnvrn->CosLatitude * std::sin(SolarZenith));
    4297              :         // Following because above can yield invalid cos value.  (e.g. at south pole)
    4298       326122 :         CosAzimuth = max(CosAzimuth, -1.0);
    4299       326122 :         CosAzimuth = min(1.0, CosAzimuth);
    4300       326122 :         Real64 SolarAzimuth = std::acos(CosAzimuth);
    4301              : 
    4302       326122 :         state.dataWeather->SolarAltitudeAngle = SolarAltitude / Constant::DegToRad;
    4303       326122 :         state.dataWeather->SolarAzimuthAngle = SolarAzimuth / Constant::DegToRad;
    4304       326122 :         if (state.dataWeather->HrAngle < 0.0) {
    4305       163219 :             state.dataWeather->SolarAzimuthAngle = 360.0 - state.dataWeather->SolarAzimuthAngle;
    4306              :         }
    4307              : 
    4308       326122 :         SunCOS.z = CosZenith;
    4309       326122 :         state.dataEnvrn->SunIsUpPrevTS = state.dataEnvrn->SunIsUp;
    4310       326122 :         if (CosZenith < DataEnvironment::SunIsUpValue) {
    4311       166429 :             state.dataEnvrn->SunIsUp = false;
    4312       166429 :             SunCOS.y = 0.0;
    4313       166429 :             SunCOS.x = 0.0;
    4314              :         } else {
    4315       159693 :             state.dataEnvrn->SunIsUp = true;
    4316       159693 :             SunCOS.y = state.dataWeather->TodayVariables.SinSolarDeclinAngle * state.dataEnvrn->CosLatitude -
    4317       159693 :                        state.dataWeather->TodayVariables.CosSolarDeclinAngle * state.dataEnvrn->SinLatitude * std::cos(H);
    4318       159693 :             SunCOS.x = state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::sin(H);
    4319              :         }
    4320       326122 :     }
    4321              : 
    4322          115 :     void OpenWeatherFile(EnergyPlusData &state, bool &ErrorsFound)
    4323              :     {
    4324              : 
    4325              :         // SUBROUTINE INFORMATION:
    4326              :         //       AUTHOR         Linda Lawrie
    4327              :         //       DATE WRITTEN   June 1999
    4328              : 
    4329              :         // PURPOSE OF THIS SUBROUTINE:
    4330              :         // This subroutine checks to see if a weather file and what kind of weather file
    4331              :         // exists in the working directory and calls appropriate routines to
    4332              :         // open the files and set up for use.
    4333              : 
    4334          115 :         state.dataWeather->WeatherFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
    4335          115 :         if (state.dataWeather->WeatherFileExists) {
    4336           29 :             OpenEPlusWeatherFile(state, ErrorsFound, true);
    4337              :         }
    4338          115 :     }
    4339              : 
    4340           73 :     void OpenEPlusWeatherFile(EnergyPlusData &state,
    4341              :                               bool &ErrorsFound,       // Will be set to true if errors found
    4342              :                               bool const ProcessHeader // Set to true when headers should be processed (rather than just read)
    4343              :     )
    4344              :     {
    4345              : 
    4346              :         // SUBROUTINE INFORMATION:
    4347              :         //       AUTHOR         Linda K. Lawrie
    4348              :         //       DATE WRITTEN   June 1999
    4349              : 
    4350              :         // PURPOSE OF THIS SUBROUTINE:
    4351              :         // This subroutine opens the EnergyPlus Weather File (in.epw) and processes
    4352              :         // the initial header records.
    4353              : 
    4354              :         // METHODOLOGY EMPLOYED:
    4355              :         // List directed reads, as possible.
    4356              : 
    4357           73 :         state.files.inputWeatherFile.close();
    4358           73 :         state.files.inputWeatherFile.filePath = state.files.inputWeatherFilePath.filePath;
    4359           73 :         state.files.inputWeatherFile.open();
    4360           73 :         if (!state.files.inputWeatherFile.good()) {
    4361            0 :             ShowFatalError(state, "OpenWeatherFile: Could not OPEN EPW Weather File", OptionalOutputFileRef(state.files.eso));
    4362              :         }
    4363              : 
    4364           73 :         if (ProcessHeader) {
    4365              :             // Read in Header Information
    4366              : 
    4367              :             // Headers should come in order
    4368          261 :             for (int typeNum = static_cast<int>(EpwHeaderType::Location); typeNum < static_cast<int>(EpwHeaderType::Num); ++typeNum) {
    4369          232 :                 auto Line = state.files.inputWeatherFile.readLine();
    4370          232 :                 if (Line.eof) {
    4371            0 :                     ShowFatalError(
    4372              :                         state,
    4373            0 :                         format("OpenWeatherFile: Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}",
    4374            0 :                                epwHeaders[typeNum]),
    4375            0 :                         OptionalOutputFileRef(state.files.eso));
    4376              :                 }
    4377              : 
    4378          232 :                 int endcol = len(Line.data);
    4379          232 :                 if (endcol > 0) {
    4380          232 :                     if (int(Line.data[endcol - 1]) == DataSystemVariables::iUnicode_end) {
    4381            0 :                         ShowSevereError(state,
    4382              :                                         "OpenWeatherFile: EPW Weather File appears to be a Unicode or binary file.",
    4383            0 :                                         OptionalOutputFileRef(state.files.eso));
    4384            0 :                         ShowContinueError(state, "...This file cannot be read by this program. Please save as PC or Unix file and try again");
    4385            0 :                         ShowFatalError(state, "Program terminates due to previous condition.");
    4386              :                     }
    4387              :                 }
    4388          232 :                 std::string::size_type const Pos = FindNonSpace(Line.data);
    4389          232 :                 std::string::size_type const HdPos = index(Line.data, epwHeaders[typeNum]);
    4390          232 :                 if (Pos != HdPos) {
    4391            0 :                     continue;
    4392              :                 }
    4393          232 :                 ProcessEPWHeader(state, static_cast<EpwHeaderType>(typeNum), Line.data, ErrorsFound);
    4394          232 :             }
    4395              :         } else { // Header already processed, just read
    4396           44 :             SkipEPlusWFHeader(state);
    4397              :         }
    4398           73 :     }
    4399              : 
    4400          961 :     void CloseWeatherFile(EnergyPlusData &state)
    4401              :     {
    4402          961 :         state.files.inputWeatherFile.close();
    4403          961 :     }
    4404              : 
    4405          112 :     void ResolveLocationInformation(EnergyPlusData &state, bool &ErrorsFound) // Set to true if no location evident
    4406              :     {
    4407              : 
    4408              :         // SUBROUTINE INFORMATION:
    4409              :         //       AUTHOR         Rick Strand
    4410              :         //       DATE WRITTEN   June 1997
    4411              : 
    4412              :         // PURPOSE OF THIS SUBROUTINE:
    4413              :         // This subroutine is currently the main interface between the old data
    4414              :         // structure on the BLAST Weather file and the new data structure contained
    4415              :         // in this module.  At some point, this subroutine will be converted
    4416              :         // to read information directly from the new input file.
    4417              : 
    4418          164 :         if (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather &&
    4419           52 :             state.dataWeather->WeatherFileExists) {
    4420           27 :             if (state.dataWeather->LocationGathered) {
    4421              :                 // See if "matching" location
    4422           26 :                 if (!state.dataWeather->keepUserSiteLocationDefinition) {
    4423           32 :                     if (std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude) > 1.0 ||
    4424           14 :                         std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude) > 1.0 ||
    4425           39 :                         std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone) > 0.0 ||
    4426            7 :                         std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) / max(state.dataEnvrn->Elevation, 1.0) >
    4427              :                             0.10) {
    4428           36 :                         ShowWarningError(state, "Weather file location will be used rather than entered (IDF) Location object.");
    4429           18 :                         ShowContinueError(state, format("..Location object={}", state.dataWeather->LocationTitle));
    4430           18 :                         ShowContinueError(state, format("..Weather File Location={}", state.dataEnvrn->WeatherFileLocationTitle));
    4431           36 :                         ShowContinueError(
    4432              :                             state,
    4433           36 :                             format("..due to location differences, Latitude difference=[{:.2R}] degrees, Longitude difference=[{:.2R}] degrees.",
    4434           18 :                                    std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude),
    4435           18 :                                    std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude)));
    4436           36 :                         ShowContinueError(state,
    4437           36 :                                           format("..Time Zone difference=[{:.1R}] hour(s), Elevation difference=[{:.2R}] percent, [{:.2R}] meters.",
    4438           18 :                                                  std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone),
    4439           36 :                                                  std::abs((state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) /
    4440           18 :                                                           max(state.dataEnvrn->Elevation, 1.0) * 100.0),
    4441           36 :                                                  std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation)));
    4442              :                     }
    4443              :                 }
    4444              :             }
    4445           27 :             if (!state.dataWeather->keepUserSiteLocationDefinition) {
    4446           26 :                 state.dataWeather->LocationTitle = state.dataEnvrn->WeatherFileLocationTitle;
    4447           26 :                 state.dataEnvrn->Latitude = state.dataWeather->WeatherFileLatitude;
    4448           26 :                 state.dataEnvrn->Longitude = state.dataWeather->WeatherFileLongitude;
    4449           26 :                 state.dataEnvrn->TimeZoneNumber = state.dataWeather->WeatherFileTimeZone;
    4450           26 :                 state.dataEnvrn->Elevation = state.dataWeather->WeatherFileElevation;
    4451              :             }
    4452           85 :         } else if (!state.dataWeather->LocationGathered) {
    4453            1 :             state.dataWeather->LocationTitle = "Not Entered";
    4454            2 :             ShowSevereError(state, "No Location given. Must have location information for simulation.");
    4455            1 :             ErrorsFound = true;
    4456              :         }
    4457              : 
    4458          112 :         if (!ErrorsFound) {
    4459          111 :             state.dataEnvrn->StdBaroPress = DataEnvironment::StdPressureSeaLevel * std::pow(1.0 - 2.25577e-05 * state.dataEnvrn->Elevation, 5.2559);
    4460          222 :             state.dataEnvrn->StdRhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(
    4461          111 :                 state, state.dataEnvrn->StdBaroPress, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
    4462              :             // Write Final Location Information to the initialization output file
    4463              :             static constexpr std::string_view LocHdFormat(
    4464              :                 "! <Site:Location>, Location Name, Latitude {N+/S- Deg}, Longitude {E+/W- Deg},  Time Zone Number "
    4465              :                 "{GMT+/-}, Elevation {m},  Standard Pressure at Elevation {Pa}, Standard RhoAir at Elevation\n");
    4466          111 :             print(state.files.eio, "{}", LocHdFormat);
    4467              : 
    4468              :             static constexpr std::string_view LocFormat("Site:Location,{},{:.2R},{:.2R},{:.2R},{:.2R},{:.0R},{:.4R}\n");
    4469          111 :             print(state.files.eio,
    4470              :                   LocFormat,
    4471          111 :                   state.dataWeather->LocationTitle,
    4472          111 :                   state.dataEnvrn->Latitude,
    4473          111 :                   state.dataEnvrn->Longitude,
    4474          111 :                   state.dataEnvrn->TimeZoneNumber,
    4475          111 :                   state.dataEnvrn->Elevation,
    4476          111 :                   state.dataEnvrn->StdBaroPress,
    4477          111 :                   state.dataEnvrn->StdRhoAir);
    4478              :         }
    4479          112 :     }
    4480              : 
    4481          113 :     void CheckLocationValidity(EnergyPlusData &state)
    4482              :     {
    4483              : 
    4484              :         // SUBROUTINE INFORMATION:
    4485              :         //       AUTHOR         Rick Strand
    4486              :         //       DATE WRITTEN   June 1997
    4487              : 
    4488              :         // PURPOSE OF THIS SUBROUTINE:
    4489              :         // This subroutine is checks to see whether the user specified location
    4490              :         // or the weather file location (if one exists) is valid.  The standard
    4491              :         // time meridian is also calculated and compared to the user supplied
    4492              :         // or weather file time zone number.
    4493              : 
    4494          113 :         bool LocationError = false; // Set to true if there is a problem detected
    4495              : 
    4496          113 :         if ((state.dataEnvrn->Latitude == -999.0) && (state.dataEnvrn->Longitude == -999.0) && (state.dataEnvrn->TimeZoneNumber != -999.0)) {
    4497            0 :             ShowSevereError(state, "No location specified");
    4498            0 :             LocationError = true;
    4499              :         }
    4500              : 
    4501          113 :         if ((state.dataEnvrn->Latitude < -90.0) || (state.dataEnvrn->Latitude > 90.0)) {
    4502            0 :             ShowSevereError(state, format("Latitude must be between -90 and 90; Entered={:.2R}", state.dataEnvrn->Latitude));
    4503            0 :             LocationError = true;
    4504              :         }
    4505              : 
    4506          113 :         if ((state.dataEnvrn->Longitude < -180.0) || (state.dataEnvrn->Longitude > 180.0)) {
    4507            0 :             ShowSevereError(state, format("Longitude must be between -180 and 180; Entered={:.2R}", state.dataEnvrn->Longitude));
    4508            0 :             LocationError = true;
    4509              :         }
    4510              : 
    4511          113 :         if ((state.dataEnvrn->TimeZoneNumber < -12.00) || (state.dataEnvrn->TimeZoneNumber > 14.00)) {
    4512            0 :             ShowSevereError(state, format("Time Zone must be between -12 and +14; Entered={:.2R}", state.dataEnvrn->TimeZoneNumber));
    4513            0 :             LocationError = true;
    4514              :         }
    4515              : 
    4516          113 :         Real64 const StdTimeMerid = GetSTM(state.dataEnvrn->Longitude); // Standard time meridian.
    4517              : 
    4518              :         // Compare the standard time meridian with the time zone number.  If
    4519              :         // different, notify the user.  If StdTimeMerid couldn't be calculated,
    4520              :         // produce an error message.
    4521              : 
    4522          113 :         if (state.dataEnvrn->varyingLocationLatSched != nullptr || state.dataEnvrn->varyingLocationLongSched != nullptr) {
    4523              :             // don't do any warnings, the building is moving
    4524          113 :         } else if (StdTimeMerid >= -12.0 && StdTimeMerid <= 12.0) {
    4525          113 :             if (state.dataEnvrn->TimeZoneNumber != StdTimeMerid) {
    4526              :                 // Difference between Standard Time Meridian and TimeZone
    4527            6 :                 Real64 const DiffCalc = std::abs(state.dataEnvrn->TimeZoneNumber - StdTimeMerid);
    4528            6 :                 if (DiffCalc > 1.0 && DiffCalc < 24.0) {
    4529            0 :                     if (DiffCalc < 3.0) {
    4530            0 :                         ShowWarningError(state,
    4531            0 :                                          format("Standard Time Meridian and Time Zone differ by more than 1, Difference=\"{:.1R}\"", DiffCalc));
    4532            0 :                         ShowContinueError(state, "Solar Positions may be incorrect");
    4533              :                     } else {
    4534            0 :                         ShowSevereError(state, format("Standard Time Meridian and Time Zone differ by more than 2, Difference=\"{:.1R}\"", DiffCalc));
    4535            0 :                         ShowContinueError(state, "Solar Positions will be incorrect");
    4536              :                         //          LocationError=.TRUE.
    4537              :                     }
    4538              :                 }
    4539              :             }
    4540          113 :         } else {
    4541            0 :             ShowSevereError(state, "Unable to calculate the standard time meridian");
    4542            0 :             LocationError = true;
    4543              :         }
    4544              : 
    4545              :         // Error handling:  if there are any errors in the location information
    4546              :         // the simulation must be terminated
    4547              : 
    4548          113 :         if (LocationError) {
    4549            0 :             ShowFatalError(state, "Due to previous error condition, simulation terminated");
    4550              :         }
    4551              : 
    4552          113 :         if (state.dataEnvrn->TimeZoneNumber <= 12.00) {
    4553          113 :             state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0;
    4554              :         } else {
    4555            0 :             state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0 - 360.0;
    4556              :         }
    4557          113 :         state.dataEnvrn->SinLatitude = std::sin(Constant::DegToRad * state.dataEnvrn->Latitude);
    4558          113 :         state.dataEnvrn->CosLatitude = std::cos(Constant::DegToRad * state.dataEnvrn->Latitude);
    4559              : 
    4560          113 :         if (state.dataEnvrn->Latitude == 0.0 && state.dataEnvrn->Longitude == 0.0 && state.dataEnvrn->TimeZoneNumber == 0.0) {
    4561            3 :             ShowWarningError(state,
    4562              :                              "Did you realize that you have Latitude=0.0, Longitude=0.0 and TimeZone=0.0?  Your building site is in the middle of "
    4563              :                              "the Atlantic Ocean.");
    4564              :         }
    4565          113 :     }
    4566              : 
    4567           50 :     void CheckWeatherFileValidity(EnergyPlusData &state)
    4568              :     {
    4569              : 
    4570              :         // SUBROUTINE INFORMATION:
    4571              :         //       AUTHOR         Linda Lawrie
    4572              :         //       DATE WRITTEN   February 1977
    4573              :         //       MODIFIED       June 1997 (RKS)
    4574              : 
    4575              :         // PURPOSE OF THIS SUBROUTINE:
    4576              :         // This subroutine contains a portion of the legacy subroutine CKBLDE.
    4577              :         // The main purpose of this routine is to check the validity of the
    4578              :         // weather dates provided by the user and the attached weather file.
    4579              :         // These functions may eventually be pushed to an interface.  This
    4580              :         // routine also sends the weather file header information at the
    4581              :         // Environment derived type.
    4582              : 
    4583           50 :         if (!state.dataWeather->WeatherFileExists) { // No weather file exists but the user requested one--print error message
    4584              : 
    4585           25 :             if (state.dataGlobal->DoWeathSim) {
    4586            2 :                 ShowSevereError(state, "GetNextEnvironment: Weather Environment(s) requested, but no weather file found");
    4587            3 :                 ShowFatalError(state, "Due to previous error condition, simulation terminated");
    4588              :             }
    4589              : 
    4590              :         } // ... end of WeatherFileExists IF-THEN
    4591           49 :     }
    4592              : 
    4593          107 :     void ReportOutputFileHeaders(EnergyPlusData &state)
    4594              :     {
    4595              : 
    4596              :         // SUBROUTINE INFORMATION:
    4597              :         //       AUTHOR         Rick Strand
    4598              :         //       DATE WRITTEN   June 1997
    4599              :         //       MODIFIED       December 2017; Jason DeGraw
    4600              : 
    4601              :         // PURPOSE OF THIS SUBROUTINE:
    4602              :         // This subroutine prints out the necessary header information required
    4603              :         // by the EnergyPlus output file format.  This subroutine can be
    4604              :         // replicated in any other modules which must send data to the output
    4605              :         // file.
    4606              : 
    4607              :         // METHODOLOGY EMPLOYED:
    4608              :         // For each report, the report flag integer must be saved from the
    4609              :         // global report number counter.  Then, the report counter must be
    4610              :         // incremented.  Finally, the header information for the report must
    4611              :         // be sent to the output file.
    4612              : 
    4613              :         using OutputProcessor::ReportFreq;
    4614              : 
    4615              :         static constexpr std::string_view EnvironmentString(",5,Environment Title[],Latitude[deg],Longitude[deg],Time Zone[],Elevation[m]");
    4616              : 
    4617              :         static constexpr std::array<std::string_view, (int)ReportFreq::Num> freqStrings = {
    4618              :             "", // No EachCall string
    4619              :             ",8,Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],Hour[],StartMinute[],EndMinute[],DayType",
    4620              :             "", // No Hour string
    4621              :             ",5,Cumulative Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],DayType  ! When Daily ",
    4622              :             ",2,Cumulative Days of Simulation[],Month[]  ! When Monthly ",
    4623              :             ",1,Cumulative Days of Simulation[] ! When Run Period ",
    4624              :             ",1,Calendar Year of Simulation[] ! When Annual "};
    4625              : 
    4626          107 :         auto &op = state.dataOutputProcessor;
    4627              : 
    4628          107 :         state.dataWeather->EnvironmentReportNbr = ++op->ReportNumberCounter;
    4629          107 :         if (state.dataWeather->EnvironmentReportNbr != 1) { //  problem
    4630            0 :             ShowFatalError(state, "ReportOutputFileHeaders: Assigned report number for Environment title is not 1.  Contact Support.");
    4631              :         }
    4632          107 :         state.dataWeather->EnvironmentReportChr = fmt::to_string(state.dataWeather->EnvironmentReportNbr);
    4633          107 :         strip(state.dataWeather->EnvironmentReportChr);
    4634          107 :         print(state.files.eso, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
    4635          107 :         print(state.files.mtr, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
    4636              : 
    4637              :         // TImeStep and Hour share a stamp
    4638          107 :         op->freqStampReportNums[(int)ReportFreq::Hour] = op->freqStampReportNums[(int)ReportFreq::TimeStep] = ++op->ReportNumberCounter;
    4639          107 :         print(state.files.eso, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
    4640          107 :         print(state.files.mtr, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
    4641              : 
    4642          535 :         for (ReportFreq freq : {ReportFreq::Day, ReportFreq::Month, ReportFreq::Simulation, ReportFreq::Year}) {
    4643          428 :             op->freqStampReportNums[(int)freq] = ++op->ReportNumberCounter;
    4644          428 :             print(state.files.eso, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Report Variables Requested");
    4645          428 :             print(state.files.mtr, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Meters Requested");
    4646              :         }
    4647          107 :     }
    4648              : 
    4649       326115 :     void ReportWeatherAndTimeInformation(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
    4650              :     {
    4651              : 
    4652              :         // SUBROUTINE INFORMATION:
    4653              :         //       AUTHOR         Rick Strand
    4654              :         //       DATE WRITTEN   June 1997
    4655              : 
    4656              :         // PURPOSE OF THIS SUBROUTINE:
    4657              :         // This subroutine is the main driver of the weather reporting.  This
    4658              :         // routine is also responsible for printing the time and environment
    4659              :         // stamps.
    4660              : 
    4661              :         // METHODOLOGY EMPLOYED:
    4662              :         // Reporting is only done for non-warmup days.  The environment stamp
    4663              :         // is only reported at the beginning of an environment, but after the
    4664              :         // warmup days (to allow all modules to print the report headers to the
    4665              :         // output file.  This is controlled by the PrintEnvrnStamp variable
    4666              :         // which is passed in and reset if necessary.
    4667              : 
    4668              :         // Report the time stamp and the current weather to the output file
    4669              : 
    4670       326115 :         if (!state.dataGlobal->WarmupFlag && !state.dataWeather->RPReadAllWeatherData) { // Write the required output information
    4671              : 
    4672              :             // The first time through in a non-warmup day, the environment header
    4673              :             // must be printed.  This must be done here and not in the generic
    4674              :             // DataGlobals::BeginEnvrnFlag block above because other modules in the simulation
    4675              :             // must also print out header information.  This can be done during
    4676              :             // the simulation warmup if the environment stamp printing is delayed
    4677              :             // until the warmup is completed.  The stamp should only be printed once
    4678              :             // per environment (set/reset of PrintEnvrnStamp).  In addition, before
    4679              :             // the first environment, the end of the header block flag must also be
    4680              :             // sent to the output file.
    4681              : 
    4682        34684 :             if (printEnvrnStamp) {
    4683              : 
    4684        16044 :                 if (state.dataReportFlag->PrintEndDataDictionary && state.dataGlobal->DoOutputReporting) {
    4685              :                     static constexpr std::string_view EndOfHeaderString("End of Data Dictionary"); // End of data dictionary marker
    4686           73 :                     print(state.files.eso, "{}\n", EndOfHeaderString);
    4687           73 :                     print(state.files.mtr, "{}\n", EndOfHeaderString);
    4688           73 :                     state.dataReportFlag->PrintEndDataDictionary = false;
    4689              :                 }
    4690        16044 :                 if (state.dataGlobal->DoOutputReporting) {
    4691          129 :                     std::string const &Title = state.dataWeather->Environment(state.dataWeather->Envrn).Title;
    4692              :                     static constexpr std::string_view EnvironmentStampFormatStr(
    4693              :                         "{},{},{:7.2F},{:7.2F},{:7.2F},{:7.2F}\n"); // Format descriptor for environ stamp
    4694          129 :                     print(state.files.eso,
    4695              :                           EnvironmentStampFormatStr,
    4696          129 :                           state.dataWeather->EnvironmentReportChr,
    4697              :                           Title,
    4698          129 :                           state.dataEnvrn->Latitude,
    4699          129 :                           state.dataEnvrn->Longitude,
    4700          129 :                           state.dataEnvrn->TimeZoneNumber,
    4701          129 :                           state.dataEnvrn->Elevation);
    4702          129 :                     print(state.files.mtr,
    4703              :                           EnvironmentStampFormatStr,
    4704          129 :                           state.dataWeather->EnvironmentReportChr,
    4705              :                           Title,
    4706          129 :                           state.dataEnvrn->Latitude,
    4707          129 :                           state.dataEnvrn->Longitude,
    4708          129 :                           state.dataEnvrn->TimeZoneNumber,
    4709          129 :                           state.dataEnvrn->Elevation);
    4710          129 :                     printEnvrnStamp = false;
    4711              :                 }
    4712              :             }
    4713              :         } // ... end of .NOT.WarmupFlag IF-THEN block.
    4714       326115 :     }
    4715              : 
    4716          111 :     void ReadUserWeatherInput(EnergyPlusData &state)
    4717              :     {
    4718              : 
    4719              :         // SUBROUTINE INFORMATION:
    4720              :         //       AUTHOR         Richard Liesen
    4721              :         //       DATE WRITTEN   September 1997
    4722              : 
    4723              :         // PURPOSE OF THIS SUBROUTINE:
    4724              :         // This subroutine is the main driver of the weather manager module.
    4725              :         // It controls the assignment of weather related global variables as
    4726              :         // well as the reads and writes for retrieving weather information.
    4727              : 
    4728              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4729          111 :         bool ErrorsFound(false);
    4730              : 
    4731              :         // Get the number of design days and annual runs from user inpout
    4732          111 :         state.dataEnvrn->TotDesDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:DesignDay");
    4733          111 :         int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
    4734          111 :         int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
    4735          111 :         state.dataWeather->TotRunPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriod");
    4736          111 :         state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
    4737          111 :         state.dataGlobal->WeathSimReq = state.dataWeather->TotRunPers > 0;
    4738          111 :         state.dataWeather->TotReportPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Output:Table:ReportPeriod");
    4739              : #ifdef GET_OUT
    4740              :         state.dataWeather->SPSiteScheduleNamePtr.allocate(state.dataEnvrn->TotDesDays * 5);
    4741              :         state.dataWeather->SPSiteScheduleUnits.allocate(state.dataEnvrn->TotDesDays * 5);
    4742              : 
    4743              :         state.dataWeather->SPSiteScheduleNamePtr = 0;
    4744              :         state.dataWeather->SPSiteScheduleUnits = "";
    4745              : #endif //
    4746              :        // Allocate the Design Day and Environment array to the # of DD's or/and
    4747              :        // Annual runs on input file
    4748          111 :         state.dataWeather->DesignDay.allocate(state.dataEnvrn->TotDesDays);
    4749          111 :         state.dataWeather->Environment.allocate(state.dataWeather->NumOfEnvrn);
    4750              : 
    4751              :         // Set all Environments to DesignDay and then the weather environment will be set
    4752              :         //  in the get annual run data subroutine
    4753          288 :         for (int Env = 1; Env <= state.dataEnvrn->TotDesDays; ++Env) {
    4754          177 :             state.dataWeather->Environment(Env).KindOfEnvrn = Constant::KindOfSim::DesignDay;
    4755              :         }
    4756          111 :         for (int Env = 1; Env <= RPD1 + RPD2; ++Env) {
    4757            0 :             if (!state.dataSysVars->DDOnly) {
    4758            0 :                 state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
    4759              :             } else {
    4760            0 :                 state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
    4761              :             }
    4762              :         }
    4763          164 :         for (int Env = 1; Env <= state.dataWeather->TotRunPers; ++Env) {
    4764           53 :             state.dataWeather->Environment(state.dataEnvrn->TotDesDays + RPD1 + RPD2 + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
    4765              :         }
    4766              : 
    4767          111 :         if (state.dataEnvrn->TotDesDays >= 1) {
    4768          103 :             GetDesignDayData(state, state.dataEnvrn->TotDesDays, ErrorsFound);
    4769              :         }
    4770              : 
    4771          111 :         if (RPD1 >= 1 || RPD2 >= 1) {
    4772            0 :             GetRunPeriodDesignData(state, ErrorsFound);
    4773              :         }
    4774              : 
    4775              :         // the last environment(s) is designated the weather environment if an annual run
    4776              :         // is selected.  All of the design systems is done from the design day info
    4777              :         // which will have to be completed to run the annual run.
    4778          111 :         if (state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
    4779           50 :             GetRunPeriodData(state, state.dataWeather->TotRunPers, ErrorsFound);
    4780              :         }
    4781              : 
    4782          111 :         if (state.dataWeather->TotReportPers > 0) {
    4783            0 :             GetReportPeriodData(state, state.dataWeather->TotReportPers, ErrorsFound);
    4784            0 :             GroupReportPeriodByType(state, state.dataWeather->TotReportPers);
    4785              :         }
    4786              : 
    4787          111 :         if (state.dataSysVars->FullAnnualRun) {
    4788              :             // GetRunPeriodData may have reset the value of TotRunPers
    4789            0 :             state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
    4790              :         }
    4791              : 
    4792          111 :         if (RPD1 >= 1 || RPD2 >= 1 || state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
    4793           50 :             GetSpecialDayPeriodData(state, ErrorsFound);
    4794           50 :             GetDSTData(state, ErrorsFound);
    4795           50 :             if (state.dataWeather->IDFDaylightSaving) {
    4796            0 :                 state.dataWeather->DST = state.dataWeather->IDFDST;
    4797              :             }
    4798              :         }
    4799              : 
    4800          111 :         GetLocationInfo(state, ErrorsFound);
    4801              : 
    4802          111 :         GetGroundTemps(state);
    4803              : 
    4804          111 :         GetGroundReflectances(state, ErrorsFound);
    4805              : 
    4806          111 :         GetSnowGroundRefModifiers(state, ErrorsFound);
    4807              : 
    4808          111 :         GetWaterMainsTemperatures(state, ErrorsFound);
    4809              : 
    4810          111 :         GetWeatherStation(state, ErrorsFound);
    4811              : 
    4812          111 :         SetupEnvironmentTypes(state);
    4813              : 
    4814          111 :         GetWeatherProperties(state, ErrorsFound);
    4815              : #ifdef GET_OUT
    4816              :         // Deallocate ones used for schedule pointers
    4817              :         state.dataWeather->SPSiteScheduleNamePtr.deallocate();
    4818              :         state.dataWeather->SPSiteScheduleUnits.deallocate();
    4819              : #endif //
    4820          111 :         if (ErrorsFound) {
    4821            0 :             ShowFatalError(state, "GetWeatherInput: Above errors cause termination");
    4822              :         }
    4823          111 :     }
    4824              : 
    4825           55 :     static int findYearForWeekday(int const month, int const day, Sched::DayType const weekday)
    4826              :     {
    4827              :         // Find a year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
    4828              :         // the date with the weekday specified.
    4829              : 
    4830              :         // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
    4831              :         static std::array<int, 13> const defaultYear{{2013, 2014, 2015, 2010, 2011, 2017, 2007, 2013, 2014, 2015, 2010, 2011, 2017}};
    4832              : 
    4833           55 :         int rem = calculateDayOfYear(month, day) % 7;
    4834           55 :         return defaultYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
    4835              :     }
    4836              : 
    4837            2 :     static int findLeapYearForWeekday(int const month, int const day, Sched::DayType const weekday)
    4838              :     {
    4839              :         // Find a leap year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
    4840              :         // the date with the weekday specified.
    4841              : 
    4842              :         // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
    4843              :         static std::array<int, 13> const defaultLeapYear{{2008, 1992, 2004, 2016, 2000, 2012, 1996, 2008, 1992, 2004, 2016, 2000, 2012}};
    4844              : 
    4845            2 :         int rem = calculateDayOfYear(month, day, true) % 7;
    4846            2 :         return defaultLeapYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
    4847              :     }
    4848              : 
    4849            5 :     void GetReportPeriodData(EnergyPlusData &state,
    4850              :                              int nReportPeriods, // Total number of Report Periods requested
    4851              :                              bool &ErrorsFound)
    4852              :     {
    4853            5 :         constexpr std::string_view routineName = "GetReportPeriodData";
    4854            5 :         state.dataWeather->ReportPeriodInput.allocate(nReportPeriods);
    4855              : 
    4856            5 :         auto const &ipsc = state.dataIPShortCut;
    4857            5 :         ipsc->cCurrentModuleObject = "Output:Table:ReportPeriod";
    4858            5 :         int Count = 0;
    4859              :         int NumAlpha;   // Number of alphas being input
    4860              :         int NumNumeric; // Number of numbers being input
    4861              :         int IOStat;     // IO Status when calling get input subroutine
    4862           15 :         for (int i = 1; i <= nReportPeriods; ++i) {
    4863           20 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    4864           10 :                                                                      ipsc->cCurrentModuleObject,
    4865              :                                                                      i,
    4866           10 :                                                                      ipsc->cAlphaArgs,
    4867              :                                                                      NumAlpha,
    4868           10 :                                                                      ipsc->rNumericArgs,
    4869              :                                                                      NumNumeric,
    4870              :                                                                      IOStat,
    4871           10 :                                                                      ipsc->lNumericFieldBlanks,
    4872           10 :                                                                      ipsc->lAlphaFieldBlanks,
    4873           10 :                                                                      ipsc->cAlphaFieldNames,
    4874           10 :                                                                      ipsc->cNumericFieldNames);
    4875              : 
    4876           10 :             std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
    4877           10 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
    4878              :             // A1, \field Name
    4879           10 :             if (std::find_if(state.dataWeather->ReportPeriodInput.begin(),
    4880           10 :                              state.dataWeather->ReportPeriodInput.end(),
    4881           30 :                              [&newName](ReportPeriodData const &rpd) { return newName == rpd.title; }) !=
    4882           10 :                 state.dataWeather->ReportPeriodInput.end()) {
    4883            1 :                 ShowSevereDuplicateName(state, eoh);
    4884            1 :                 ErrorsFound = true;
    4885              :             }
    4886              : 
    4887           10 :             ++Count;
    4888              : 
    4889           10 :             auto &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
    4890              :             // Loop = RP + Ptr;
    4891              :             // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "ReportPeriod 1" anyways
    4892              :             // which is fine for our reporting below
    4893           10 :             reportPeriodInput.title = newName;
    4894              :             // A2, \field Report Name
    4895           10 :             reportPeriodInput.reportName = ipsc->cAlphaArgs(2);
    4896              : 
    4897              :             // set the start and end day of month from user input
    4898              :             // N1, \field Begin Year
    4899              :             // N2, \field Begin Month
    4900              :             // N3, \field Begin Day of Month
    4901              :             // N4, \field Begin Hour of Day
    4902              :             // N5, \field End Year
    4903              :             // N6, \field End Month
    4904              :             // N7, \field End Day of Month
    4905              :             // N8; \field End Hour of Day
    4906           10 :             reportPeriodInput.startYear = int(ipsc->rNumericArgs(1));
    4907           10 :             reportPeriodInput.startMonth = int(ipsc->rNumericArgs(2));
    4908           10 :             reportPeriodInput.startDay = int(ipsc->rNumericArgs(3));
    4909           10 :             reportPeriodInput.startHour = int(ipsc->rNumericArgs(4));
    4910           10 :             reportPeriodInput.endYear = int(ipsc->rNumericArgs(5));
    4911           10 :             reportPeriodInput.endMonth = int(ipsc->rNumericArgs(6));
    4912           10 :             reportPeriodInput.endDay = int(ipsc->rNumericArgs(7));
    4913           10 :             reportPeriodInput.endHour = int(ipsc->rNumericArgs(8));
    4914              : 
    4915              :             // Validate year inputs
    4916           10 :             if (reportPeriodInput.startYear == 0) {
    4917           10 :                 if (reportPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
    4918            0 :                     ShowSevereError(state,
    4919            0 :                                     format("{}: object={}, end year cannot be specified if the start year is not.",
    4920            0 :                                            ipsc->cCurrentModuleObject,
    4921            0 :                                            reportPeriodInput.title));
    4922            0 :                     ErrorsFound = true;
    4923              :                 }
    4924            0 :             } else if (reportPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
    4925            0 :                 ShowSevereError(state,
    4926            0 :                                 format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
    4927            0 :                                        ipsc->cCurrentModuleObject,
    4928            0 :                                        reportPeriodInput.title,
    4929            0 :                                        reportPeriodInput.startYear));
    4930            0 :                 ErrorsFound = true;
    4931              :             }
    4932              : 
    4933           10 :             if (reportPeriodInput.endYear != 0 && reportPeriodInput.startYear > reportPeriodInput.endYear) {
    4934            0 :                 ShowSevereError(state,
    4935            0 :                                 format("{}: object={}, start year ({}) is after the end year ({}).",
    4936            0 :                                        ipsc->cCurrentModuleObject,
    4937            0 :                                        reportPeriodInput.title,
    4938            0 :                                        reportPeriodInput.startYear,
    4939            0 :                                        reportPeriodInput.endYear));
    4940            0 :                 ErrorsFound = true;
    4941              :             }
    4942              : 
    4943           10 :             reportPeriodInput.startJulianDate =
    4944           10 :                 computeJulianDate(reportPeriodInput.startYear, reportPeriodInput.startMonth, reportPeriodInput.startDay);
    4945           10 :             reportPeriodInput.endJulianDate = computeJulianDate(reportPeriodInput.endYear, reportPeriodInput.endMonth, reportPeriodInput.endDay);
    4946           10 :         }
    4947            5 :     }
    4948              : 
    4949            4 :     void GroupReportPeriodByType(EnergyPlusData &state, const int nReportPeriods)
    4950              :     {
    4951              :         // transfer data from the reporting period object to the corresponding report period type arrays
    4952              :         // ThermalResilienceSummary, CO2ResilienceSummary, VisualResilienceSummary, and AllResilienceSummaries
    4953           13 :         for (auto const &reportPeriodInput : state.dataWeather->ReportPeriodInput) {
    4954              : 
    4955            9 :             if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
    4956            4 :                 ++state.dataWeather->TotThermalReportPers;
    4957            5 :             } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
    4958            3 :                 ++state.dataWeather->TotCO2ReportPers;
    4959            2 :             } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
    4960            2 :                 ++state.dataWeather->TotVisualReportPers;
    4961            0 :             } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
    4962            0 :                 ++state.dataWeather->TotThermalReportPers;
    4963            0 :                 ++state.dataWeather->TotCO2ReportPers;
    4964            0 :                 ++state.dataWeather->TotVisualReportPers;
    4965              :             }
    4966              :         }
    4967              : 
    4968            4 :         state.dataWeather->ThermalReportPeriodInput.allocate(state.dataWeather->TotThermalReportPers);
    4969            4 :         state.dataWeather->CO2ReportPeriodInput.allocate(state.dataWeather->TotCO2ReportPers);
    4970            4 :         state.dataWeather->VisualReportPeriodInput.allocate(state.dataWeather->TotVisualReportPers);
    4971              : 
    4972           13 :         for (int i = 1, iThermal = 1, iVisual = 1, iCO2 = 1; i <= nReportPeriods; ++i) {
    4973            9 :             auto const &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
    4974            9 :             if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
    4975            4 :                 state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
    4976            4 :                 ++iThermal;
    4977            5 :             } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
    4978            3 :                 state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
    4979            3 :                 ++iCO2;
    4980            2 :             } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
    4981            2 :                 state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
    4982            2 :                 ++iVisual;
    4983            0 :             } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
    4984            0 :                 state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
    4985            0 :                 ++iThermal;
    4986            0 :                 state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
    4987            0 :                 ++iCO2;
    4988            0 :                 state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
    4989            0 :                 ++iVisual;
    4990              :             }
    4991              :         }
    4992            4 :     }
    4993              : 
    4994           55 :     void GetRunPeriodData(EnergyPlusData &state,
    4995              :                           int nRunPeriods, // Total number of Run Periods requested
    4996              :                           bool &ErrorsFound)
    4997              :     {
    4998              : 
    4999              :         // SUBROUTINE INFORMATION:
    5000              :         //       AUTHOR         Richard Liesen
    5001              :         //       DATE WRITTEN   October 1997
    5002              :         //       MODIFIED       February 1999, Add multiple run periods, Change name.
    5003              :         //                      March 2012, LKL, Add features to object; New "actual weather" object;
    5004              : 
    5005              :         // PURPOSE OF THIS SUBROUTINE:
    5006              :         // This subroutine gets the run period info from User input and the
    5007              :         //  simulation dates
    5008              : 
    5009           55 :         constexpr std::string_view routineName = "GetRunPeriodData";
    5010              :         // Call Input Get routine to retrieve annual run data
    5011           55 :         state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
    5012              : 
    5013           55 :         auto const &ipsc = state.dataIPShortCut;
    5014           55 :         ipsc->cCurrentModuleObject = "RunPeriod";
    5015           55 :         int Count = 0;
    5016              :         int NumAlpha;   // Number of alphas being input
    5017              :         int NumNumeric; // Number of numbers being input
    5018              :         int IOStat;     // IO Status when calling get input subroutine
    5019          119 :         for (int i = 1; i <= nRunPeriods; ++i) {
    5020          128 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    5021           64 :                                                                      ipsc->cCurrentModuleObject,
    5022              :                                                                      i,
    5023           64 :                                                                      ipsc->cAlphaArgs,
    5024              :                                                                      NumAlpha,
    5025           64 :                                                                      ipsc->rNumericArgs,
    5026              :                                                                      NumNumeric,
    5027              :                                                                      IOStat,
    5028           64 :                                                                      ipsc->lNumericFieldBlanks,
    5029           64 :                                                                      ipsc->lAlphaFieldBlanks,
    5030           64 :                                                                      ipsc->cAlphaFieldNames,
    5031           64 :                                                                      ipsc->cNumericFieldNames);
    5032              : 
    5033              :             // A1, \field Name
    5034           64 :             std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
    5035           64 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
    5036              : 
    5037           64 :             if (std::find_if(state.dataWeather->RunPeriodInput.begin(),
    5038           64 :                              state.dataWeather->RunPeriodInput.end(),
    5039          178 :                              [&newName](RunPeriodData const &rpd) { return rpd.title == newName; }) != state.dataWeather->RunPeriodInput.end()) {
    5040            0 :                 ShowSevereDuplicateName(state, eoh);
    5041            0 :                 ErrorsFound = true;
    5042              :             }
    5043              : 
    5044           64 :             ++Count;
    5045              :             // Loop = RP + Ptr;
    5046              :             // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "RUNPERIOD 1" anyways
    5047              :             // which is fine for our reporting below
    5048           64 :             auto &runPeriodInput = state.dataWeather->RunPeriodInput(i);
    5049           64 :             runPeriodInput.title = ipsc->cAlphaArgs(1);
    5050              : 
    5051              :             // set the start and end day of month from user input
    5052              :             // N1 , \field Begin Month
    5053              :             // N2 , \field Begin Day of Month
    5054              :             // N3,  \field Start Year
    5055              :             // N4 , \field End Month
    5056              :             // N5 , \field End Day of Month
    5057              :             // N6,  \field End Year
    5058           64 :             runPeriodInput.startMonth = int(ipsc->rNumericArgs(1));
    5059           64 :             runPeriodInput.startDay = int(ipsc->rNumericArgs(2));
    5060           64 :             runPeriodInput.startYear = int(ipsc->rNumericArgs(3));
    5061           64 :             runPeriodInput.endMonth = int(ipsc->rNumericArgs(4));
    5062           64 :             runPeriodInput.endDay = int(ipsc->rNumericArgs(5));
    5063           64 :             runPeriodInput.endYear = int(ipsc->rNumericArgs(6));
    5064           64 :             runPeriodInput.TreatYearsAsConsecutive = true;
    5065              : 
    5066           64 :             if (state.dataSysVars->FullAnnualRun && i == 1) {
    5067            0 :                 runPeriodInput.startMonth = 1;
    5068            0 :                 runPeriodInput.startDay = 1;
    5069            0 :                 runPeriodInput.endMonth = 12;
    5070            0 :                 runPeriodInput.endDay = 31;
    5071              :             }
    5072              : 
    5073              :             // Validate year inputs
    5074           64 :             if (runPeriodInput.startYear == 0) {
    5075           58 :                 if (runPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
    5076            2 :                     ShowSevereError(state,
    5077            2 :                                     format("{}: object={}, end year cannot be specified if the start year is not.",
    5078            1 :                                            ipsc->cCurrentModuleObject,
    5079            1 :                                            runPeriodInput.title));
    5080            1 :                     ErrorsFound = true;
    5081              :                 }
    5082            6 :             } else if (runPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
    5083            0 :                 ShowSevereError(state,
    5084            0 :                                 format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
    5085            0 :                                        ipsc->cCurrentModuleObject,
    5086            0 :                                        runPeriodInput.title,
    5087            0 :                                        runPeriodInput.startYear));
    5088            0 :                 ErrorsFound = true;
    5089              :             }
    5090              : 
    5091           64 :             if (runPeriodInput.endYear != 0 && runPeriodInput.startYear > runPeriodInput.endYear) {
    5092            0 :                 ShowSevereError(state,
    5093            0 :                                 format("{}: object={}, start year ({}) is after the end year ({}).",
    5094            0 :                                        ipsc->cCurrentModuleObject,
    5095            0 :                                        runPeriodInput.title,
    5096            0 :                                        runPeriodInput.startYear,
    5097            0 :                                        runPeriodInput.endYear));
    5098            0 :                 ErrorsFound = true;
    5099              :             }
    5100              : 
    5101              :             // A2 , \field Day of Week for Start Day
    5102           64 :             bool inputWeekday = false;
    5103           64 :             if (!state.dataIPShortCut->lAlphaFieldBlanks(2)) { // Have input
    5104           61 :                 int dayType = getEnumValue(Sched::dayTypeNamesUC, state.dataIPShortCut->cAlphaArgs(2));
    5105           61 :                 if (dayType < 1) {
    5106            0 :                     ShowWarningError(state,
    5107            0 :                                      format("{}: object={}{} invalid (Day of Week) [{}] for Start is not valid, Sunday will be used.",
    5108            0 :                                             state.dataIPShortCut->cCurrentModuleObject,
    5109            0 :                                             state.dataWeather->RunPeriodInput(i).title,
    5110            0 :                                             state.dataIPShortCut->cAlphaFieldNames(2),
    5111            0 :                                             state.dataIPShortCut->cAlphaArgs(2)));
    5112            0 :                     runPeriodInput.startWeekDay = Sched::DayType::Sunday;
    5113              :                 } else {
    5114           61 :                     runPeriodInput.startWeekDay = static_cast<Sched::DayType>(dayType);
    5115           61 :                     inputWeekday = true;
    5116              :                 }
    5117              :             } else { // No input, set the default as Sunday. This may get overriden below
    5118            3 :                 runPeriodInput.startWeekDay = Sched::DayType::Sunday;
    5119              :             }
    5120              : 
    5121              :             // Validate the dates now that the weekday field has been looked at
    5122           64 :             if (runPeriodInput.startMonth == 2 && runPeriodInput.startDay == 29) {
    5123              :                 // Requested start date is a leap year
    5124            4 :                 if (runPeriodInput.startYear == 0) { // No input starting year
    5125            2 :                     if (inputWeekday) {
    5126            2 :                         runPeriodInput.startYear =
    5127            2 :                             findLeapYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
    5128              :                     } else {
    5129              :                         // 2012 is the default year, 1/1 is a Sunday
    5130            0 :                         runPeriodInput.startYear = 2012;
    5131            0 :                         runPeriodInput.startWeekDay =
    5132            0 :                             calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
    5133              :                     }
    5134              :                 } else {                                         // Have an input start year
    5135            2 :                     if (!isLeapYear(runPeriodInput.startYear)) { // Start year is not a leap year
    5136            2 :                         ShowSevereError(state,
    5137            2 :                                         format("{}: object={}, start year ({}) is not a leap year but the requested start date is 2/29.",
    5138            1 :                                                ipsc->cCurrentModuleObject,
    5139            1 :                                                runPeriodInput.title,
    5140            1 :                                                runPeriodInput.startYear));
    5141            1 :                         ErrorsFound = true;
    5142              :                     } else { // Start year is a leap year
    5143              :                         Sched::DayType weekday =
    5144            1 :                             calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
    5145            1 :                         if (inputWeekday) { // Check for correctness of input
    5146            1 :                             if (weekday != runPeriodInput.startWeekDay) {
    5147            0 :                                 ShowWarningError(state,
    5148            0 :                                                  format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
    5149            0 :                                                         ipsc->cCurrentModuleObject,
    5150            0 :                                                         runPeriodInput.title,
    5151            0 :                                                         ipsc->cAlphaArgs(2),
    5152            0 :                                                         runPeriodInput.startYear,
    5153            0 :                                                         Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
    5154            0 :                                 runPeriodInput.startWeekDay = weekday;
    5155              :                             }
    5156              :                         } else { // Set the weekday if it was not input
    5157            0 :                             runPeriodInput.startWeekDay = weekday;
    5158              :                         }
    5159              :                     }
    5160              :                 }
    5161            4 :             } else {
    5162              :                 // Non leap-day start date
    5163           60 :                 if (!validMonthDay(runPeriodInput.startMonth, runPeriodInput.startDay)) {
    5164            0 :                     ShowSevereError(state,
    5165            0 :                                     format("{}: object={}, Invalid input start month/day ({}/{})",
    5166            0 :                                            ipsc->cCurrentModuleObject,
    5167            0 :                                            runPeriodInput.title,
    5168            0 :                                            runPeriodInput.startMonth,
    5169            0 :                                            runPeriodInput.startDay));
    5170            0 :                     ErrorsFound = true;
    5171              :                 } else {                                 // Month/day is valid
    5172           60 :                     if (runPeriodInput.startYear == 0) { // No input starting year
    5173           56 :                         if (inputWeekday) {
    5174           55 :                             runPeriodInput.startYear =
    5175           55 :                                 findYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
    5176              :                         } else {
    5177              :                             // 2017 is the default year, 1/1 is a Sunday
    5178            1 :                             runPeriodInput.startYear = 2017;
    5179            1 :                             runPeriodInput.startWeekDay =
    5180            1 :                                 calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
    5181              :                         }
    5182              :                     } else { // Have an input starting year
    5183              :                         Sched::DayType weekday =
    5184            4 :                             calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
    5185            4 :                         if (inputWeekday) { // Check for correctness of input
    5186            2 :                             if (weekday != runPeriodInput.startWeekDay) {
    5187            2 :                                 ShowWarningError(state,
    5188            3 :                                                  format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
    5189            1 :                                                         ipsc->cCurrentModuleObject,
    5190            1 :                                                         runPeriodInput.title,
    5191            1 :                                                         ipsc->cAlphaArgs(2),
    5192            1 :                                                         runPeriodInput.startYear,
    5193            1 :                                                         Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
    5194            1 :                                 runPeriodInput.startWeekDay = weekday;
    5195              :                             }
    5196              :                         } else { // Set the weekday if it was not input
    5197            2 :                             runPeriodInput.startWeekDay = weekday;
    5198              :                         }
    5199              :                     }
    5200              :                 }
    5201              :             }
    5202              : 
    5203              :             // Compute the Julian date of the start date
    5204           64 :             runPeriodInput.startJulianDate = computeJulianDate(runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
    5205              : 
    5206              :             // Validate the end date
    5207           64 :             if (runPeriodInput.endMonth == 2 && runPeriodInput.endDay == 29) {
    5208              :                 // Requested end date is a leap year
    5209            0 :                 if (runPeriodInput.endYear == 0) { // No input end year
    5210            0 :                     if (isLeapYear(runPeriodInput.startYear) && runPeriodInput.startMonth < 3) {
    5211              :                         // The run period is from some date on or before 2/29 through 2/29
    5212            0 :                         runPeriodInput.endYear = runPeriodInput.startYear;
    5213              :                     } else {
    5214              :                         // There might be a better approach here, but for now just loop forward for the next leap year
    5215            0 :                         for (int yr = runPeriodInput.startYear + 1; yr < runPeriodInput.startYear + 10; yr++) {
    5216            0 :                             if (isLeapYear(yr)) {
    5217            0 :                                 runPeriodInput.endYear = yr;
    5218            0 :                                 break;
    5219              :                             }
    5220              :                         }
    5221              :                     }
    5222              :                 } else {                                       // Have an input end year
    5223            0 :                     if (!isLeapYear(runPeriodInput.endYear)) { // End year is not a leap year
    5224            0 :                         ShowSevereError(state,
    5225            0 :                                         format("{}: object={}, end year ({}) is not a leap year but the requested end date is 2/29.",
    5226            0 :                                                ipsc->cCurrentModuleObject,
    5227            0 :                                                runPeriodInput.title,
    5228            0 :                                                runPeriodInput.startYear));
    5229            0 :                         ErrorsFound = true;
    5230              :                     } else {
    5231            0 :                         runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
    5232            0 :                         if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
    5233            0 :                             ShowSevereError(state,
    5234            0 :                                             format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
    5235            0 :                                                    ipsc->cCurrentModuleObject,
    5236            0 :                                                    runPeriodInput.title,
    5237            0 :                                                    runPeriodInput.startJulianDate,
    5238            0 :                                                    runPeriodInput.endJulianDate));
    5239            0 :                             ErrorsFound = true;
    5240              :                         }
    5241              :                     }
    5242              :                 }
    5243            0 :             } else {
    5244              :                 // Non leap-day end date
    5245           64 :                 if (!validMonthDay(runPeriodInput.endMonth, runPeriodInput.endDay)) {
    5246            0 :                     ShowSevereError(state,
    5247            0 :                                     format("{}: object={}, Invalid input end month/day ({}/{})",
    5248            0 :                                            ipsc->cCurrentModuleObject,
    5249            0 :                                            runPeriodInput.title,
    5250            0 :                                            runPeriodInput.startMonth,
    5251            0 :                                            runPeriodInput.startDay));
    5252            0 :                     ErrorsFound = true;
    5253              :                 } else {                               // Month/day is valid
    5254           64 :                     if (runPeriodInput.endYear == 0) { // No input end year
    5255              :                         // Assume same year as start year
    5256           61 :                         runPeriodInput.endYear = runPeriodInput.startYear;
    5257           61 :                         runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
    5258           61 :                         if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
    5259            0 :                             runPeriodInput.endJulianDate = 0; // Force recalculation later
    5260            0 :                             runPeriodInput.endYear += 1;
    5261              :                         }
    5262              :                     } else { // Have an input end year
    5263            3 :                         runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
    5264            3 :                         if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
    5265            2 :                             ShowSevereError(state,
    5266            2 :                                             format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
    5267            1 :                                                    ipsc->cCurrentModuleObject,
    5268            1 :                                                    runPeriodInput.title,
    5269            1 :                                                    runPeriodInput.startJulianDate,
    5270            1 :                                                    runPeriodInput.endJulianDate));
    5271            1 :                             ErrorsFound = true;
    5272              :                         }
    5273              :                     }
    5274              :                 }
    5275              :             }
    5276              : 
    5277           64 :             if (runPeriodInput.endJulianDate == 0) {
    5278            0 :                 runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
    5279              :             }
    5280              : 
    5281           64 :             runPeriodInput.numSimYears = runPeriodInput.endYear - runPeriodInput.startYear + 1;
    5282              : 
    5283              :             // A3,  \field Use Weather File Holidays and Special Days
    5284              :             BooleanSwitch b;
    5285           64 :             if (ipsc->lAlphaFieldBlanks(3)) {
    5286            0 :                 runPeriodInput.useHolidays = true;
    5287           64 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
    5288           64 :                 runPeriodInput.useHolidays = static_cast<bool>(b);
    5289              :             } else {
    5290            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    5291            0 :                 ErrorsFound = true;
    5292              :             }
    5293              : 
    5294              :             // A4,  \field Use Weather File Daylight Saving Period
    5295           64 :             if (ipsc->lAlphaFieldBlanks(4)) {
    5296            0 :                 runPeriodInput.useDST = true;
    5297           64 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
    5298           64 :                 runPeriodInput.useDST = static_cast<bool>(b);
    5299              :             } else {
    5300            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
    5301            0 :                 ErrorsFound = true;
    5302              :             }
    5303              : 
    5304              :             // A5,  \field Apply Weekend Holiday Rule
    5305           64 :             if (ipsc->lAlphaFieldBlanks(5)) {
    5306            0 :                 runPeriodInput.applyWeekendRule = true;
    5307           64 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
    5308           64 :                 runPeriodInput.applyWeekendRule = static_cast<bool>(b);
    5309              :             } else {
    5310            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    5311            0 :                 ErrorsFound = true;
    5312              :             }
    5313              : 
    5314              :             // A6,  \field Use Weather File Rain Indicators
    5315           64 :             if (ipsc->lAlphaFieldBlanks(6)) {
    5316            0 :                 runPeriodInput.useRain = true;
    5317           64 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(6)))) != BooleanSwitch::Invalid) {
    5318           64 :                 runPeriodInput.useRain = static_cast<bool>(b);
    5319              :             } else {
    5320            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6));
    5321            0 :                 ErrorsFound = true;
    5322              :             }
    5323              : 
    5324              :             // A7,  \field Use Weather File Snow Indicators
    5325           64 :             if (ipsc->lAlphaFieldBlanks(7)) {
    5326            0 :                 runPeriodInput.useSnow = true;
    5327           64 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
    5328           64 :                 runPeriodInput.useSnow = static_cast<bool>(b);
    5329              :             } else {
    5330            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7));
    5331            0 :                 ErrorsFound = true;
    5332              :             }
    5333              : 
    5334              :             // A8,  \field Treat Weather as Actual
    5335           64 :             if (ipsc->lAlphaFieldBlanks(8)) {
    5336           62 :                 runPeriodInput.actualWeather = false;
    5337            2 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
    5338            2 :                 runPeriodInput.actualWeather = static_cast<bool>(b);
    5339              :             } else {
    5340            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8));
    5341            0 :                 ErrorsFound = true;
    5342              :             }
    5343              : 
    5344              :             // A9,  \field First Hour Interpolation Starting Values
    5345           64 :             if (ipsc->lAlphaFieldBlanks(9) || Util::SameString(ipsc->cAlphaArgs(8), "Hour24")) {
    5346           62 :                 runPeriodInput.firstHrInterpUsingHr1 = false;
    5347            2 :             } else if (Util::SameString(ipsc->cAlphaArgs(9), "Hour1")) {
    5348            2 :                 runPeriodInput.firstHrInterpUsingHr1 = true;
    5349              :             } else {
    5350              :                 // fail-safe default
    5351            0 :                 runPeriodInput.firstHrInterpUsingHr1 = false;
    5352              :             }
    5353              : 
    5354           64 :             runPeriodInput.dayOfWeek = static_cast<int>(runPeriodInput.startWeekDay);
    5355           64 :             runPeriodInput.isLeapYear = isLeapYear(runPeriodInput.startYear);
    5356              : 
    5357              :             // calculate the annual start and end days from the user inputted month and day
    5358           64 :             runPeriodInput.monWeekDay = 0;
    5359           64 :             if (runPeriodInput.dayOfWeek != 0 && !ErrorsFound) {
    5360           62 :                 SetupWeekDaysByMonth(state, runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.dayOfWeek, runPeriodInput.monWeekDay);
    5361              :             }
    5362           64 :         }
    5363              : 
    5364           55 :         if (nRunPeriods == 0 && state.dataSysVars->FullAnnualRun) {
    5365            0 :             ShowWarningError(state, "No Run Periods input but Full Annual Simulation selected.  Adding Run Period to 1/1 through 12/31.");
    5366            0 :             state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
    5367            0 :             state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
    5368            0 :             nRunPeriods = 1;
    5369            0 :             state.dataGlobal->WeathSimReq = true;
    5370            0 :             state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
    5371            0 :             auto &runPerInput1 = state.dataWeather->RunPeriodInput(1);
    5372            0 :             runPerInput1.startJulianDate = General::OrdinalDay(runPerInput1.startMonth, runPerInput1.startDay, state.dataWeather->LeapYearAdd);
    5373            0 :             runPerInput1.endJulianDate = General::OrdinalDay(runPerInput1.endMonth, runPerInput1.endDay, state.dataWeather->LeapYearAdd);
    5374            0 :             runPerInput1.monWeekDay = 0;
    5375            0 :             if (runPerInput1.dayOfWeek != 0 && !ErrorsFound) {
    5376            0 :                 SetupWeekDaysByMonth(state, runPerInput1.startMonth, runPerInput1.startDay, runPerInput1.dayOfWeek, runPerInput1.monWeekDay);
    5377              :             }
    5378           55 :         } else if (nRunPeriods > 1 && state.dataSysVars->FullAnnualRun) {
    5379            0 :             nRunPeriods = 1;
    5380              :         }
    5381           55 :     }
    5382              : 
    5383            1 :     void GetRunPeriodDesignData(EnergyPlusData &state, bool &ErrorsFound)
    5384              :     {
    5385              : 
    5386              :         // SUBROUTINE INFORMATION:
    5387              :         //       AUTHOR         Linda Lawrie
    5388              :         //       DATE WRITTEN   March 2008
    5389              : 
    5390              :         // PURPOSE OF THIS SUBROUTINE:
    5391              :         // This subroutine gets the run period design info from User input and the
    5392              :         //  simulation dates
    5393              : 
    5394            1 :         constexpr std::string_view routineName = "GetRunPeriodDesignData";
    5395              :         // Call Input Get routine to retrieve annual run data
    5396            1 :         int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
    5397            1 :         int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
    5398            1 :         state.dataWeather->TotRunDesPers = RPD1 + RPD2;
    5399              : 
    5400            1 :         state.dataWeather->RunPeriodDesignInput.allocate(RPD1 + RPD2);
    5401              : 
    5402            1 :         int Count = 0;
    5403            1 :         auto const &ipsc = state.dataIPShortCut;
    5404            1 :         ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileDays";
    5405            2 :         for (int i = 1; i <= RPD1; ++i) {
    5406              :             int NumAlphas;   // Number of alphas being input
    5407              :             int NumNumerics; // Number of Numerics being input
    5408              :             int IOStat;      // IO Status when calling get input subroutine
    5409            2 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    5410            1 :                                                                      ipsc->cCurrentModuleObject,
    5411              :                                                                      i,
    5412            1 :                                                                      ipsc->cAlphaArgs,
    5413              :                                                                      NumAlphas,
    5414            1 :                                                                      ipsc->rNumericArgs,
    5415              :                                                                      NumNumerics,
    5416              :                                                                      IOStat,
    5417            1 :                                                                      ipsc->lNumericFieldBlanks,
    5418            1 :                                                                      ipsc->lAlphaFieldBlanks,
    5419            1 :                                                                      ipsc->cAlphaFieldNames,
    5420            1 :                                                                      ipsc->cNumericFieldNames);
    5421              : 
    5422            1 :             std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
    5423            1 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
    5424            2 :             if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
    5425            2 :                              state.dataWeather->RunPeriodDesignInput.end(),
    5426            2 :                              [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
    5427            2 :                 state.dataWeather->RunPeriodDesignInput.end()) {
    5428            0 :                 ShowSevereDuplicateName(state, eoh);
    5429            0 :                 ErrorsFound = true;
    5430              :             }
    5431              : 
    5432            1 :             ++Count;
    5433              : 
    5434            1 :             auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
    5435            1 :             runPerDesInput.title = newName;
    5436            1 :             runPerDesInput.periodType = "User Selected WeatherFile RunPeriod (Design)";
    5437              : 
    5438              :             // set the start and end day of month from user input
    5439            1 :             runPerDesInput.startMonth = int(ipsc->rNumericArgs(1));
    5440            1 :             runPerDesInput.startDay = int(ipsc->rNumericArgs(2));
    5441            1 :             runPerDesInput.endMonth = int(ipsc->rNumericArgs(3));
    5442            1 :             runPerDesInput.endDay = int(ipsc->rNumericArgs(4));
    5443              : 
    5444            1 :             switch (runPerDesInput.startMonth) {
    5445            0 :             case 1:
    5446              :             case 3:
    5447              :             case 5:
    5448              :             case 7:
    5449              :             case 8:
    5450              :             case 10:
    5451              :             case 12: {
    5452            0 :                 if (runPerDesInput.startDay > 31) {
    5453            0 :                     ShowSevereError(state,
    5454            0 :                                     format("{}: object={} {} invalid (Day of Month) [{}]",
    5455            0 :                                            ipsc->cCurrentModuleObject,
    5456            0 :                                            runPerDesInput.title,
    5457            0 :                                            ipsc->cNumericFieldNames(2),
    5458            0 :                                            runPerDesInput.startDay));
    5459            0 :                     ErrorsFound = true;
    5460              :                 }
    5461            0 :             } break;
    5462            1 :             case 4:
    5463              :             case 6:
    5464              :             case 9:
    5465              :             case 11: {
    5466            1 :                 if (runPerDesInput.startDay > 30) {
    5467            2 :                     ShowSevereError(state,
    5468            3 :                                     format("{}: object={} {} invalid (Day of Month) [{}]",
    5469            1 :                                            ipsc->cCurrentModuleObject,
    5470            1 :                                            runPerDesInput.title,
    5471            1 :                                            ipsc->cNumericFieldNames(2),
    5472            1 :                                            runPerDesInput.startDay));
    5473            1 :                     ErrorsFound = true;
    5474              :                 }
    5475            1 :             } break;
    5476            0 :             case 2: {
    5477            0 :                 if (runPerDesInput.startDay > 28 + state.dataWeather->LeapYearAdd) {
    5478            0 :                     ShowSevereError(state,
    5479            0 :                                     format("{}: object={} {} invalid (Day of Month) [{}]",
    5480            0 :                                            ipsc->cCurrentModuleObject,
    5481            0 :                                            runPerDesInput.title,
    5482            0 :                                            ipsc->cNumericFieldNames(2),
    5483            0 :                                            runPerDesInput.startDay));
    5484            0 :                     ErrorsFound = true;
    5485              :                 }
    5486            0 :             } break;
    5487            0 :             default: {
    5488            0 :                 ShowSevereError(state,
    5489            0 :                                 format("{}: object={} {} invalid (Month) [{}]",
    5490            0 :                                        ipsc->cCurrentModuleObject,
    5491            0 :                                        runPerDesInput.title,
    5492            0 :                                        ipsc->cNumericFieldNames(1),
    5493            0 :                                        runPerDesInput.startMonth));
    5494            0 :                 ErrorsFound = true;
    5495            0 :             } break;
    5496              :             } // switch
    5497              : 
    5498            1 :             if (ipsc->lAlphaFieldBlanks(2)) {
    5499            0 :                 runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
    5500              :             } else {
    5501            1 :                 runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
    5502            1 :                 if (runPerDesInput.dayOfWeek < 1 || runPerDesInput.dayOfWeek == 8) {
    5503            0 :                     ShowWarningError(state,
    5504            0 :                                      format("{}: object={} {} invalid (Day of Week) [{} for Start is not Valid, Monday will be Used.",
    5505            0 :                                             ipsc->cCurrentModuleObject,
    5506            0 :                                             runPerDesInput.title,
    5507            0 :                                             ipsc->cAlphaFieldNames(1),
    5508            0 :                                             ipsc->cAlphaArgs(1)));
    5509            0 :                     runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
    5510              :                 }
    5511              :             }
    5512              : 
    5513              :             BooleanSwitch b;
    5514            1 :             if (ipsc->lAlphaFieldBlanks(3)) {
    5515            0 :                 runPerDesInput.useDST = true;
    5516            1 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
    5517            1 :                 runPerDesInput.useDST = static_cast<bool>(b);
    5518              :             } else {
    5519            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    5520            0 :                 ErrorsFound = true;
    5521              :             }
    5522              : 
    5523            1 :             if (ipsc->lAlphaFieldBlanks(4)) {
    5524            0 :                 runPerDesInput.useRain = runPerDesInput.useSnow = true;
    5525            1 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
    5526            1 :                 runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
    5527              :             } else {
    5528            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
    5529            0 :                 ErrorsFound = true;
    5530              :             }
    5531              : 
    5532              :             // calculate the annual start and end days from the user inputted month and day
    5533            1 :             runPerDesInput.startJulianDate = General::OrdinalDay(runPerDesInput.startMonth, runPerDesInput.startDay, state.dataWeather->LeapYearAdd);
    5534            1 :             runPerDesInput.endJulianDate = General::OrdinalDay(runPerDesInput.endMonth, runPerDesInput.endDay, state.dataWeather->LeapYearAdd);
    5535            1 :             if (runPerDesInput.startJulianDate <= runPerDesInput.endJulianDate) {
    5536            1 :                 runPerDesInput.totalDays = (runPerDesInput.endJulianDate - runPerDesInput.startJulianDate + 1) * runPerDesInput.numSimYears;
    5537              :             } else {
    5538            0 :                 runPerDesInput.totalDays = (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - runPerDesInput.startJulianDate + 1 +
    5539            0 :                                             runPerDesInput.endJulianDate) *
    5540            0 :                                            runPerDesInput.numSimYears;
    5541              :             }
    5542            1 :             runPerDesInput.monWeekDay = 0;
    5543            1 :             auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
    5544            1 :             if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
    5545            0 :                 SetupWeekDaysByMonth(state,
    5546              :                                      runPeriodDesignInput1.startMonth,
    5547              :                                      runPeriodDesignInput1.startDay,
    5548              :                                      runPeriodDesignInput1.dayOfWeek,
    5549            0 :                                      runPeriodDesignInput1.monWeekDay);
    5550              :             }
    5551            1 :         }
    5552              : 
    5553            1 :         ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileConditionType";
    5554            1 :         for (int i = 1; i <= RPD2; ++i) {
    5555              :             int NumAlphas;   // Number of alphas being input
    5556              :             int NumNumerics; // Number of Numerics being input
    5557              :             int IOStat;      // IO Status when calling get input subroutine
    5558            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    5559            0 :                                                                      ipsc->cCurrentModuleObject,
    5560              :                                                                      i,
    5561            0 :                                                                      ipsc->cAlphaArgs,
    5562              :                                                                      NumAlphas,
    5563            0 :                                                                      ipsc->rNumericArgs,
    5564              :                                                                      NumNumerics,
    5565              :                                                                      IOStat,
    5566            0 :                                                                      ipsc->lNumericFieldBlanks,
    5567            0 :                                                                      ipsc->lAlphaFieldBlanks,
    5568            0 :                                                                      ipsc->cAlphaFieldNames,
    5569            0 :                                                                      ipsc->cNumericFieldNames);
    5570            0 :             std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
    5571              : 
    5572            0 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
    5573            0 :             if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
    5574            0 :                              state.dataWeather->RunPeriodDesignInput.end(),
    5575            0 :                              [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
    5576            0 :                 state.dataWeather->RunPeriodDesignInput.end()) {
    5577            0 :                 ShowSevereDuplicateName(state, eoh);
    5578            0 :                 ErrorsFound = true;
    5579              :             }
    5580              : 
    5581            0 :             ++Count;
    5582            0 :             auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
    5583            0 :             runPerDesInput.title = ipsc->cAlphaArgs(1);
    5584            0 :             runPerDesInput.periodType = "User Selected WeatherFile Typical/Extreme Period (Design)=" + ipsc->cAlphaArgs(2);
    5585              : 
    5586              :             // Period Selection
    5587            0 :             if (ipsc->lAlphaFieldBlanks(2)) {
    5588            0 :                 ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
    5589            0 :                 ErrorsFound = true;
    5590              :             } else {
    5591            0 :                 int WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue);
    5592            0 :                 if (WhichPeriod == 0) {
    5593            0 :                     WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue1);
    5594              :                     if (WhichPeriod != 0) {
    5595              :                     }
    5596              :                 }
    5597            0 :                 if (WhichPeriod == 0) {
    5598            0 :                     WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue2);
    5599            0 :                     if (WhichPeriod != 0) {
    5600            0 :                         ShowWarningError(state,
    5601            0 :                                          format("{}: object={} {}={} matched to {}",
    5602            0 :                                                 ipsc->cCurrentModuleObject,
    5603            0 :                                                 runPerDesInput.title,
    5604            0 :                                                 ipsc->cAlphaFieldNames(2),
    5605            0 :                                                 ipsc->cAlphaArgs(2),
    5606            0 :                                                 state.dataWeather->TypicalExtremePeriods(WhichPeriod).MatchValue2));
    5607              :                     }
    5608              :                 }
    5609            0 :                 if (WhichPeriod == 0) {
    5610            0 :                     ShowSevereError(state,
    5611            0 :                                     format("{}: object={} {} invalid (not on Weather File)={}",
    5612            0 :                                            ipsc->cCurrentModuleObject,
    5613            0 :                                            runPerDesInput.title,
    5614            0 :                                            ipsc->cAlphaFieldNames(2),
    5615            0 :                                            ipsc->cAlphaArgs(2)));
    5616            0 :                     ErrorsFound = true;
    5617              :                 } else {
    5618            0 :                     auto const &typicalExtPer = state.dataWeather->TypicalExtremePeriods(WhichPeriod);
    5619            0 :                     runPerDesInput.startDay = typicalExtPer.StartDay;
    5620            0 :                     runPerDesInput.startMonth = typicalExtPer.StartMonth;
    5621            0 :                     runPerDesInput.startJulianDate = typicalExtPer.StartJDay;
    5622            0 :                     runPerDesInput.endDay = typicalExtPer.EndDay;
    5623            0 :                     runPerDesInput.endMonth = typicalExtPer.EndMonth;
    5624            0 :                     runPerDesInput.endJulianDate = typicalExtPer.EndJDay;
    5625            0 :                     runPerDesInput.totalDays = typicalExtPer.TotalDays;
    5626              :                 }
    5627              :             }
    5628              : 
    5629            0 :             if (ipsc->lAlphaFieldBlanks(3)) {
    5630            0 :                 runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
    5631              :             } else {
    5632            0 :                 runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(3));
    5633            0 :                 if (runPerDesInput.dayOfWeek < (int)Sched::DayType::Sunday || runPerDesInput.dayOfWeek == (int)Sched::DayType::Holiday) {
    5634              :                     // Sunday-Saturday, SummerDesignDay, WinterDesignDay, CustomDay1, and CustomDay2 are all valid. Holiday is not valid.
    5635              :                     // The input processor should trap invalid key choices, so this should never trip.
    5636            0 :                     assert(false);
    5637              :                 }
    5638              :             }
    5639              : 
    5640              :             BooleanSwitch b;
    5641            0 :             if (ipsc->lAlphaFieldBlanks(4)) {
    5642            0 :                 runPerDesInput.useDST = true;
    5643            0 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
    5644            0 :                 runPerDesInput.useDST = static_cast<bool>(b);
    5645              :             } else {
    5646            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
    5647            0 :                 ErrorsFound = true;
    5648              :             }
    5649              : 
    5650            0 :             if (ipsc->lAlphaFieldBlanks(5)) {
    5651            0 :                 runPerDesInput.useRain = runPerDesInput.useSnow = true;
    5652            0 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
    5653            0 :                 runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
    5654              :             } else {
    5655            0 :                 ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    5656            0 :                 ErrorsFound = true;
    5657              :             }
    5658            0 :             auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
    5659            0 :             runPeriodDesignInput1.monWeekDay = 0;
    5660            0 :             if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
    5661            0 :                 SetupWeekDaysByMonth(state,
    5662              :                                      runPeriodDesignInput1.startMonth,
    5663              :                                      runPeriodDesignInput1.startDay,
    5664              :                                      runPeriodDesignInput1.dayOfWeek,
    5665            0 :                                      runPeriodDesignInput1.monWeekDay);
    5666              :             }
    5667            0 :         }
    5668            1 :     }
    5669              : 
    5670           50 :     void GetSpecialDayPeriodData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
    5671              :     {
    5672              : 
    5673              :         // SUBROUTINE INFORMATION:
    5674              :         //       AUTHOR         Linda Lawrie
    5675              :         //       DATE WRITTEN   June 2000
    5676              : 
    5677              :         // PURPOSE OF THIS SUBROUTINE:
    5678              :         // This subroutine reads any special day period data from the IDF and
    5679              :         // processes it into the data structure that will drive the values
    5680              :         // in the SpecialDayTypes array.
    5681              : 
    5682              :         // METHODOLOGY EMPLOYED:
    5683              :         // Processes the following IDD definition:
    5684              :         // SpecialDayPeriod,
    5685              :         //      \memo This object sets up holidays/special days to be used during weather file
    5686              :         //      \memo run periods.  (These are not used with DesignDay objects.)
    5687              :         //      \memo Depending on the value in the run period, days on the weather file may also
    5688              :         //      \memo be used.  However, the weather file specification will take precedence over
    5689              :         //      \memo any specification shown here.  (No error message on duplicate days or overlapping
    5690              :         //      \memo days).
    5691              :         //  A1, \field Holiday Name
    5692              :         //  A2, \field StartDate
    5693              :         //      \memo  Dates can be several formats:
    5694              :         //      \memo  <number>/<number>  (month/day)
    5695              :         //      \memo  <number> Month
    5696              :         //      \memo  Month <number>
    5697              :         //      \memo Months are January, February, March, April, May, June, July, August, September, October, November, December
    5698              :         //      \memo Months can be the first 3 letters of the month
    5699              :         //        \note will eventually allow: 3 Monday April (meaning 3rd Monday in April)
    5700              :         //  N1, \field duration (number of days)
    5701              :         //  A3; \field SpecialDayType
    5702              :         //        \note SpecialDayType selects the schedules appropriate for each day so labeled
    5703              :         //        \type choice
    5704              :         //        \key Holiday
    5705              :         //        \key SummerDesignDay
    5706              :         //        \key WinterDesignDay
    5707              :         //        \key CustomDay1
    5708              :         //        \key CustomDay2
    5709              : 
    5710           50 :         constexpr std::string_view routineName = "GetSpecialDayPeriodData";
    5711              : 
    5712           50 :         auto const &ipsc = state.dataIPShortCut;
    5713           50 :         ipsc->cCurrentModuleObject = "RunPeriodControl:SpecialDays";
    5714           50 :         int NumSpecDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    5715              :         int Count;
    5716           50 :         if (allocated(state.dataWeather->SpecialDays)) { // EPW already allocated the array
    5717           25 :             Count = state.dataWeather->NumSpecialDays - NumSpecDays + 1;
    5718              :         } else {
    5719           25 :             state.dataWeather->SpecialDays.allocate(NumSpecDays);
    5720           25 :             state.dataWeather->NumSpecialDays = NumSpecDays;
    5721           25 :             Count = 1;
    5722              :         }
    5723              : 
    5724           50 :         Array1D_string AlphArray(3);
    5725              :         int NumAlphas;
    5726           50 :         Array1D<Real64> Duration(1);
    5727              :         int NumNumbers;
    5728              :         int IOStat;
    5729              : 
    5730           50 :         for (int i = 1; i <= NumSpecDays; ++i, ++Count) {
    5731              : 
    5732            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(
    5733            0 :                 state, ipsc->cCurrentModuleObject, i, AlphArray, NumAlphas, Duration, NumNumbers, IOStat);
    5734              : 
    5735            0 :             auto &specialDay = state.dataWeather->SpecialDays(Count);
    5736              : 
    5737            0 :             specialDay.Name = AlphArray(1);
    5738            0 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, specialDay.Name};
    5739              : 
    5740              :             int PMonth;
    5741              :             int PDay;
    5742              :             int PWeekDay;
    5743              :             DateType dateType;
    5744            0 :             General::ProcessDateString(state, AlphArray(2), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
    5745            0 :             if (dateType == DateType::MonthDay) {
    5746            0 :                 specialDay.dateType = dateType;
    5747            0 :                 specialDay.Month = PMonth;
    5748            0 :                 specialDay.Day = PDay;
    5749            0 :                 specialDay.WeekDay = 0;
    5750            0 :                 specialDay.CompDate = PMonth * 32 + PDay;
    5751            0 :                 specialDay.WthrFile = false;
    5752            0 :             } else if (dateType != DateType::Invalid) {
    5753            0 :                 specialDay.dateType = dateType;
    5754            0 :                 specialDay.Month = PMonth;
    5755            0 :                 specialDay.Day = PDay;
    5756            0 :                 specialDay.WeekDay = PWeekDay;
    5757            0 :                 specialDay.CompDate = 0;
    5758            0 :                 specialDay.WthrFile = false;
    5759              :             } else {
    5760            0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
    5761            0 :                 ErrorsFound = true;
    5762              :             }
    5763              : 
    5764            0 :             if (Duration(1) > 0) {
    5765            0 :                 specialDay.Duration = int(Duration(1));
    5766              :             } else {
    5767            0 :                 ShowSevereError(
    5768            0 :                     state, format("{}: {} Invalid {}={:.0T}", ipsc->cCurrentModuleObject, AlphArray(1), ipsc->cNumericFieldNames(1), Duration(1)));
    5769            0 :                 ErrorsFound = true;
    5770              :             }
    5771              : 
    5772            0 :             int DayType = getEnumValue(Sched::dayTypeNamesUC, AlphArray(3));
    5773            0 :             if (DayType == 0) {
    5774            0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), AlphArray(3));
    5775            0 :                 ErrorsFound = true;
    5776              :             } else {
    5777            0 :                 specialDay.DayType = DayType;
    5778              :             }
    5779              :         }
    5780           50 :     }
    5781              : 
    5782            5 :     void CalcSpecialDayTypes(EnergyPlusData &state)
    5783              :     {
    5784              : 
    5785              :         // SUBROUTINE INFORMATION:
    5786              :         //       AUTHOR         Linda Lawrie
    5787              :         //       DATE WRITTEN   June 2000
    5788              : 
    5789              :         // PURPOSE OF THIS SUBROUTINE:
    5790              :         // This subroutine creates the array of Special Day types used during
    5791              :         // the simulation.
    5792              : 
    5793              :         // METHODOLOGY EMPLOYED:
    5794              :         // Sets up the SpecialDayTypes array that then is used during simulation.
    5795              : 
    5796            5 :         state.dataWeather->SpecialDayTypes = 0; // Initialize/Reset Special Day Types array
    5797              : 
    5798            5 :         for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
    5799            0 :             auto const &specialDay = state.dataWeather->SpecialDays(i);
    5800            0 :             if (specialDay.WthrFile) {
    5801            0 :                 continue;
    5802              :             }
    5803              : 
    5804            0 :             int Warn = 0;
    5805              : 
    5806            0 :             int JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd) - 1;
    5807              : 
    5808            0 :             for (int j = 1; j <= specialDay.Duration; ++j) {
    5809            0 :                 ++JDay;
    5810            0 :                 if (JDay > 366) {
    5811            0 :                     ShowWarningError(state, format("SpecialDay={} causes index of more than 366, ignoring those beyond 366", specialDay.Name));
    5812              :                 } else {
    5813            0 :                     if (state.dataWeather->SpecialDayTypes(JDay) != 0 && Warn == 0) {
    5814            0 :                         ShowWarningError(state, format("SpecialDay={} attempted overwrite of previous set special day", specialDay.Name));
    5815            0 :                         Warn = 1;
    5816            0 :                     } else if (state.dataWeather->SpecialDayTypes(JDay) == 0) {
    5817            0 :                         state.dataWeather->SpecialDayTypes(JDay) = specialDay.DayType;
    5818              :                     }
    5819              :                 }
    5820              :             }
    5821              :         }
    5822            5 :     }
    5823              : 
    5824           50 :     void GetDSTData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
    5825              :     {
    5826              : 
    5827              :         // SUBROUTINE INFORMATION:
    5828              :         //       AUTHOR         Linda Lawrie
    5829              :         //       DATE WRITTEN   August 2000
    5830              : 
    5831              :         // PURPOSE OF THIS SUBROUTINE:
    5832              :         // This subroutine gets a possible "Daylight Saving Period" from the IDF.  Using this
    5833              :         // will overwrite any prior DST data.
    5834              : 
    5835              :         // METHODOLOGY EMPLOYED:
    5836              :         // Processes the following IDD definition:
    5837              :         // DaylightSavingPeriod,
    5838              :         //      \memo This object sets up the Daylight Saving period for any RunPeriod.
    5839              :         //      \memo Ignores any DaylightSavingperiod values on the weather file and uses this definition.
    5840              :         //      \memo (These are not used with DesignDay objects.)
    5841              :         //  A1, \field StartDate
    5842              :         //  A2, \field EndDate
    5843              :         //      \memo  Dates can be several formats:
    5844              :         //      \memo  <number>/<number>  (month/day)
    5845              :         //      \memo  <number> <Month>
    5846              :         //      \memo  <Month> <number>
    5847              :         //      \memo <Nth> <Weekday> in <Month)
    5848              :         //      \memo Last <WeekDay> in <Month>
    5849              :         //      \memo <Month> can be January, February, March, April, May, June, July, August, September,
    5850              :         // October, November, December
    5851              :         //      \memo Months can be the first 3 letters of the month
    5852              :         //      \memo <Weekday> can be Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
    5853              :         //      \memo <Nth> can be 1 or 1st, 2 or 2nd, etc. up to 5(?)
    5854              : 
    5855           50 :         constexpr std::string_view routineName = "GetDSTData";
    5856              : 
    5857           50 :         auto const &ipsc = state.dataIPShortCut;
    5858           50 :         ipsc->cCurrentModuleObject = "RunPeriodControl:DaylightSavingTime";
    5859           50 :         int NumFound = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    5860              : 
    5861           50 :         if (NumFound == 1) {
    5862              :             int NumAlphas;
    5863              :             int IOStat;
    5864              :             int NumNumbers;
    5865            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    5866            0 :                                                                      ipsc->cCurrentModuleObject,
    5867              :                                                                      1,
    5868            0 :                                                                      ipsc->cAlphaArgs,
    5869              :                                                                      NumAlphas,
    5870            0 :                                                                      ipsc->rNumericArgs,
    5871              :                                                                      NumNumbers,
    5872              :                                                                      IOStat,
    5873            0 :                                                                      ipsc->lNumericFieldBlanks,
    5874            0 :                                                                      ipsc->lAlphaFieldBlanks,
    5875            0 :                                                                      ipsc->cAlphaFieldNames,
    5876            0 :                                                                      ipsc->cNumericFieldNames);
    5877              : 
    5878            0 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
    5879              : 
    5880            0 :             if (NumAlphas != 2) {
    5881            0 :                 ShowSevereError(state, format("{}: Insufficient fields, must have Start AND End Dates", ipsc->cCurrentModuleObject));
    5882            0 :                 ErrorsFound = true;
    5883              :             } else { // Correct number of arguments
    5884            0 :                 General::ProcessDateString(state,
    5885            0 :                                            ipsc->cAlphaArgs(1),
    5886            0 :                                            state.dataWeather->IDFDST.StMon,
    5887            0 :                                            state.dataWeather->IDFDST.StDay,
    5888            0 :                                            state.dataWeather->IDFDST.StWeekDay,
    5889            0 :                                            state.dataWeather->IDFDST.StDateType,
    5890              :                                            ErrorsFound);
    5891            0 :                 if (state.dataWeather->IDFDST.StDateType == DateType::Invalid) {
    5892            0 :                     ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
    5893            0 :                     ErrorsFound = true;
    5894              :                 }
    5895            0 :                 General::ProcessDateString(state,
    5896            0 :                                            ipsc->cAlphaArgs(2),
    5897            0 :                                            state.dataWeather->IDFDST.EnMon,
    5898            0 :                                            state.dataWeather->IDFDST.EnDay,
    5899            0 :                                            state.dataWeather->IDFDST.EnWeekDay,
    5900            0 :                                            state.dataWeather->IDFDST.EnDateType,
    5901              :                                            ErrorsFound);
    5902            0 :                 if (state.dataWeather->IDFDST.EnDateType == DateType::Invalid) {
    5903            0 :                     ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
    5904            0 :                     ErrorsFound = true;
    5905              :                 }
    5906            0 :                 state.dataWeather->IDFDaylightSaving = true;
    5907              :             }
    5908           50 :         } else if (NumFound > 1) {
    5909            0 :             ShowSevereError(state, format("{}: Too many objects in Input File, only one allowed.", ipsc->cCurrentModuleObject));
    5910            0 :             ErrorsFound = true;
    5911              :         }
    5912           50 :     }
    5913              : 
    5914          106 :     void GetDesignDayData(EnergyPlusData &state,
    5915              :                           int TotDesDays, // Total number of Design days to Setup
    5916              :                           bool &ErrorsFound)
    5917              :     {
    5918              : 
    5919              :         // SUBROUTINE INFORMATION:
    5920              :         //       AUTHOR         Richard Liesen
    5921              :         //       DATE WRITTEN   September 1997
    5922              : 
    5923              :         // PURPOSE OF THIS SUBROUTINE:
    5924              :         // This subroutine retrieves the design day info from user input file
    5925              :         //  which is later to be used in the Setup Design Day Routine.
    5926              : 
    5927              :         // REFERENCES:
    5928              :         // SizingPeriod:DesignDay,
    5929              :         //   A1, \field Name
    5930              :         //   N1,  \field Month
    5931              :         //   N2,  \field Day of Month
    5932              :         //   A2,  \field Day Type
    5933              :         //   N3,  \field Maximum Dry-Bulb Temperature
    5934              :         //   N4,  \field Daily Dry-Bulb Temperature Range
    5935              :         //   A3,  \field Dry-Bulb Temperature Range Modifier Type
    5936              :         //   A4,  \field Dry-Bulb Temperature Range Modifier Day Schedule Name
    5937              :         //   A5,  \field Humidity Condition Type
    5938              :         //   N5,  \field Wetbulb or DewPoint at Maximum Dry-Bulb
    5939              :         //   A6,  \field Humidity Condition Day Schedule Name
    5940              :         //   N6,  \field Humidity Ratio at Maximum Dry-Bulb
    5941              :         //   N7,  \field Enthalpy at Maximum Dry-Bulb  !will require units transition.
    5942              :         //   N8,  \field Daily Wet-Bulb Temperature Range
    5943              :         //   N9,  \field Barometric Pressure
    5944              :         //   N10, \field Wind Speed
    5945              :         //   N11, \field Wind Direction
    5946              :         //   A7,  \field Rain Indicator
    5947              :         //   A8,  \field Snow Indicator
    5948              :         //   A9,  \field Daylight Saving Time Indicator
    5949              :         //   A10, \field Solar Model Indicator
    5950              :         //   A11, \field Beam Solar Day Schedule Name
    5951              :         //   A12, \field Diffuse Solar Day Schedule Name
    5952              :         //   N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
    5953              :         //   N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
    5954              :         //   N14; \field Sky Clearness
    5955              : 
    5956              :         static constexpr std::array<std::string_view, static_cast<int>(DesDayHumIndType::Num)> DesDayHumIndTypeStringRep = {
    5957              :             "Wetbulb [C]",
    5958              :             "Dewpoint [C]",
    5959              :             "Enthalpy [J/kg]",
    5960              :             "Humidity Ratio []",
    5961              :             "Schedule []",
    5962              :             "WetBulbProfileDefaultMultipliers []",
    5963              :             "WetBulbProfileDifferenceSchedule []",
    5964              :             "WetBulbProfileMultiplierSchedule []"};
    5965              : 
    5966              :         // Below are the 2009 fractions, HOF, Chap 14, Table 6
    5967              :         static constexpr std::array<Real64, 24> DefaultTempRangeMult = {0.88, 0.92, 0.95, 0.98, 1.0,  0.98, 0.91, 0.74, 0.55, 0.38, 0.23, 0.13,
    5968              :                                                                         0.05, 0.00, 0.00, 0.06, 0.14, 0.24, 0.39, 0.50, 0.59, 0.68, 0.75, 0.82};
    5969              : 
    5970              :         static constexpr std::string_view routineName = "GetDesignDayData";
    5971              : 
    5972              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5973              :         Constant::Units unitType;
    5974              : 
    5975          106 :         state.dataWeather->DesDayInput.allocate(TotDesDays); // Allocate the array to the # of DD's
    5976          106 :         state.dataWeather->desDayMods.allocate(TotDesDays);
    5977          288 :         for (int iDD = 1; iDD <= TotDesDays; ++iDD) {
    5978          182 :             state.dataWeather->desDayMods(iDD).allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
    5979              :         }
    5980              : 
    5981          106 :         state.dataWeather->spSiteSchedules.dimension(TotDesDays, Weather::SPSiteSchedules());
    5982              : 
    5983          106 :         if (state.dataSysVars->ReverseDD && TotDesDays <= 1) {
    5984            0 :             ShowSevereError(state, "GetDesignDayData: Reverse Design Day requested but # Design Days <=1");
    5985              :         }
    5986              : 
    5987          106 :         auto const &ipsc = state.dataIPShortCut;
    5988          106 :         ipsc->cCurrentModuleObject = "SizingPeriod:DesignDay";
    5989          288 :         for (int iDesDay = 1; iDesDay <= TotDesDays; ++iDesDay) {
    5990              : 
    5991              :             int EnvrnNum;
    5992          182 :             if (!state.dataSysVars->ReverseDD) {
    5993          182 :                 EnvrnNum = iDesDay;
    5994            0 :             } else if (iDesDay == 1 && TotDesDays > 1) {
    5995            0 :                 EnvrnNum = 2;
    5996            0 :             } else if (iDesDay == 2) {
    5997            0 :                 EnvrnNum = 1;
    5998              :             } else {
    5999            0 :                 EnvrnNum = iDesDay;
    6000              :             }
    6001              : 
    6002              :             // Call Input Get routine to retrieve design day data
    6003          182 :             bool MaxDryBulbEntered = false;
    6004          182 :             bool PressureEntered = false;
    6005              :             int NumAlpha;    // Number of material alpha names being passed
    6006              :             int NumNumerics; // Number of material properties being passed
    6007              :             int IOStat;      // IO Status when calling get input subroutine
    6008          364 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    6009          182 :                                                                      ipsc->cCurrentModuleObject,
    6010              :                                                                      iDesDay,
    6011          182 :                                                                      ipsc->cAlphaArgs,
    6012              :                                                                      NumAlpha,
    6013          182 :                                                                      ipsc->rNumericArgs,
    6014              :                                                                      NumNumerics,
    6015              :                                                                      IOStat,
    6016          182 :                                                                      ipsc->lNumericFieldBlanks,
    6017          182 :                                                                      ipsc->lAlphaFieldBlanks,
    6018          182 :                                                                      ipsc->cAlphaFieldNames,
    6019          182 :                                                                      ipsc->cNumericFieldNames);
    6020              : 
    6021          182 :             auto &envCurr = state.dataWeather->Environment(EnvrnNum);
    6022          182 :             auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
    6023          182 :             desDayInput.Title = ipsc->cAlphaArgs(1); // Environment name
    6024          182 :             envCurr.Title = desDayInput.Title;
    6025              : 
    6026          182 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, desDayInput.Title};
    6027              : 
    6028              :             //   N3,  \field Maximum Dry-Bulb Temperature
    6029              :             //   N4,  \field Daily Dry-Bulb Temperature Range
    6030              :             //   N9,  \field Barometric Pressure
    6031              :             //   N10, \field Wind Speed
    6032              :             //   N11, \field Wind Direction
    6033          182 :             desDayInput.MaxDryBulb = ipsc->rNumericArgs(3); // Maximum Dry-Bulb Temperature (C)
    6034          182 :             MaxDryBulbEntered = !ipsc->lNumericFieldBlanks(3);
    6035          182 :             desDayInput.DailyDBRange = ipsc->rNumericArgs(4); // Daily dry-bulb temperature range (deltaC)
    6036          182 :             desDayInput.PressBarom = ipsc->rNumericArgs(9);   // Atmospheric/Barometric Pressure (Pascals)
    6037          182 :             PressureEntered = !ipsc->lNumericFieldBlanks(9);
    6038          182 :             desDayInput.PressureEntered = PressureEntered;
    6039          182 :             desDayInput.WindSpeed = ipsc->rNumericArgs(10);           // Wind Speed (m/s)
    6040          182 :             desDayInput.WindDir = mod(ipsc->rNumericArgs(11), 360.0); // Wind Direction
    6041              :             // (degrees clockwise from North, N=0, E=90, S=180, W=270)
    6042              :             //   N1,  \field Month
    6043              :             //   N2,  \field Day of Month
    6044              :             //   N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
    6045              :             //   N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
    6046              :             //   N8,  \field Daily Wet-Bulb Temperature Range
    6047          182 :             desDayInput.Month = int(ipsc->rNumericArgs(1));      // Month of Year ( 1 - 12 )
    6048          182 :             desDayInput.DayOfMonth = int(ipsc->rNumericArgs(2)); // Day of Month ( 1 - 31 )
    6049          182 :             desDayInput.TauB = ipsc->rNumericArgs(12);           // beam tau >= 0
    6050          182 :             desDayInput.TauD = ipsc->rNumericArgs(13);           // diffuse tau >= 0
    6051          182 :             desDayInput.DailyWBRange = ipsc->rNumericArgs(8);    // Daily wet-bulb temperature range (deltaC)
    6052              : 
    6053              :             //   N14; \field Sky Clearness
    6054          182 :             desDayInput.SkyClear = ipsc->rNumericArgs(14); // Sky Clearness (0 to 1)
    6055              : 
    6056              :             //   N15, \field Maximum Warmup Days Between Sizing Periods
    6057          182 :             if (ipsc->lNumericFieldBlanks(15)) {
    6058              :                 // Default to -1 if not input
    6059          182 :                 desDayInput.maxWarmupDays = -1;
    6060              :             } else {
    6061            0 :                 desDayInput.maxWarmupDays = int(ipsc->rNumericArgs(15));
    6062              :             }
    6063              :             //   A13, \field Begin Environment Reset Mode
    6064          182 :             if (ipsc->lAlphaFieldBlanks(13)) {
    6065          180 :                 desDayInput.suppressBegEnvReset = false;
    6066              :             } else {
    6067            2 :                 if (Util::SameString(ipsc->cAlphaArgs(13), "FullResetAtBeginEnvironment")) {
    6068            2 :                     desDayInput.suppressBegEnvReset = false;
    6069            0 :                 } else if (Util::SameString(ipsc->cAlphaArgs(13), "SuppressThermalResetAtBeginEnvironment")) {
    6070            0 :                     desDayInput.suppressBegEnvReset = true;
    6071              :                 }
    6072              :             }
    6073              :             // for PerformancePrecisionTradeoffs
    6074          182 :             if (state.dataEnvrn->forceBeginEnvResetSuppress) {
    6075            0 :                 desDayInput.suppressBegEnvReset = true;
    6076              :             }
    6077              :             //   A7,  \field Rain Indicator
    6078              :             BooleanSwitch b;
    6079              : 
    6080          182 :             if (ipsc->lAlphaFieldBlanks(7)) {
    6081            0 :                 desDayInput.RainInd = 0;
    6082          182 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
    6083          182 :                 desDayInput.RainInd = (int)b;
    6084              :             } else {
    6085            0 :                 ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7), "No");
    6086            0 :                 desDayInput.RainInd = 0;
    6087              :             }
    6088              : 
    6089              :             //   A8,  \field Snow Indicator
    6090          182 :             if (ipsc->lAlphaFieldBlanks(8)) {
    6091            0 :                 desDayInput.SnowInd = 0;
    6092          182 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
    6093          182 :                 desDayInput.SnowInd = (int)b;
    6094              :             } else {
    6095            0 :                 ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8), "No");
    6096            0 :                 desDayInput.SnowInd = 0;
    6097              :             }
    6098              : 
    6099              :             //   A3,  \field Dry-Bulb Temperature Range Modifier Type
    6100              :             // check DB profile input
    6101          182 :             if (ipsc->lAlphaFieldBlanks(3)) {
    6102          166 :                 desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
    6103           32 :             } else if ((desDayInput.dryBulbRangeType = static_cast<DesDayDryBulbRangeType>(
    6104           16 :                             getEnumValue(DesDayDryBulbRangeTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))))) != DesDayDryBulbRangeType::Invalid) {
    6105              :             } else {
    6106            0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    6107            0 :                 ErrorsFound = true;
    6108            0 :                 desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
    6109              :             }
    6110              : 
    6111              :             // std::string units; // not used
    6112          182 :             if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
    6113              :                 // units = "[]";
    6114            0 :                 unitType = Constant::Units::None;
    6115          182 :             } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
    6116              :                 // units = "[deltaC]";
    6117            0 :                 unitType = Constant::Units::deltaC;
    6118          182 :             } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
    6119              :                 // units = "[C]";
    6120            0 :                 unitType = Constant::Units::C;
    6121              :             }
    6122              : 
    6123          182 :             if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile && !MaxDryBulbEntered && ipsc->cAlphaArgs(3) != "invalid field") {
    6124            0 :                 ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(3), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    6125            0 :                 ErrorsFound = true;
    6126              :             }
    6127              : 
    6128              :             // Assume either "multiplier" option will make full use of range...
    6129          182 :             if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Difference &&
    6130          182 :                 desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
    6131          182 :                 Real64 testval = desDayInput.MaxDryBulb - desDayInput.DailyDBRange;
    6132          182 :                 if (testval < -90.0 || testval > 70.0) {
    6133            0 :                     ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
    6134            0 :                     ShowContinueError(state, format("{} ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(3), testval));
    6135            0 :                     ErrorsFound = true;
    6136              :                 }
    6137              :             }
    6138              : 
    6139              :             //   A4,  \field Dry-Bulb Temperature Range Modifier Day Schedule Name
    6140          182 :             if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Default) {
    6141              :                 // Default dry-bulb temperature Range
    6142          182 :                 Real64 LastHrValue = DefaultTempRangeMult[23];
    6143         4550 :                 for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
    6144        29208 :                     for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
    6145        24840 :                         Real64 WNow = state.dataWeather->Interpolation(ts);
    6146        24840 :                         Real64 WPrev = 1.0 - WNow;
    6147        24840 :                         state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutDryBulbTemp =
    6148        24840 :                             LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
    6149              :                     }
    6150         4368 :                     LastHrValue = DefaultTempRangeMult[hour - 1];
    6151              :                 }
    6152              : 
    6153            0 :             } else if (ipsc->lAlphaFieldBlanks(4)) {
    6154            0 :                 ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaFieldNames(3), "SCHEDULE");
    6155            0 :                 ErrorsFound = true;
    6156            0 :             } else if ((desDayInput.tempRangeSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(4))) == nullptr) {
    6157            0 :                 ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
    6158            0 :                 ErrorsFound = true;
    6159              : 
    6160              :             } else {
    6161            0 :                 std::vector<Real64> const &dayVals = desDayInput.tempRangeSched->getDayVals(state);
    6162            0 :                 auto &desDayModEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    6163            0 :                 for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    6164            0 :                     for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    6165            0 :                         desDayModEnvrn(ts + 1, hr + 1).OutDryBulbTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    6166              :                     }
    6167              :                 }
    6168              : 
    6169            0 :                 if (std::find(state.dataWeather->spSiteSchedNums.begin(),
    6170            0 :                               state.dataWeather->spSiteSchedNums.end(),
    6171            0 :                               desDayInput.tempRangeSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
    6172            0 :                     state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.tempRangeSched->Num);
    6173            0 :                     SetupOutputVariable(state,
    6174              :                                         "Sizing Period Site Drybulb Temperature Range Modifier Schedule Value",
    6175              :                                         unitType,
    6176            0 :                                         state.dataWeather->spSiteSchedules(EnvrnNum).OutDryBulbTemp,
    6177              :                                         OutputProcessor::TimeStepType::Zone,
    6178              :                                         OutputProcessor::StoreType::Average,
    6179            0 :                                         ipsc->cAlphaArgs(4));
    6180              :                 }
    6181              : 
    6182            0 :                 if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
    6183            0 :                     if (!desDayInput.tempRangeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
    6184            0 :                         Sched::ShowSevereBadMinMax(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0, Clusive::In, 1.0);
    6185            0 :                         ErrorsFound = true;
    6186              :                     }
    6187            0 :                 } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) { // delta, must be > 0.0
    6188            0 :                     if (!desDayInput.tempRangeSched->checkMinVal(state, Clusive::In, 0.0)) {
    6189            0 :                         Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0);
    6190            0 :                         ErrorsFound = true;
    6191              :                     }
    6192              :                 }
    6193              : 
    6194            0 :                 auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    6195            0 :                 Real64 testval = std::numeric_limits<Real64>::min();
    6196            0 :                 for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
    6197            0 :                     for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
    6198            0 :                         if (desDayModsEnvrn(iTS, iHr).OutDryBulbTemp > testval) {
    6199            0 :                             testval = desDayModsEnvrn(iTS, iHr).OutDryBulbTemp;
    6200              :                         }
    6201              :                     }
    6202              :                 }
    6203              : 
    6204            0 :                 if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
    6205            0 :                     if (MaxDryBulbEntered) {
    6206            0 :                         ShowWarningError(state, format("{}=\"{}\", data override.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6207            0 :                         ShowContinueError(state, format("..{}=[{:.2R}] will be overwritten.", ipsc->cNumericFieldNames(3), desDayInput.MaxDryBulb));
    6208            0 :                         ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3)));
    6209            0 :                         ShowContinueError(state, format("..with max value=[{:.2R}].", testval));
    6210              :                     }
    6211            0 :                     desDayInput.MaxDryBulb = testval;
    6212              :                 }
    6213              : 
    6214            0 :                 testval = desDayInput.MaxDryBulb - testval;
    6215            0 :                 if (testval < -90.0 || testval > 70.0) {
    6216            0 :                     ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
    6217              :                     // should this be cNumericFieldNames?
    6218            0 :                     ShowContinueError(state, format("{} = ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(4), testval));
    6219            0 :                     ErrorsFound = true;
    6220              :                 }
    6221              :             }
    6222              : 
    6223              :             //   A5,  \field Humidity Condition Type
    6224          182 :             desDayInput.HumIndType = static_cast<DesDayHumIndType>(getEnumValue(DesDayHumIndTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
    6225              : 
    6226          182 :             switch (desDayInput.HumIndType) {
    6227          174 :             case DesDayHumIndType::WetBulb: {
    6228              :                 //   N5,  \field Wetbulb or DewPoint at Maximum Dry-Bulb
    6229          174 :                 if (ipsc->lNumericFieldBlanks(5)) {
    6230            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6231            0 :                     ErrorsFound = true;
    6232              :                 } else {
    6233          174 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
    6234              :                 }
    6235              : 
    6236          174 :                 if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
    6237            0 :                     ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
    6238            0 :                     ShowContinueError(
    6239              :                         state,
    6240            0 :                         format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - WetBulb", desDayInput.HumIndValue));
    6241            0 :                     ErrorsFound = true;
    6242              :                 }
    6243          174 :             } break;
    6244              : 
    6245            0 :             case DesDayHumIndType::DewPoint: {
    6246            0 :                 if (ipsc->lNumericFieldBlanks(5)) {
    6247            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6248            0 :                     ErrorsFound = true;
    6249              :                 } else {
    6250            0 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
    6251              :                 }
    6252              : 
    6253            0 :                 if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
    6254            0 :                     ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
    6255            0 :                     ShowContinueError(
    6256              :                         state,
    6257            0 :                         format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - DewPoint", desDayInput.HumIndValue));
    6258            0 :                     ErrorsFound = true;
    6259              :                 }
    6260            0 :             } break;
    6261              : 
    6262            0 :             case DesDayHumIndType::HumRatio: {
    6263              :                 //   N6,  \field Humidity Ratio at Maximum Dry-Bulb
    6264            0 :                 if (ipsc->lNumericFieldBlanks(6)) {
    6265            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(6), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6266            0 :                     ErrorsFound = true;
    6267              :                 } else {
    6268            0 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(6); // Humidity Indicating Conditions at Max Dry-Bulb
    6269              :                 }
    6270              : 
    6271            0 :                 if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 0.03) {
    6272            0 :                     ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
    6273            0 :                     ShowContinueError(
    6274              :                         state,
    6275            0 :                         format("{} = {:.2R} is out of range [0.0, 0.03]", ipsc->cAlphaFieldNames(5) + " - Humidity-Ratio", desDayInput.HumIndValue));
    6276            0 :                     ErrorsFound = true;
    6277              :                 }
    6278            0 :             } break;
    6279              : 
    6280            1 :             case DesDayHumIndType::Enthalpy: {
    6281              :                 //   N7,  \field Enthalpy at Maximum Dry-Bulb {J/kg}.
    6282            1 :                 if (ipsc->lNumericFieldBlanks(7)) {
    6283            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(7), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6284            0 :                     ErrorsFound = true;
    6285              :                 } else {
    6286            1 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(7); // Humidity Indicating Conditions at Max Dry-Bulb
    6287              :                 }
    6288              : 
    6289            1 :                 desDayInput.HumIndType = DesDayHumIndType::Enthalpy;
    6290            1 :                 if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 130000.0) {
    6291            0 :                     ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
    6292            0 :                     ShowContinueError(
    6293              :                         state,
    6294            0 :                         format("{} = {.0R} is out of range [0.0, 130000.0]", ipsc->cAlphaFieldNames(5) + " - Enthalpy", desDayInput.HumIndValue));
    6295            0 :                     ErrorsFound = true;
    6296              :                 }
    6297            1 :             } break;
    6298              : 
    6299            0 :             case DesDayHumIndType::RelHumSch: {
    6300              :                 // units = "[%]";
    6301            0 :                 unitType = Constant::Units::Perc;
    6302            0 :             } break;
    6303              : 
    6304            0 :             case DesDayHumIndType::WBProfMul: {
    6305              :                 // units = "[]";
    6306            0 :                 unitType = Constant::Units::None;
    6307            0 :                 if (ipsc->lNumericFieldBlanks(5)) {
    6308            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6309            0 :                     ErrorsFound = true;
    6310              :                 } else {
    6311            0 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
    6312              :                 }
    6313            0 :             } break;
    6314              : 
    6315            0 :             case DesDayHumIndType::WBProfDif: {
    6316              :                 // units = "[]";
    6317            0 :                 unitType = Constant::Units::None;
    6318            0 :                 if (ipsc->lNumericFieldBlanks(5)) {
    6319            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6320            0 :                     ErrorsFound = true;
    6321              :                 } else {
    6322            0 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
    6323              :                 }
    6324            0 :             } break;
    6325              : 
    6326            7 :             case DesDayHumIndType::WBProfDef: {
    6327            7 :                 if (ipsc->lNumericFieldBlanks(5)) {
    6328            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
    6329            0 :                     ErrorsFound = true;
    6330              :                 } else {
    6331            7 :                     desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
    6332              :                 }
    6333            7 :             } break;
    6334              : 
    6335            0 :             default: {
    6336            0 :                 ShowWarningError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6337            0 :                 ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
    6338            0 :                 ShowContinueError(state, "WetBulb will be used. Maximum Dry Bulb will be used as WetBulb at Maximum Dry Bulb.");
    6339            0 :                 desDayInput.HumIndType = DesDayHumIndType::WetBulb;
    6340            0 :                 desDayInput.HumIndValue = ipsc->rNumericArgs(3);
    6341            0 :             } break;
    6342              :             } // switch (desDayInput.HumIndType)
    6343              : 
    6344              :             // resolve humidity schedule if needed
    6345              :             //   A6,  \field Humidity Condition Day Schedule Name
    6346          182 :             if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch || desDayInput.HumIndType == DesDayHumIndType::WBProfMul ||
    6347          182 :                 desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
    6348            0 :                 if (ipsc->lAlphaFieldBlanks(6)) {
    6349            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    6350            0 :                     ErrorsFound = true;
    6351            0 :                 } else if ((desDayInput.humIndSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(6))) == nullptr) {
    6352            0 :                     ShowWarningItemNotFound(state,
    6353              :                                             eoh,
    6354            0 :                                             ipsc->cAlphaFieldNames(6),
    6355            0 :                                             ipsc->cAlphaArgs(6),
    6356              :                                             "Default Humidity (constant for day using Humidity Indicator Temp).");
    6357              :                     // reset HumIndType ?
    6358              :                 } else {
    6359            0 :                     std::vector<Real64> const &dayVals = desDayInput.humIndSched->getDayVals(state);
    6360            0 :                     auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    6361            0 :                     for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    6362            0 :                         for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    6363            0 :                             desDayModsEnvrn(ts + 1, hr + 1).OutRelHum = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    6364              :                         }
    6365              :                     }
    6366              : 
    6367            0 :                     if (std::find(state.dataWeather->spSiteSchedNums.begin(),
    6368            0 :                                   state.dataWeather->spSiteSchedNums.end(),
    6369            0 :                                   desDayInput.humIndSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
    6370            0 :                         state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.humIndSched->Num);
    6371            0 :                         SetupOutputVariable(state,
    6372              :                                             "Sizing Period Site Humidity Condition Schedule Value",
    6373              :                                             unitType,
    6374            0 :                                             state.dataWeather->spSiteSchedules(EnvrnNum).OutRelHum,
    6375              :                                             OutputProcessor::TimeStepType::Zone,
    6376              :                                             OutputProcessor::StoreType::Average,
    6377            0 :                                             ipsc->cAlphaArgs(6));
    6378              :                     }
    6379              : 
    6380            0 :                     switch (desDayInput.HumIndType) {
    6381            0 :                     case DesDayHumIndType::RelHumSch: {
    6382            0 :                         if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 100.0)) {
    6383            0 :                             Sched::ShowSevereBadMinMax(
    6384            0 :                                 state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 100.0);
    6385            0 :                             ErrorsFound = true;
    6386              :                         }
    6387            0 :                     } break;
    6388            0 :                     case DesDayHumIndType::WBProfMul: {
    6389              :                         // multiplier: use schedule value, check 0 <= v <= 1
    6390            0 :                         if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
    6391            0 :                             Sched::ShowSevereBadMinMax(
    6392            0 :                                 state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 1.0);
    6393            0 :                             ErrorsFound = true;
    6394              :                         }
    6395            0 :                     } break;
    6396            0 :                     case DesDayHumIndType::WBProfDif: {
    6397            0 :                         if (!desDayInput.humIndSched->checkMinVal(state, Clusive::In, 0.0)) {
    6398            0 :                             Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0);
    6399            0 :                             ErrorsFound = true;
    6400              :                         }
    6401            0 :                     } break;
    6402            0 :                     default: {
    6403            0 :                     } break;
    6404              :                     } // switch (desDayInput.HumIndType)
    6405              :                 } // if (desDayInput.HumIndSchPtr == 0)
    6406              : 
    6407          182 :             } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
    6408              :                 // re WetBulbProfileDefaultMultipliers
    6409            7 :                 Real64 LastHrValue = DefaultTempRangeMult[23];
    6410          175 :                 for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
    6411          456 :                     for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
    6412          288 :                         Real64 WNow = state.dataWeather->Interpolation(ts);
    6413          288 :                         Real64 WPrev = 1.0 - WNow;
    6414          288 :                         state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutRelHum = LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
    6415              :                     }
    6416          168 :                     LastHrValue = DefaultTempRangeMult[hour - 1];
    6417              :                 }
    6418              :             }
    6419              : 
    6420              :             // verify that design WB or DP <= design DB
    6421          182 :             if (desDayInput.HumIndType == DesDayHumIndType::DewPoint || desDayInput.HumIndType == DesDayHumIndType::WetBulb ||
    6422            8 :                 desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::WBProfDef ||
    6423            1 :                 desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
    6424          181 :                 if (desDayInput.HumIndValue > desDayInput.MaxDryBulb) {
    6425            0 :                     ShowWarningError(state, format("{}=\"{}\", range check data.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6426            0 :                     ShowContinueError(state,
    6427            0 :                                       format("..Humidity Indicator Temperature at Max Temperature={:.1R} > Max DryBulb={:.1R}",
    6428            0 :                                              desDayInput.HumIndValue,
    6429            0 :                                              desDayInput.MaxDryBulb));
    6430            0 :                     ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
    6431            0 :                     ShowContinueError(state, "..Conditions for day will be set to Relative Humidity = 100%");
    6432            0 :                     if (desDayInput.HumIndType == DesDayHumIndType::DewPoint) {
    6433            0 :                         desDayInput.DewPointNeedsSet = true;
    6434              :                     } else {
    6435              :                         // wet-bulb
    6436            0 :                         desDayInput.HumIndValue = desDayInput.MaxDryBulb;
    6437              :                     }
    6438              :                 }
    6439              :             }
    6440              : 
    6441              :             //   A10, \field Solar Model Indicator
    6442          182 :             if (ipsc->lAlphaFieldBlanks(10)) {
    6443            0 :                 desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
    6444          364 :             } else if ((desDayInput.solarModel = static_cast<DesDaySolarModel>(
    6445          182 :                             getEnumValue(DesDaySolarModelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(10))))) != DesDaySolarModel::Invalid) {
    6446              :             } else {
    6447            0 :                 ShowWarningInvalidKey(state, eoh, ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "ASHRAE ClearSky");
    6448            0 :                 desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
    6449              :             }
    6450              : 
    6451          182 :             if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
    6452              :                 //   A11, \field Beam Solar Day Schedule Name
    6453            0 :                 if (ipsc->lAlphaFieldBlanks(11)) {
    6454              :                     // should have entered beam schedule
    6455            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(11));
    6456            0 :                     ErrorsFound = true;
    6457            0 :                 } else if ((desDayInput.beamSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(11))) == nullptr) {
    6458            0 :                     ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11));
    6459            0 :                     ErrorsFound = true;
    6460              :                 } else {
    6461            0 :                     std::vector<Real64> const &dayVals = desDayInput.beamSolarSched->getDayVals(state);
    6462            0 :                     auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    6463            0 :                     for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    6464            0 :                         for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    6465            0 :                             desDayModsEnvrn(ts + 1, hr + 1).BeamSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    6466              :                         }
    6467              :                     }
    6468              : 
    6469            0 :                     unitType = Constant::Units::W_m2;
    6470              :                     // units = "[W/m2]";
    6471            0 :                     if (std::find(state.dataWeather->spSiteSchedNums.begin(),
    6472            0 :                                   state.dataWeather->spSiteSchedNums.end(),
    6473            0 :                                   desDayInput.beamSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
    6474            0 :                         state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.beamSolarSched->Num);
    6475            0 :                         SetupOutputVariable(state,
    6476              :                                             "Sizing Period Site Beam Solar Schedule Value",
    6477              :                                             unitType,
    6478            0 :                                             state.dataWeather->spSiteSchedules(EnvrnNum).BeamSolarRad,
    6479              :                                             OutputProcessor::TimeStepType::Zone,
    6480              :                                             OutputProcessor::StoreType::Average,
    6481            0 :                                             ipsc->cAlphaArgs(11));
    6482              :                     }
    6483              : 
    6484            0 :                     if (!desDayInput.beamSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
    6485            0 :                         Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11), Clusive::In, 0.0);
    6486            0 :                         ErrorsFound = true;
    6487              :                     }
    6488              :                 }
    6489              : 
    6490              :                 //   A12, \field Diffuse Solar Day Schedule Name
    6491            0 :                 if (ipsc->lAlphaFieldBlanks(12)) {
    6492              :                     // should have entered diffuse schedule
    6493            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(12));
    6494            0 :                     ErrorsFound = true;
    6495            0 :                 } else if ((desDayInput.diffuseSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(12))) == nullptr) {
    6496            0 :                     ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12));
    6497            0 :                     ErrorsFound = true;
    6498              :                 } else {
    6499            0 :                     std::vector<Real64> const &dayVals = desDayInput.diffuseSolarSched->getDayVals(state);
    6500            0 :                     auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
    6501            0 :                     for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
    6502            0 :                         for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
    6503            0 :                             desDayModsEnvrn(ts + 1, hr + 1).DifSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
    6504              :                         }
    6505              :                     }
    6506              : 
    6507              :                     // units = "[W/m2]";
    6508            0 :                     unitType = Constant::Units::W_m2;
    6509            0 :                     if (std::find(state.dataWeather->spSiteSchedNums.begin(),
    6510            0 :                                   state.dataWeather->spSiteSchedNums.end(),
    6511            0 :                                   desDayInput.diffuseSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
    6512            0 :                         state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.diffuseSolarSched->Num);
    6513            0 :                         SetupOutputVariable(state,
    6514              :                                             "Sizing Period Site Diffuse Solar Schedule Value",
    6515              :                                             unitType,
    6516            0 :                                             state.dataWeather->spSiteSchedules(EnvrnNum).DifSolarRad,
    6517              :                                             OutputProcessor::TimeStepType::Zone,
    6518              :                                             OutputProcessor::StoreType::Average,
    6519            0 :                                             ipsc->cAlphaArgs(12));
    6520              :                     }
    6521            0 :                     if (!desDayInput.diffuseSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
    6522            0 :                         Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12), Clusive::In, 0.0);
    6523            0 :                         ErrorsFound = true;
    6524              :                     }
    6525              :                 }
    6526              : 
    6527          182 :             } else if (desDayInput.solarModel == DesDaySolarModel::ASHRAE_ClearSky) {
    6528          167 :                 if (ipsc->lNumericFieldBlanks(14)) {
    6529            0 :                     ShowWarningEmptyField(
    6530            0 :                         state, eoh, ipsc->cNumericFieldNames(14), ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "Zero clear sky (no solar)");
    6531              :                 }
    6532              :             }
    6533              : 
    6534              :             // Validate Design Day Month
    6535              : 
    6536          182 :             switch (desDayInput.Month) {
    6537          179 :             case 1:
    6538              :             case 3:
    6539              :             case 5:
    6540              :             case 7:
    6541              :             case 8:
    6542              :             case 10:
    6543              :             case 12: {
    6544          179 :                 if (desDayInput.DayOfMonth > 31) {
    6545            0 :                     ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6546            0 :                     ShowContinueError(
    6547              :                         state,
    6548            0 :                         format(".. invalid field: {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
    6549            0 :                     ErrorsFound = true;
    6550              :                 }
    6551          179 :             } break;
    6552            1 :             case 4:
    6553              :             case 6:
    6554              :             case 9:
    6555              :             case 11: {
    6556            1 :                 if (desDayInput.DayOfMonth > 30) {
    6557            0 :                     ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6558            0 :                     ShowContinueError(
    6559            0 :                         state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
    6560            0 :                     ErrorsFound = true;
    6561              :                 }
    6562            1 :             } break;
    6563            2 :             case 2: {
    6564            2 :                 if (desDayInput.DayOfMonth > 28) {
    6565            0 :                     ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6566            0 :                     ShowContinueError(
    6567            0 :                         state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
    6568            0 :                     ErrorsFound = true;
    6569              :                 }
    6570            2 :             } break;
    6571            0 :             default: {
    6572            0 :                 ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
    6573            0 :                 ShowContinueError(state, format(".. invalid {} invalid (Month) [{}].", ipsc->cNumericFieldNames(1), desDayInput.Month));
    6574            0 :                 ErrorsFound = true;
    6575            0 :             } break;
    6576              :             } // switch (desDayInput.Month)
    6577              : 
    6578              :             //   A9,  \field Daylight Saving Time Indicator
    6579          182 :             if (ipsc->lAlphaFieldBlanks(9)) {
    6580            0 :                 desDayInput.DSTIndicator = 0;
    6581          182 :             } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(9)))) != BooleanSwitch::Invalid) {
    6582          182 :                 desDayInput.DSTIndicator = (int)b;
    6583              :             } else {
    6584            0 :                 ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(9), ipsc->cAlphaArgs(9), "No");
    6585            0 :                 desDayInput.DSTIndicator = 0;
    6586              :             }
    6587              : 
    6588              :             //   A2,  \field Day Type
    6589          182 :             desDayInput.DayType = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
    6590          182 :             if (desDayInput.DayType <= 0) {
    6591            0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
    6592            0 :                 ErrorsFound = true;
    6593              :             }
    6594              : 
    6595          182 :             auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
    6596          182 :             envCurr.Title = desDayInput.Title;
    6597          182 :             envCurr.KindOfEnvrn = Constant::KindOfSim::DesignDay;
    6598          182 :             envCurr.DesignDayNum = EnvrnNum;
    6599          182 :             envCurr.RunPeriodDesignNum = 0;
    6600          182 :             envCurr.TotalDays = 1;
    6601          182 :             envCurr.StartMonth = desDayInput.Month;
    6602          182 :             envCurr.StartDay = desDayInput.DayOfMonth;
    6603          182 :             envCurr.EndMonth = envCurr.StartMonth;
    6604          182 :             envCurr.EndDay = envCurr.StartDay;
    6605          182 :             envCurr.DayOfWeek = 0;
    6606          182 :             envCurr.UseDST = false;
    6607          182 :             envCurr.UseHolidays = false;
    6608          182 :             envCurr.StartJDay = designDay.DayOfYear;
    6609          182 :             envCurr.EndJDay = envCurr.StartJDay;
    6610              : 
    6611              :             // create predefined report on design day
    6612          182 :             std::string envTitle = desDayInput.Title;
    6613          182 :             OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDmaxDB, envTitle, desDayInput.MaxDryBulb);
    6614          182 :             OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDrange, envTitle, desDayInput.DailyDBRange);
    6615          182 :             if (desDayInput.HumIndType != DesDayHumIndType::RelHumSch) {
    6616          182 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, desDayInput.HumIndValue);
    6617              :             } else {
    6618            0 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, "N/A");
    6619              :             }
    6620          364 :             OutputReportPredefined::PreDefTableEntry(
    6621          364 :                 state, state.dataOutRptPredefined->pdchDDhumTyp, envTitle, DesDayHumIndTypeStringRep[(int)desDayInput.HumIndType]);
    6622          182 :             OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindSp, envTitle, desDayInput.WindSpeed);
    6623          182 :             OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindDr, envTitle, desDayInput.WindDir);
    6624              :         }
    6625          106 :     }
    6626              : 
    6627          115 :     void GetLocationInfo(EnergyPlusData &state, bool &ErrorsFound)
    6628              :     {
    6629              : 
    6630              :         // SUBROUTINE INFORMATION:
    6631              :         //       AUTHOR         Richard Liesen
    6632              :         //       DATE WRITTEN   October 1997
    6633              : 
    6634              :         // PURPOSE OF THIS SUBROUTINE:
    6635              :         // This subroutine gets the location info from the IDF file; latitude,
    6636              :         //  longitude and time zone number.
    6637              : 
    6638          115 :         auto const &ipsc = state.dataIPShortCut;
    6639          115 :         ipsc->cCurrentModuleObject = "Site:Location";
    6640          115 :         int const NumLocations = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    6641              : 
    6642          115 :         if (NumLocations > 1) {
    6643            0 :             ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
    6644            0 :             ErrorsFound = true;
    6645              :         }
    6646              : 
    6647          115 :         if (NumLocations == 1) {
    6648              :             int LocNumAlpha;             // Number of alpha names being passed
    6649              :             int LocNumProp;              // Number of properties being passed
    6650              :             int IOStat;                  // IO Status when calling get input subroutine
    6651          112 :             Array1D_string LocAlphas(2); // Temporary array to transfer location info (non-numerics)
    6652          112 :             Array1D<Real64> LocProps(4); // Temporary array to transfer location info (numerics)
    6653              :             // Call Input Get routine to retrieve Location information
    6654          224 :             state.dataInputProcessing->inputProcessor->getObjectItem(
    6655          112 :                 state, ipsc->cCurrentModuleObject, 1, LocAlphas, LocNumAlpha, LocProps, LocNumProp, IOStat);
    6656              : 
    6657              :             // set latitude, longitude, and time zone number variables
    6658          112 :             state.dataWeather->LocationTitle = LocAlphas(1);
    6659          112 :             state.dataEnvrn->Latitude = LocProps(1);
    6660          112 :             state.dataEnvrn->Longitude = LocProps(2);
    6661          112 :             state.dataEnvrn->TimeZoneNumber = LocProps(3);
    6662          112 :             state.dataEnvrn->Elevation = LocProps(4);
    6663          112 :             if (Util::SameString(LocAlphas(2), "Yes")) {
    6664            1 :                 state.dataWeather->keepUserSiteLocationDefinition = true;
    6665              :             }
    6666          112 :             state.dataWeather->LocationGathered = true;
    6667          112 :         }
    6668          115 :     }
    6669              : 
    6670          111 :     void GetWeatherProperties(EnergyPlusData &state, bool &ErrorsFound)
    6671              :     {
    6672              : 
    6673              :         // SUBROUTINE INFORMATION:
    6674              :         //       AUTHOR         Linda Lawrie
    6675              :         //       DATE WRITTEN   July 2009
    6676              : 
    6677              :         // PURPOSE OF THIS SUBROUTINE:
    6678              :         // Weather properties are an advanced concept for simulation.  Primarily, these properties are
    6679              :         // used in the test suite runs that have specific requirements for certain properties (such as
    6680              :         // sky temperature).
    6681              : 
    6682              :         // REFERENCES:
    6683              :         // WeatherProperty:SkyTemperature,
    6684              :         //        \memo This object is used to override internal sky temperature calculations.
    6685              :         //   A1,  \field Name
    6686              :         //        \reference DesignDays
    6687              :         //        \note leave blank for RunPeriods (until we name them)
    6688              :         //        \note This field references the applicable design day or runperiod(s) if left blank.
    6689              :         //   A2,  \field Calculation Type
    6690              :         //        \type choice
    6691              :         //        \key ScheduleValue
    6692              :         //        \key DifferenceScheduleDryBulbValue
    6693              :         //        \key DifferenceScheduleDewPointValue
    6694              :         //        \key AlgorithmA
    6695              :         //   A3;  \field Schedule Name
    6696              :         //        \type object-list
    6697              :         //        \object-list DayScheduleNames
    6698              :         //        \object-list ScheduleNames
    6699              : 
    6700              :         static constexpr std::string_view routineName = "GetWeatherProperties";
    6701              : 
    6702              :         int Found;
    6703              :         int envFound;
    6704              : 
    6705          111 :         auto const &ipsc = state.dataIPShortCut;
    6706          111 :         ipsc->cCurrentModuleObject = "WeatherProperty:SkyTemperature";
    6707          111 :         state.dataWeather->NumWPSkyTemperatures = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    6708              : 
    6709          111 :         state.dataWeather->WPSkyTemperature.allocate(state.dataWeather->NumWPSkyTemperatures); // by default, not used.
    6710              : 
    6711          111 :         for (int i = 1; i <= state.dataWeather->NumWPSkyTemperatures; ++i) {
    6712              :             int IOStat;
    6713              :             int NumAlpha;
    6714              :             int NumNumerics;
    6715            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    6716            0 :                                                                      ipsc->cCurrentModuleObject,
    6717              :                                                                      i,
    6718            0 :                                                                      ipsc->cAlphaArgs,
    6719              :                                                                      NumAlpha,
    6720            0 :                                                                      ipsc->rNumericArgs,
    6721              :                                                                      NumNumerics,
    6722              :                                                                      IOStat,
    6723            0 :                                                                      ipsc->lNumericFieldBlanks,
    6724            0 :                                                                      ipsc->lAlphaFieldBlanks,
    6725            0 :                                                                      ipsc->cAlphaFieldNames,
    6726            0 :                                                                      ipsc->cNumericFieldNames);
    6727              : 
    6728            0 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
    6729            0 :             auto &wpSkyTemp = state.dataWeather->WPSkyTemperature(i);
    6730            0 :             if (ipsc->cAlphaArgs(1).empty()) {
    6731            0 :                 Found = 0;
    6732            0 :                 for (int j = 1; j <= state.dataWeather->NumOfEnvrn; ++j) {
    6733            0 :                     auto &environJ = state.dataWeather->Environment(j);
    6734            0 :                     if (environJ.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
    6735            0 :                         continue;
    6736              :                     }
    6737            0 :                     if (environJ.WP_Type1 != 0) {
    6738            0 :                         ShowSevereError(state,
    6739            0 :                                         format("{}: {}=\"{}\", indicated Environment Name already assigned.",
    6740              :                                                routineName,
    6741            0 :                                                ipsc->cCurrentModuleObject,
    6742            0 :                                                ipsc->cAlphaArgs(1)));
    6743            0 :                         if (!environJ.Title.empty()) {
    6744            0 :                             ShowContinueError(state,
    6745            0 :                                               format("...Environment=\"{}\", already using {}=\"{}\".",
    6746            0 :                                                      environJ.Title,
    6747            0 :                                                      ipsc->cCurrentModuleObject,
    6748            0 :                                                      state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
    6749              :                         } else {
    6750            0 :                             ShowContinueError(state,
    6751            0 :                                               format("... Runperiod Environment, already using {}=\"{}\".",
    6752            0 :                                                      ipsc->cCurrentModuleObject,
    6753            0 :                                                      state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
    6754              :                         }
    6755            0 :                         ErrorsFound = true;
    6756              :                     } else {
    6757            0 :                         environJ.WP_Type1 = i;
    6758            0 :                         Found = j;
    6759              :                     }
    6760              :                 }
    6761            0 :                 if (Found == 0) {
    6762            0 :                     ShowWarningError(state, "GetWeatherProperties: WeatherProperty:SkyTemperature=blank, no run periods found.");
    6763            0 :                     ShowContinueError(state, "...SkyTemperature will not be applied.");
    6764            0 :                     continue;
    6765              :                 }
    6766              :             } else { // really a name
    6767            0 :                 Found = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataWeather->Environment, &EnvironmentData::Title);
    6768            0 :                 envFound = Found;
    6769            0 :                 if (Found == 0) {
    6770            0 :                     ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
    6771            0 :                     ErrorsFound = true;
    6772            0 :                     continue;
    6773              :                 }
    6774              : 
    6775            0 :                 auto &envrnFound = state.dataWeather->Environment(Found);
    6776            0 :                 if (envrnFound.WP_Type1 != 0) {
    6777            0 :                     ShowSevereError(state,
    6778            0 :                                     format("{}:{}=\"{}\", indicated Environment Name already assigned.",
    6779              :                                            routineName,
    6780            0 :                                            ipsc->cCurrentModuleObject,
    6781            0 :                                            ipsc->cAlphaArgs(1)));
    6782            0 :                     ShowContinueError(state,
    6783            0 :                                       format("...Environment=\"{}\", already using {}=\"{}\".",
    6784            0 :                                              envrnFound.Title,
    6785            0 :                                              ipsc->cCurrentModuleObject,
    6786            0 :                                              state.dataWeather->WPSkyTemperature(envrnFound.WP_Type1).Name));
    6787            0 :                     ErrorsFound = true;
    6788              :                 } else {
    6789            0 :                     state.dataWeather->Environment(Found).WP_Type1 = i;
    6790              :                 }
    6791              :             }
    6792              : 
    6793            0 :             wpSkyTemp.Name = !ipsc->lAlphaFieldBlanks(1) ? ipsc->cAlphaArgs(1) : "All RunPeriods";
    6794              : 
    6795              :             // Validate Calculation Type.
    6796              :             // std::string units;
    6797              :             Constant::Units unitType;
    6798            0 :             wpSkyTemp.skyTempModel = static_cast<SkyTempModel>(getEnumValue(Weather::SkyTempModelNamesUC, ipsc->cAlphaArgs(2)));
    6799              : 
    6800            0 :             switch (wpSkyTemp.skyTempModel) {
    6801            0 :             case SkyTempModel::ScheduleValue: {
    6802            0 :                 wpSkyTemp.IsSchedule = true;
    6803              :                 // units = "[C]";
    6804            0 :                 unitType = Constant::Units::C;
    6805            0 :             } break;
    6806            0 :             case SkyTempModel::DryBulbDelta:
    6807              :             case SkyTempModel::DewPointDelta: {
    6808            0 :                 wpSkyTemp.IsSchedule = true;
    6809              :                 // units = "[deltaC]";
    6810            0 :                 unitType = Constant::Units::deltaC;
    6811            0 :             } break;
    6812            0 :             case SkyTempModel::Brunt:
    6813              :             case SkyTempModel::Idso:
    6814              :             case SkyTempModel::BerdahlMartin:
    6815              :             case SkyTempModel::ClarkAllen: {
    6816            0 :                 wpSkyTemp.IsSchedule = false;
    6817            0 :             } break;
    6818            0 :             default: {
    6819              :                 // Bad inputs are trapped by input processor
    6820            0 :                 assert(false);
    6821              :             } break;
    6822              :             } // switch (skyTempModel)
    6823              : 
    6824            0 :             if (wpSkyTemp.IsSchedule) {
    6825            0 :                 if (state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather ||
    6826            0 :                     state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
    6827              :                     // See if it's a schedule.
    6828            0 :                     if ((wpSkyTemp.sched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
    6829            0 :                         ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    6830            0 :                         ErrorsFound = true;
    6831              :                     } else {
    6832            0 :                         wpSkyTemp.IsSchedule = true;
    6833              :                     }
    6834              :                 } else { // See if it's a valid schedule.
    6835              :                     // How can a schedule be either a yearly schedule or a day schedule?
    6836            0 :                     if ((wpSkyTemp.sched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
    6837            0 :                         ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
    6838            0 :                         ErrorsFound = true;
    6839              :                     } else {
    6840            0 :                         if (envFound != 0) {
    6841            0 :                             if (std::find(state.dataWeather->spSiteSchedNums.begin(),
    6842            0 :                                           state.dataWeather->spSiteSchedNums.end(),
    6843            0 :                                           wpSkyTemp.sched->Num) == state.dataWeather->spSiteSchedNums.end()) {
    6844            0 :                                 state.dataWeather->spSiteSchedNums.emplace_back(wpSkyTemp.sched->Num);
    6845            0 :                                 SetupOutputVariable(state,
    6846              :                                                     "Sizing Period Site Sky Temperature Schedule Value",
    6847              :                                                     unitType,
    6848            0 :                                                     state.dataWeather->spSiteSchedules(envFound).SkyTemp,
    6849              :                                                     OutputProcessor::TimeStepType::Zone,
    6850              :                                                     OutputProcessor::StoreType::Average,
    6851            0 :                                                     ipsc->cAlphaArgs(3));
    6852              :                             }
    6853            0 :                             wpSkyTemp.IsSchedule = true;
    6854              :                         }
    6855              :                     }
    6856              :                 }
    6857              :             }
    6858              : 
    6859            0 :             if (!wpSkyTemp.IsSchedule && !ipsc->lAlphaFieldBlanks(4)) {
    6860            0 :                 if (BooleanSwitch b = getYesNoValue(ipsc->cAlphaArgs(4)); b != BooleanSwitch::Invalid) {
    6861            0 :                     wpSkyTemp.UseWeatherFileHorizontalIR = static_cast<bool>(b);
    6862              :                 } else {
    6863            0 :                     ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
    6864            0 :                     ErrorsFound = true;
    6865              :                 }
    6866              :             } else {
    6867            0 :                 wpSkyTemp.UseWeatherFileHorizontalIR = true;
    6868              :             }
    6869              :         }
    6870          341 :         for (auto &envCurr : state.dataWeather->Environment) {
    6871          230 :             if (envCurr.WP_Type1 != 0 && state.dataWeather->NumWPSkyTemperatures >= envCurr.WP_Type1) {
    6872            0 :                 envCurr.skyTempModel = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel;
    6873            0 :                 envCurr.UseWeatherFileHorizontalIR = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).UseWeatherFileHorizontalIR;
    6874              :             }
    6875              :         }
    6876          111 :     }
    6877              : 
    6878          111 :     void GetGroundTemps(EnergyPlusData &state)
    6879              :     {
    6880              : 
    6881              :         // SUBROUTINE INFORMATION:
    6882              :         //       AUTHOR         Richard Liesen
    6883              :         //       DATE WRITTEN   October 1997
    6884              : 
    6885              :         // PURPOSE OF THIS SUBROUTINE:
    6886              :         // This file reads the Ground Temps from the input file and puts them
    6887              :         //  in a new variable.
    6888              : 
    6889              :         // Initialize Site:GroundTemperature:BuildingSurface object
    6890          222 :         state.dataWeather->siteBuildingSurfaceGroundTempsPtr =
    6891          222 :             GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteBuildingSurface, "");
    6892              : 
    6893              :         // Initialize Site:GroundTemperature:FCFactorMethod object
    6894          222 :         state.dataWeather->siteFCFactorMethodGroundTempsPtr =
    6895          222 :             GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteFCFactorMethod, "");
    6896              : 
    6897              :         // Initialize Site:GroundTemperature:Shallow object
    6898          222 :         state.dataWeather->siteShallowGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteShallow, "");
    6899              : 
    6900              :         // Initialize Site:GroundTemperature:Deep object
    6901          111 :         state.dataWeather->siteDeepGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteDeep, "");
    6902          111 :     }
    6903              : 
    6904          111 :     void GetGroundReflectances(EnergyPlusData &state, bool &ErrorsFound)
    6905              :     {
    6906              : 
    6907              :         // SUBROUTINE INFORMATION:
    6908              :         //       AUTHOR         Linda Lawrie
    6909              :         //       DATE WRITTEN   March 2002
    6910              : 
    6911              :         // PURPOSE OF THIS SUBROUTINE:
    6912              :         // This file reads the Ground Reflectances from the input file (optional) and
    6913              :         // places them in the monthly array.
    6914              : 
    6915          111 :         auto const &ipsc = state.dataIPShortCut;
    6916          111 :         ipsc->cCurrentModuleObject = "Site:GroundReflectance";
    6917          111 :         int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    6918          111 :         if (nObjs != 0) {
    6919            0 :             Array1D_string GndAlphas(1);  // Construction Alpha names defined
    6920            0 :             Array1D<Real64> GndProps(12); // Temporary array to transfer ground reflectances
    6921            0 :             if (nObjs == 1) {
    6922              :                 int GndNumAlpha; // Number of construction alpha names being passed
    6923              :                 int GndNumProp;  // dummy variable for properties being passed
    6924              :                 int IOStat;      // IO Status when calling get input subroutine
    6925              :                 // Get the object names for each construction from the input processor
    6926            0 :                 state.dataInputProcessing->inputProcessor->getObjectItem(
    6927            0 :                     state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
    6928              : 
    6929            0 :                 if (GndNumProp < 12) {
    6930            0 :                     ShowSevereError(state, format("{}: Less than 12 values entered.", ipsc->cCurrentModuleObject));
    6931            0 :                     ErrorsFound = true;
    6932              :                 }
    6933              : 
    6934              :                 // Assign the ground reflectances to the variable
    6935            0 :                 state.dataWeather->GroundReflectances({1, 12}) = GndProps({1, 12});
    6936              : 
    6937              :             } else {
    6938            0 :                 ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
    6939            0 :                 ErrorsFound = true;
    6940              :             }
    6941            0 :         }
    6942              : 
    6943              :         // Write Final Ground Reflectance Information to the initialization output file
    6944          111 :         print(state.files.eio,
    6945              :               "{}\n",
    6946              :               "! "
    6947              :               "<Site:GroundReflectance>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{dimensionless},"
    6948              :               "May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{dimensionless},Oct{"
    6949              :               "dimensionless},Nov{dimensionless},Dec{dimensionless}");
    6950              : 
    6951          111 :         print(state.files.eio, " Site:GroundReflectance");
    6952         1443 :         for (int i = 1; i <= 12; ++i) {
    6953         1332 :             print(state.files.eio, ", {:5.2F}", state.dataWeather->GroundReflectances(i));
    6954              :         }
    6955          111 :         print(state.files.eio, "\n");
    6956          111 :     }
    6957              : 
    6958          111 :     void GetSnowGroundRefModifiers(EnergyPlusData &state, bool &ErrorsFound)
    6959              :     {
    6960              : 
    6961              :         // SUBROUTINE INFORMATION:
    6962              :         //       AUTHOR         Linda Lawrie
    6963              :         //       DATE WRITTEN   March 2002
    6964              : 
    6965              :         // PURPOSE OF THIS SUBROUTINE:
    6966              :         // This file reads the Snow Ground Reflectance Modifiers from the input file (optional) and
    6967              :         // places them in the variables.
    6968              : 
    6969          111 :         auto const &ipsc = state.dataIPShortCut;
    6970          111 :         ipsc->cCurrentModuleObject = "Site:GroundReflectance:SnowModifier";
    6971          111 :         int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    6972          111 :         if (nObjs != 0) {
    6973            0 :             Array1D_string GndAlphas(1); // Construction Alpha names defined
    6974            0 :             Array1D<Real64> GndProps(2); // Temporary array to transfer ground reflectances
    6975            0 :             if (nObjs == 1) {
    6976              :                 int GndNumAlpha; // Number of construction alpha names being passed
    6977              :                 int GndNumProp;  // dummy variable for properties being passed
    6978              :                 int IOStat;      // IO Status when calling get input subroutine
    6979              :                 // Get the object names for each construction from the input processor
    6980            0 :                 state.dataInputProcessing->inputProcessor->getObjectItem(
    6981            0 :                     state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
    6982              : 
    6983              :                 // Assign the ground reflectances to the variable
    6984            0 :                 state.dataWeather->SnowGndRefModifier = GndProps(1);
    6985            0 :                 state.dataWeather->SnowGndRefModifierForDayltg = GndProps(2);
    6986              : 
    6987              :             } else {
    6988            0 :                 ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
    6989            0 :                 ErrorsFound = true;
    6990              :             }
    6991            0 :         }
    6992              : 
    6993              :         // Write Final Ground Reflectance Modifier Information to the initialization output file
    6994          111 :         print(state.files.eio, "{}\n", "! <Site:GroundReflectance:SnowModifier>, Normal, Daylighting {dimensionless}");
    6995              :         static constexpr std::string_view Format_720(" Site:GroundReflectance:SnowModifier, {:7.3F}, {:7.3F}\n");
    6996          111 :         print(state.files.eio, Format_720, state.dataWeather->SnowGndRefModifier, state.dataWeather->SnowGndRefModifierForDayltg);
    6997              : 
    6998          111 :         print(state.files.eio,
    6999              :               "{}\n",
    7000              :               "! "
    7001              :               "<Site:GroundReflectance:Snow>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
    7002              :               "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
    7003              :               "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
    7004          111 :         print(state.files.eio, "{}", " Site:GroundReflectance:Snow");
    7005         1443 :         for (int i = 1; i <= 12; ++i) {
    7006         1332 :             print(state.files.eio, ", {:5.2F}", max(min(state.dataWeather->GroundReflectances(i) * state.dataWeather->SnowGndRefModifier, 1.0), 0.0));
    7007              :         }
    7008          111 :         print(state.files.eio, "\n");
    7009          111 :         print(state.files.eio,
    7010              :               "{}\n",
    7011              :               "! "
    7012              :               "<Site:GroundReflectance:Snow:Daylighting>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
    7013              :               "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
    7014              :               "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
    7015          111 :         print(state.files.eio, " Site:GroundReflectance:Snow:Daylighting");
    7016         1443 :         for (nObjs = 1; nObjs <= 12; ++nObjs) {
    7017         1332 :             print(state.files.eio,
    7018              :                   ", {:5.2F}",
    7019         2664 :                   max(min(state.dataWeather->GroundReflectances(nObjs) * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0));
    7020              :         }
    7021          111 :         print(state.files.eio, "\n");
    7022          111 :     }
    7023              : 
    7024          117 :     void GetWaterMainsTemperatures(EnergyPlusData &state, bool &ErrorsFound)
    7025              :     {
    7026              : 
    7027              :         // SUBROUTINE INFORMATION:
    7028              :         //       AUTHOR         Peter Graham Ellis
    7029              :         //       DATE WRITTEN   January 2005
    7030              : 
    7031              :         // PURPOSE OF THIS SUBROUTINE:
    7032              :         // Reads the input data for the WATER MAINS TEMPERATURES object.
    7033              : 
    7034          117 :         constexpr std::string_view routineName = "GetWaterMainsTemperatures";
    7035              : 
    7036          117 :         auto const &ipsc = state.dataIPShortCut;
    7037          117 :         ipsc->cCurrentModuleObject = "Site:WaterMainsTemperature";
    7038          117 :         int NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    7039              : 
    7040          117 :         if (NumObjects == 1) {
    7041              :             int NumAlphas;               // Number of elements in the alpha array
    7042              :             int NumNums;                 // Number of elements in the numeric array
    7043              :             int IOStat;                  // IO Status when calling get input subroutine
    7044           12 :             Array1D_string AlphArray(2); // Character string data
    7045           12 :             Array1D<Real64> NumArray(4); // Numeric data
    7046           36 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    7047           12 :                                                                      ipsc->cCurrentModuleObject,
    7048              :                                                                      1,
    7049              :                                                                      AlphArray,
    7050              :                                                                      NumAlphas,
    7051              :                                                                      NumArray,
    7052              :                                                                      NumNums,
    7053              :                                                                      IOStat,
    7054           12 :                                                                      ipsc->lNumericFieldBlanks,
    7055           12 :                                                                      ipsc->lAlphaFieldBlanks,
    7056           12 :                                                                      ipsc->cAlphaFieldNames,
    7057           12 :                                                                      ipsc->cNumericFieldNames);
    7058              : 
    7059           12 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
    7060              : 
    7061           24 :             state.dataWeather->WaterMainsTempsMethod =
    7062           12 :                 static_cast<Weather::WaterMainsTempCalcMethod>(getEnumValue(waterMainsCalcMethodNamesUC, AlphArray(1)));
    7063              : 
    7064           12 :             switch (state.dataWeather->WaterMainsTempsMethod) {
    7065            0 :             case WaterMainsTempCalcMethod::Schedule: {
    7066            0 :                 if (ipsc->lAlphaFieldBlanks(2)) {
    7067            0 :                     ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
    7068            0 :                     ErrorsFound = true;
    7069            0 :                 } else if ((state.dataWeather->waterMainsTempSched = Sched::GetSchedule(state, AlphArray(2))) == nullptr) {
    7070            0 :                     ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
    7071            0 :                     ErrorsFound = true;
    7072              :                 }
    7073            0 :             } break;
    7074              : 
    7075            6 :             case WaterMainsTempCalcMethod::Correlation: {
    7076            6 :                 if (NumNums == 0) {
    7077            0 :                     ShowSevereError(state, format("{}: Missing Annual Average and Maximum Difference fields.", ipsc->cCurrentModuleObject));
    7078            0 :                     ErrorsFound = true;
    7079            6 :                 } else if (NumNums == 1) {
    7080            0 :                     ShowSevereError(state, format("{}: Missing Maximum Difference field.", ipsc->cCurrentModuleObject));
    7081            0 :                     ErrorsFound = true;
    7082              :                 } else {
    7083            6 :                     state.dataWeather->WaterMainsTempsAnnualAvgAirTemp = NumArray(1);
    7084            6 :                     state.dataWeather->WaterMainsTempsMaxDiffAirTemp = NumArray(2);
    7085              :                 }
    7086            6 :             } break;
    7087            6 :             case WaterMainsTempCalcMethod::CorrelationFromWeatherFile: {
    7088              :                 // No action
    7089            6 :             } break;
    7090            0 :             default: {
    7091            0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), AlphArray(1));
    7092            0 :                 ErrorsFound = true;
    7093            0 :             } break;
    7094              :             } // switch
    7095              : 
    7096           12 :             state.dataWeather->WaterMainsTempsMultiplier = NumArray(3);
    7097           12 :             state.dataWeather->WaterMainsTempsOffset = NumArray(4);
    7098              : 
    7099          117 :         } else if (NumObjects > 1) {
    7100            0 :             ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
    7101            0 :             ErrorsFound = true;
    7102              :         }
    7103          117 :     }
    7104              : 
    7105       326130 :     void CalcWaterMainsTemp(EnergyPlusData &state)
    7106              :     {
    7107              : 
    7108              :         // SUBROUTINE INFORMATION:
    7109              :         //       AUTHOR         Peter Graham Ellis
    7110              :         //       DATE WRITTEN   January 2005
    7111              :         //       MODIFIED       June 2018, B. Nigusse
    7112              : 
    7113              :         // PURPOSE OF THIS SUBROUTINE:
    7114              :         // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
    7115              : 
    7116              :         // METHODOLOGY EMPLOYED:
    7117              :         // Water mains temperature is either taken from a schedule or calculated by a correlation.  The correlation
    7118              :         // is fit to Fahrenheit units, so the air temperature values are first convert to F, then mains temperature
    7119              :         // is calculated and converted back to C.
    7120              : 
    7121       326130 :         switch (state.dataWeather->WaterMainsTempsMethod) {
    7122            0 :         case WaterMainsTempCalcMethod::Schedule:
    7123            0 :             state.dataEnvrn->WaterMainsTemp = state.dataWeather->waterMainsTempSched->getCurrentVal();
    7124            0 :             break;
    7125        19948 :         case WaterMainsTempCalcMethod::Correlation:
    7126        19948 :             state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
    7127        19948 :                                                                             state.dataWeather->WaterMainsTempsAnnualAvgAirTemp,
    7128        19948 :                                                                             state.dataWeather->WaterMainsTempsMaxDiffAirTemp,
    7129        19948 :                                                                             state.dataWeather->WaterMainsTempsMultiplier,
    7130        19948 :                                                                             state.dataWeather->WaterMainsTempsOffset);
    7131        19948 :             break;
    7132            6 :         case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
    7133            6 :             if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
    7134            6 :                 state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
    7135            6 :                                                                                 state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp,
    7136            6 :                                                                                 state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff,
    7137            6 :                                                                                 state.dataWeather->WaterMainsTempsMultiplier,
    7138            6 :                                                                                 state.dataWeather->WaterMainsTempsOffset);
    7139              :             } else {
    7140            0 :                 state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
    7141              :             }
    7142            6 :             break;
    7143       306176 :         default:
    7144       306176 :             state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
    7145       306176 :             break;
    7146              :         }
    7147       326130 :     }
    7148              : 
    7149        19954 :     Real64 WaterMainsTempFromCorrelation(EnergyPlusData const &state,
    7150              :                                          Real64 const AnnualOAAvgDryBulbTemp,
    7151              :                                          Real64 const MonthlyOAAvgDryBulbTempMaxDiff,
    7152              :                                          Real64 const TemperatureMultiplier,
    7153              :                                          Real64 const TemperatureOffset)
    7154              :     {
    7155              : 
    7156              :         // SUBROUTINE INFORMATION:
    7157              :         //       AUTHOR         Peter Graham Ellis
    7158              :         //       DATE WRITTEN   January 2005
    7159              :         //       MODIFIED       B Nigusse June 2018 (Refactored)
    7160              : 
    7161              :         // PURPOSE OF THIS SUBROUTINE:
    7162              :         // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
    7163              : 
    7164              :         // METHODOLOGY EMPLOYED:
    7165              :         // Water mains temperature calculated by a correlation.  The correlation is fit to Fahrenheit units, so the
    7166              :         // air temperature values are first convert to F, then mains temperature is calculated and converted back to C.
    7167              :         // used for Calculated Method: 'Correlation' and 'CorrelationFromWeatherFile'.
    7168              : 
    7169              :         // REFERENCES:
    7170              :         // Correlation developed by Jay Burch and Craig Christensen at NREL, described in:
    7171              :         // Hendron, R., Anderson, R., Christensen, C., Eastment, M., and Reeves, P.  2004.  "Development of an Energy
    7172              :         // Savings Benchmark for All Residential End-Uses", Proceedings of SimBuild 2004, IBPSA-USA National Conference,
    7173              :         // Boulder, CO, August 4 - 6, 2004.
    7174              : 
    7175              :         // Annual Average Outdoor Air Temperature (F)
    7176        19954 :         Real64 const Tavg = AnnualOAAvgDryBulbTemp * (9.0 / 5.0) + 32.0;
    7177              :         // Maximum difference in monthly average outdoor air temperatures (deltaF)
    7178        19954 :         Real64 const Tdiff = MonthlyOAAvgDryBulbTempMaxDiff * (9.0 / 5.0);
    7179              : 
    7180        19954 :         Real64 const Ratio = 0.4 + 0.01 * (Tavg - 44.0);
    7181        19954 :         Real64 const Lag = 35.0 - 1.0 * (Tavg - 44.0);
    7182        19954 :         Real64 constexpr Offset = 6.0;
    7183        19954 :         int const latitude_sign = (state.dataEnvrn->Latitude >= 0) ? 1 : -1;
    7184              : 
    7185              :         // calculated water main temp (F)
    7186              :         Real64 CurrentWaterMainsTemp =
    7187        19954 :             Tavg + Offset +
    7188        19954 :             Ratio * (Tdiff / 2.0) * latitude_sign * std::sin((0.986 * (state.dataEnvrn->DayOfYear - 15.0 - Lag) - 90) * Constant::DegToRad);
    7189              : 
    7190        19954 :         if (CurrentWaterMainsTemp < 32.0) {
    7191         2166 :             CurrentWaterMainsTemp = 32.0;
    7192              :         }
    7193              : 
    7194              :         // Convert F to C
    7195        19954 :         CurrentWaterMainsTemp = (CurrentWaterMainsTemp - 32.0) * (5.0 / 9.0);
    7196              : 
    7197              :         // apply temperature multiplier and offset
    7198        19954 :         CurrentWaterMainsTemp = CurrentWaterMainsTemp * TemperatureMultiplier + TemperatureOffset;
    7199              : 
    7200        19954 :         return CurrentWaterMainsTemp;
    7201              :     }
    7202          111 :     void GetWeatherStation(EnergyPlusData &state, bool &ErrorsFound)
    7203              :     {
    7204              : 
    7205              :         // SUBROUTINE INFORMATION:
    7206              :         //       AUTHOR         Peter Graham Ellis
    7207              :         //       DATE WRITTEN   January 2006
    7208              : 
    7209              :         // PURPOSE OF THIS SUBROUTINE:
    7210              :         // Reads the input data for the WEATHER STATION object.
    7211              : 
    7212          111 :         auto const &ipsc = state.dataIPShortCut;
    7213          111 :         ipsc->cCurrentModuleObject = "Site:WeatherStation";
    7214          111 :         int const NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    7215              : 
    7216              :         // Default conditions for a weather station in an open field at a height of 10 m. (These should match the IDD defaults.)
    7217          111 :         Real64 WeatherFileWindSensorHeight = 10.0; // Height of the wind sensor at the weather station, i.e., weather file
    7218          111 :         Real64 WeatherFileWindExp = 0.14;          // Exponent for the wind velocity profile at the weather station
    7219          111 :         Real64 WeatherFileWindBLHeight = 270.0;    // Boundary layer height for the wind velocity profile at the weather station (m)
    7220          111 :         Real64 WeatherFileTempSensorHeight = 1.5;  // Height of the air temperature sensor at the weather station (m)
    7221              : 
    7222          111 :         if (NumObjects == 1) {
    7223              :             int NumAlphas;               // Number of elements in the alpha array
    7224              :             int NumNums;                 // Number of elements in the numeric array
    7225              :             int IOStat;                  // IO Status when calling get input subroutine
    7226            0 :             Array1D_string AlphArray(1); // Character string data
    7227            0 :             Array1D<Real64> NumArray(4); // Numeric data
    7228            0 :             state.dataInputProcessing->inputProcessor->getObjectItem(
    7229            0 :                 state, ipsc->cCurrentModuleObject, 1, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
    7230              : 
    7231            0 :             if (NumNums > 0) {
    7232            0 :                 WeatherFileWindSensorHeight = NumArray(1);
    7233              :             }
    7234            0 :             if (NumNums > 1) {
    7235            0 :                 WeatherFileWindExp = NumArray(2);
    7236              :             }
    7237            0 :             if (NumNums > 2) {
    7238            0 :                 WeatherFileWindBLHeight = NumArray(3);
    7239              :             }
    7240            0 :             if (NumNums > 3) {
    7241            0 :                 WeatherFileTempSensorHeight = NumArray(4);
    7242              :             }
    7243              : 
    7244          111 :         } else if (NumObjects > 1) {
    7245            0 :             ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
    7246            0 :             ErrorsFound = true;
    7247              :         }
    7248              : 
    7249          111 :         state.dataEnvrn->WeatherFileWindModCoeff = std::pow(WeatherFileWindBLHeight / WeatherFileWindSensorHeight, WeatherFileWindExp);
    7250          222 :         state.dataEnvrn->WeatherFileTempModCoeff = DataEnvironment::AtmosphericTempGradient * DataEnvironment::EarthRadius *
    7251          111 :                                                    WeatherFileTempSensorHeight / (DataEnvironment::EarthRadius + WeatherFileTempSensorHeight);
    7252              : 
    7253              :         // Write to the initialization output file
    7254          111 :         print(state.files.eio,
    7255              :               "{}\n",
    7256              :               "! <Environment:Weather Station>,Wind Sensor Height Above Ground {m},Wind Speed Profile Exponent "
    7257              :               "{},Wind Speed Profile Boundary Layer Thickness {m},Air Temperature Sensor Height Above Ground {m},Wind "
    7258              :               "Speed Modifier Coefficient-Internal,Temperature Modifier Coefficient-Internal");
    7259              : 
    7260              :         // Formats
    7261              :         static constexpr std::string_view Format_720("Environment:Weather Station,{:.3R},{:.3R},{:.3R},{:.3R},{:.3R},{:.3R}\n");
    7262          111 :         print(state.files.eio,
    7263              :               Format_720,
    7264              :               WeatherFileWindSensorHeight,
    7265              :               WeatherFileWindExp,
    7266              :               WeatherFileWindBLHeight,
    7267              :               WeatherFileTempSensorHeight,
    7268          111 :               state.dataEnvrn->WeatherFileWindModCoeff,
    7269          111 :               state.dataEnvrn->WeatherFileTempModCoeff);
    7270          111 :     }
    7271              : 
    7272       326120 :     void DayltgCurrentExtHorizIllum(EnergyPlusData &state)
    7273              :     {
    7274              : 
    7275              :         // SUBROUTINE INFORMATION:
    7276              :         //       AUTHOR         Fred Winkelmann
    7277              :         //       DATE WRITTEN   July 1997
    7278              :         //       MODIFIED       Nov98 (FW); Nov 2000 (FW)
    7279              : 
    7280              :         // PURPOSE OF THIS SUBROUTINE:
    7281              :         // CALCULATES EXTERIOR DAYLIGHT ILLUMINANCE AND LUMINOUS EFFICACY
    7282              : 
    7283              :         // METHODOLOGY EMPLOYED:
    7284              :         // CALLED by SetCurrentWeather.
    7285              :         // CALCULATES THE CURRENT-TIME-STEP
    7286              :         // ILLUMINANCE ON AN UNOBSTRUCTED HORIZONTAL SURFACE FROM THE
    7287              :         // THE SKY AND FROM DIRECT SUN.
    7288              : 
    7289              :         // REFERENCES:
    7290              :         // Based on DOE-2.1E subroutine DEXTIL.
    7291              : 
    7292              :         // SOLCOS(3), below, is the cosine of the solar zenith angle.
    7293       326120 :         if (state.dataEnvrn->SunIsUp) {
    7294              :             // Exterior horizontal beam irradiance (W/m2)
    7295       159691 :             Real64 SDIRH = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z;
    7296              :             // Exterior horizontal sky diffuse irradiance (W/m2)
    7297       159691 :             Real64 SDIFH = state.dataEnvrn->DifSolarRad;
    7298              :             // Fraction of sky covered by clouds
    7299       159691 :             state.dataEnvrn->CloudFraction = pow_2(SDIFH / (SDIRH + SDIFH + 0.0001));
    7300              :             // Luminous efficacy of sky diffuse solar and beam solar (lumens/W);
    7301              :             // Horizontal illuminance from sky and horizontal beam illuminance (lux)
    7302              :             // obtained from solar quantities on weather file and luminous efficacy.
    7303              : 
    7304       159691 :             DayltgLuminousEfficacy(state, state.dataEnvrn->PDIFLW, state.dataEnvrn->PDIRLW);
    7305       159691 :             state.dataEnvrn->HISKF = SDIFH * state.dataEnvrn->PDIFLW;
    7306       159691 :             state.dataEnvrn->HISUNF = SDIRH * state.dataEnvrn->PDIRLW;
    7307       159691 :             state.dataEnvrn->HISUNFnorm = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW;
    7308              :         } else {
    7309       166429 :             state.dataEnvrn->CloudFraction = 0.0;
    7310       166429 :             state.dataEnvrn->PDIFLW = 0.0;
    7311       166429 :             state.dataEnvrn->PDIRLW = 0.0;
    7312       166429 :             state.dataEnvrn->HISKF = 0.0;
    7313       166429 :             state.dataEnvrn->HISUNF = 0.0;
    7314       166429 :             state.dataEnvrn->HISUNFnorm = 0.0;
    7315       166429 :             state.dataEnvrn->SkyClearness = 0.0;
    7316       166429 :             state.dataEnvrn->SkyBrightness = 0.0;
    7317              :         }
    7318       326120 :     }
    7319              : 
    7320       159691 :     void DayltgLuminousEfficacy(EnergyPlusData &state,
    7321              :                                 Real64 &DiffLumEff, // Luminous efficacy of sky diffuse solar radiation (lum/W)
    7322              :                                 Real64 &DirLumEff   // Luminous efficacy of beam solar radiation (lum/W)
    7323              :     )
    7324              :     {
    7325              :         // SUBROUTINE INFORMATION:
    7326              :         //       AUTHOR         Fred Winkelmann
    7327              :         //       DATE WRITTEN   July 1997
    7328              :         //       MODIFIED       August 2009, BG fixed upper bound for sky clearness bin 7
    7329              : 
    7330              :         // PURPOSE OF THIS SUBROUTINE:
    7331              :         // Uses diffuse horizontal solar irradiance, direct normal solar
    7332              :         // irradiance, atmospheric moisture and sun position
    7333              :         // to determine the luminous efficacy in lumens/watt
    7334              :         // of sky diffuse solar radiation and direct normal solar radiation.
    7335              :         // Based on an empirical method described in
    7336              :         // R. Perez, P. Ineichen, R. Seals, J. Michalsky and R. Stewart,
    7337              :         // "Modeling daylight availability and irradiance components from direct
    7338              :         // global irradiance components from direct and global irradiance,"
    7339              :         // Solar Energy 44 (1990) 271-289.
    7340              : 
    7341              :         // Diffuse luminous efficacy coefficients
    7342              :         static constexpr std::array<Real64, 8> ADiffLumEff = {97.24, 107.22, 104.97, 102.39, 100.71, 106.42, 141.88, 152.23};
    7343              :         static constexpr std::array<Real64, 8> BDiffLumEff = {-0.46, 1.15, 2.96, 5.59, 5.94, 3.83, 1.90, 0.35};
    7344              :         static constexpr std::array<Real64, 8> CDiffLumEff = {12.00, 0.59, -5.53, -13.95, -22.75, -36.15, -53.24, -45.27};
    7345              :         static constexpr std::array<Real64, 8> DDiffLumEff = {-8.91, -3.95, -8.77, -13.90, -23.74, -28.83, -14.03, -7.98};
    7346              :         // Direct luminous efficacy coefficients
    7347              :         static constexpr std::array<Real64, 8> ADirLumEff = {57.20, 98.99, 109.83, 110.34, 106.36, 107.19, 105.75, 101.18};
    7348              :         static constexpr std::array<Real64, 8> BDirLumEff = {-4.55, -3.46, -4.90, -5.84, -3.97, -1.25, 0.77, 1.58};
    7349              :         static constexpr std::array<Real64, 8> CDirLumEff = {-2.98, -1.21, -1.71, -1.99, -1.75, -1.51, -1.26, -1.10};
    7350              :         static constexpr std::array<Real64, 8> DDirLumEff = {117.12, 12.38, -8.81, -4.56, -6.16, -26.73, -34.44, -8.29};
    7351              :         // Monthly exterrestrial direct normal illuminance (lum/m2)
    7352              :         static constexpr std::array<Real64, 12> ExtraDirNormIll = {
    7353              :             131153.0, 130613.0, 128992.0, 126816.0, 124731.0, 123240.0, 122652.0, 123120.0, 124576.0, 126658.0, 128814.0, 130471.0};
    7354              : 
    7355       159691 :         Real64 const SunZenith = std::acos(state.dataEnvrn->SOLCOS.z); // Solar zenith angle (radians)
    7356       159691 :         Real64 const SunAltitude = Constant::PiOvr2 - SunZenith;       // Solar altitude angle (radians)
    7357       159691 :         Real64 const SinSunAltitude = std::sin(SunAltitude);
    7358              :         // Clearness of sky. SkyClearness close to 1.0 corresponds to an overcast sky.
    7359              :         // SkyClearness > 6 is a clear sky.
    7360              :         // DifSolarRad is the diffuse horizontal irradiance.
    7361              :         // BeamSolarRad is the direct normal irradiance.
    7362       159691 :         Real64 const Zeta = 1.041 * pow_3(SunZenith);
    7363       319382 :         state.dataEnvrn->SkyClearness =
    7364       159691 :             ((state.dataEnvrn->DifSolarRad + state.dataEnvrn->BeamSolarRad) / (state.dataEnvrn->DifSolarRad + 0.0001) + Zeta) / (1.0 + Zeta);
    7365              :         // Relative optical air mass
    7366              :         Real64 const relAirMass =
    7367       159691 :             (1.0 - 0.1 * state.dataEnvrn->Elevation / 1000.0) / (SinSunAltitude + 0.15 / std::pow(SunAltitude / Constant::DegToRad + 3.885, 1.253));
    7368              :         // In the following, 93.73 is the extraterrestrial luminous efficacy
    7369       159691 :         state.dataEnvrn->SkyBrightness = (state.dataEnvrn->DifSolarRad * 93.73) * relAirMass / ExtraDirNormIll[state.dataEnvrn->Month - 1];
    7370              :         int ISkyClearness; // Sky clearness bin
    7371       159691 :         if (state.dataEnvrn->SkyClearness <= 1.065) {
    7372        62364 :             ISkyClearness = 0;
    7373        97327 :         } else if (state.dataEnvrn->SkyClearness <= 1.23) {
    7374         2739 :             ISkyClearness = 1;
    7375        94588 :         } else if (state.dataEnvrn->SkyClearness <= 1.50) {
    7376         3360 :             ISkyClearness = 2;
    7377        91228 :         } else if (state.dataEnvrn->SkyClearness <= 1.95) {
    7378         4314 :             ISkyClearness = 3;
    7379        86914 :         } else if (state.dataEnvrn->SkyClearness <= 2.80) {
    7380        12286 :             ISkyClearness = 4;
    7381        74628 :         } else if (state.dataEnvrn->SkyClearness <= 4.50) {
    7382        28260 :             ISkyClearness = 5;
    7383        46368 :         } else if (state.dataEnvrn->SkyClearness <= 6.20) {
    7384        17161 :             ISkyClearness = 6;
    7385              :         } else {
    7386        29207 :             ISkyClearness = 7;
    7387              :         }
    7388              : 
    7389              :         // Atmospheric moisture (cm of precipitable water)
    7390       159691 :         Real64 const AtmosMoisture = std::exp(0.07 * state.dataEnvrn->OutDewPointTemp - 0.075);
    7391              :         // Sky diffuse luminous efficacy
    7392       159691 :         if (state.dataEnvrn->SkyBrightness <= 0.0) {
    7393        51559 :             DiffLumEff = 0.0;
    7394              :         } else {
    7395       108132 :             DiffLumEff = ADiffLumEff[ISkyClearness] + BDiffLumEff[ISkyClearness] * AtmosMoisture +
    7396       108132 :                          CDiffLumEff[ISkyClearness] * state.dataEnvrn->SOLCOS.z +
    7397       108132 :                          DDiffLumEff[ISkyClearness] * std::log(state.dataEnvrn->SkyBrightness);
    7398              :         }
    7399              :         // Direct normal luminous efficacy
    7400       159691 :         if (state.dataEnvrn->SkyBrightness <= 0.0) {
    7401        51559 :             DirLumEff = 0.0;
    7402              :         } else {
    7403       108132 :             DirLumEff =
    7404       108132 :                 max(0.0,
    7405       108132 :                     ADirLumEff[ISkyClearness] + BDirLumEff[ISkyClearness] * AtmosMoisture +
    7406       108132 :                         CDirLumEff[ISkyClearness] * std::exp(5.73 * SunZenith - 5.0) + DDirLumEff[ISkyClearness] * state.dataEnvrn->SkyBrightness);
    7407              :         }
    7408       159691 :     }
    7409              : 
    7410          113 :     Real64 GetSTM(Real64 const Longitude) // Longitude from user input
    7411              :     {
    7412              :         // FUNCTION INFORMATION:
    7413              :         //       AUTHOR         Linda K. Lawrie
    7414              :         //       DATE WRITTEN   August 2003
    7415              : 
    7416              :         // PURPOSE OF THIS FUNCTION:
    7417              :         // This function determines the "standard time meridian" from the input
    7418              :         // longitude. Calculates the proper Meridian from Longitude.  This
    7419              :         // value is needed for weather calculations so that the sun comes
    7420              :         // up and goes down at the right times.
    7421              : 
    7422              :         Real64 GetSTM;
    7423              : 
    7424          113 :         Array1D<Real64> longl({-12, 12}); // Lower Longitude value for a Time Zone
    7425          113 :         Array1D<Real64> longh({-12, 12}); // Upper Longitude value for a Time Zone
    7426              : 
    7427          113 :         GetSTM = 0.0;
    7428              : 
    7429          113 :         longl(0) = -7.5;
    7430          113 :         longh(0) = 7.5;
    7431         1469 :         for (int i = 1; i <= 12; ++i) {
    7432         1356 :             longl(i) = longl(i - 1) + 15.0;
    7433         1356 :             longh(i) = longh(i - 1) + 15.0;
    7434              :         }
    7435         1469 :         for (int i = 1; i <= 12; ++i) {
    7436         1356 :             longl(-i) = longl(-i + 1) - 15.0;
    7437         1356 :             longh(-i) = longh(-i + 1) - 15.0;
    7438              :         }
    7439          113 :         Real64 temp = mod(Longitude, 360.0);
    7440          113 :         if (temp > 180.0) {
    7441            0 :             temp -= 180.0;
    7442              :         }
    7443              :         Real64 tz; // resultant tz meridian
    7444          808 :         for (int i = -12; i <= 12; ++i) {
    7445          808 :             if (temp > longl(i) && temp <= longh(i)) {
    7446          113 :                 tz = mod(i, 24.0);
    7447          113 :                 GetSTM = tz;
    7448          113 :                 break;
    7449              :             }
    7450              :         }
    7451              : 
    7452          113 :         return GetSTM;
    7453          113 :     }
    7454              : 
    7455          241 :     void ProcessEPWHeader(EnergyPlusData &state, EpwHeaderType const headerType, std::string &Line, bool &ErrorsFound)
    7456              :     {
    7457              : 
    7458              :         // SUBROUTINE INFORMATION:
    7459              :         //       AUTHOR         Linda Lawrie
    7460              :         //       DATE WRITTEN   December 1999
    7461              : 
    7462              :         // PURPOSE OF THIS SUBROUTINE:
    7463              :         // This subroutine processes each header line in the EPW weather file.
    7464              : 
    7465              :         // METHODOLOGY EMPLOYED:
    7466              :         // File is positioned to the correct line, then backspaced.  This routine
    7467              :         // reads in the line and processes as appropriate.
    7468              : 
    7469              :         Weather::DateType dateType;
    7470              :         int NumHdArgs;
    7471              : 
    7472              :         // Strip off Header value from Line
    7473          241 :         std::string::size_type Pos = index(Line, ',');
    7474          241 :         if ((Pos == std::string::npos) && !((headerType == EpwHeaderType::Comments1) || (headerType == EpwHeaderType::Comments2))) {
    7475            0 :             ShowSevereError(state, "Invalid Header line in in.epw -- no commas");
    7476            0 :             ShowContinueError(state, format("Line={}", Line));
    7477            0 :             ShowFatalError(state, "Previous conditions cause termination.");
    7478              :         }
    7479          241 :         if (Pos != std::string::npos) {
    7480          241 :             Line.erase(0, Pos + 1);
    7481              :         }
    7482              : 
    7483          241 :         switch (headerType) {
    7484           30 :         case Weather::EpwHeaderType::Location: {
    7485              : 
    7486              :             // LOCATION, A1 [City], A2 [State/Province/Region], A3 [Country],
    7487              :             // A4 [Source], N1 [WMO], N2 [Latitude],
    7488              :             // N3 [Longitude], N4 [Time Zone], N5 [Elevation {m}]
    7489              : 
    7490           30 :             NumHdArgs = 9;
    7491          300 :             for (int i = 1; i <= NumHdArgs; ++i) {
    7492          270 :                 strip(Line);
    7493          270 :                 Pos = index(Line, ',');
    7494          270 :                 if (Pos == std::string::npos) {
    7495           29 :                     if (len(Line) == 0) {
    7496            0 :                         while (Pos == std::string::npos) {
    7497            0 :                             Line = state.files.inputWeatherFile.readLine().data;
    7498            0 :                             strip(Line);
    7499            0 :                             uppercase(Line);
    7500            0 :                             Pos = index(Line, ',');
    7501              :                         }
    7502              :                     } else {
    7503           29 :                         Pos = len(Line);
    7504              :                     }
    7505              :                 }
    7506              : 
    7507          270 :                 switch (i) {
    7508           30 :                 case 1:
    7509           30 :                     state.dataWeather->EPWHeaderTitle = stripped(Line.substr(0, Pos));
    7510           30 :                     break;
    7511           90 :                 case 2:
    7512              :                 case 3:
    7513              :                 case 4:
    7514           90 :                     state.dataWeather->EPWHeaderTitle = strip(state.dataWeather->EPWHeaderTitle) + ' ' + stripped(Line.substr(0, Pos));
    7515           90 :                     break;
    7516           30 :                 case 5:
    7517           30 :                     state.dataWeather->EPWHeaderTitle += " WMO#=" + stripped(Line.substr(0, Pos));
    7518           30 :                     break;
    7519          120 :                 case 6:
    7520              :                 case 7:
    7521              :                 case 8:
    7522              :                 case 9: {
    7523              :                     bool errFlag;
    7524          120 :                     Real64 const Number = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
    7525          120 :                     if (!errFlag) {
    7526          120 :                         switch (i) {
    7527           30 :                         case 6:
    7528           30 :                             state.dataWeather->WeatherFileLatitude = Number;
    7529           30 :                             break;
    7530           30 :                         case 7:
    7531           30 :                             state.dataWeather->WeatherFileLongitude = Number;
    7532           30 :                             break;
    7533           30 :                         case 8:
    7534           30 :                             state.dataWeather->WeatherFileTimeZone = Number;
    7535           30 :                             break;
    7536           30 :                         case 9:
    7537           30 :                             state.dataWeather->WeatherFileElevation = Number;
    7538           30 :                             break;
    7539            0 :                         default:
    7540            0 :                             break;
    7541              :                         }
    7542              :                     }
    7543          120 :                 } break;
    7544            0 :                 default:
    7545            0 :                     ShowSevereError(state, format("GetEPWHeader:LOCATION, invalid numeric={}", Line.substr(0, Pos)));
    7546            0 :                     ErrorsFound = true;
    7547            0 :                     break;
    7548              :                 }
    7549          270 :                 Line.erase(0, Pos + 1);
    7550              :             }
    7551           30 :             state.dataEnvrn->WeatherFileLocationTitle = stripped(state.dataWeather->EPWHeaderTitle);
    7552           30 :         } break;
    7553           30 :         case Weather::EpwHeaderType::TypicalExtremePeriods: {
    7554           30 :             strip(Line);
    7555           30 :             Pos = index(Line, ',');
    7556           30 :             if (Pos == std::string::npos) {
    7557            0 :                 if (len(Line) == 0) {
    7558            0 :                     while (Pos == std::string::npos && len(Line) == 0) {
    7559            0 :                         Line = state.files.inputWeatherFile.readLine().data;
    7560            0 :                         strip(Line);
    7561            0 :                         Pos = index(Line, ',');
    7562              :                     }
    7563              :                 } else {
    7564            0 :                     Pos = len(Line);
    7565              :                 }
    7566              :             }
    7567              :             bool IOStatus;
    7568           30 :             state.dataWeather->NumEPWTypExtSets = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
    7569           30 :             Line.erase(0, Pos + 1);
    7570           30 :             state.dataWeather->TypicalExtremePeriods.allocate(state.dataWeather->NumEPWTypExtSets);
    7571           30 :             int TropExtremeCount = 0;
    7572          207 :             for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
    7573          177 :                 strip(Line);
    7574          177 :                 Pos = index(Line, ',');
    7575          177 :                 if (Pos != std::string::npos) {
    7576          177 :                     state.dataWeather->TypicalExtremePeriods(i).Title = Line.substr(0, Pos);
    7577          177 :                     Line.erase(0, Pos + 1);
    7578              :                 } else {
    7579            0 :                     ShowWarningError(state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={}", Line.substr(0, Pos)));
    7580            0 :                     ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
    7581            0 :                     state.dataWeather->NumEPWTypExtSets = i - 1;
    7582            0 :                     break;
    7583              :                 }
    7584          177 :                 Pos = index(Line, ',');
    7585          177 :                 if (Pos != std::string::npos) {
    7586          177 :                     state.dataWeather->TypicalExtremePeriods(i).TEType = Line.substr(0, Pos);
    7587          177 :                     Line.erase(0, Pos + 1);
    7588          177 :                     if (Util::SameString(state.dataWeather->TypicalExtremePeriods(i).TEType, "EXTREME")) {
    7589           60 :                         if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MAX")) {
    7590            1 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
    7591           59 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MIN")) {
    7592            1 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
    7593           58 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MAX")) {
    7594            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMax";
    7595           58 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MIN")) {
    7596            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMin";
    7597              :                             // to account for problems earlier in weather files:
    7598           58 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
    7599            0 :                             if (TropExtremeCount == 0) {
    7600            0 :                                 state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Max";
    7601            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
    7602            0 :                                 ++TropExtremeCount;
    7603            0 :                             } else if (TropExtremeCount == 1) {
    7604            0 :                                 state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Min";
    7605            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
    7606            0 :                                 ++TropExtremeCount;
    7607              :                             }
    7608              :                         } else { // make new short titles
    7609           58 :                             if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
    7610           29 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
    7611           29 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
    7612           29 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
    7613            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
    7614            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
    7615            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
    7616            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
    7617            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
    7618            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
    7619            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
    7620            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
    7621            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
    7622            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
    7623            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
    7624            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
    7625            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
    7626            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
    7627            0 :                             } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
    7628            0 :                                 state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
    7629              :                             }
    7630              :                         }
    7631              :                     } else { // not extreme
    7632          117 :                         if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
    7633           29 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
    7634           88 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
    7635           29 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
    7636           59 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
    7637            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
    7638           59 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
    7639            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
    7640           59 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
    7641           29 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
    7642           30 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
    7643            1 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
    7644           29 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
    7645            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
    7646           29 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
    7647            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
    7648           29 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
    7649            0 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
    7650           29 :                         } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
    7651           29 :                             state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
    7652              :                         }
    7653              :                     }
    7654              :                 } else {
    7655            0 :                     ShowWarningError(state,
    7656            0 :                                      format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={} {}",
    7657            0 :                                             state.dataWeather->TypicalExtremePeriods(i).Title,
    7658            0 :                                             Line.substr(0, Pos)));
    7659            0 :                     ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
    7660            0 :                     state.dataWeather->NumEPWTypExtSets = i - 1;
    7661            0 :                     break;
    7662              :                 }
    7663              :                 int PMonth;
    7664              :                 int PDay;
    7665              :                 int PWeekDay;
    7666          177 :                 Pos = index(Line, ',');
    7667          177 :                 if (Pos != std::string::npos) {
    7668          177 :                     std::string dateStringUC = Line.substr(0, Pos);
    7669          177 :                     dateStringUC = uppercase(dateStringUC);
    7670          177 :                     General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
    7671          177 :                     if (dateType != DateType::Invalid) {
    7672          177 :                         if (PMonth != 0 && PDay != 0) {
    7673          177 :                             state.dataWeather->TypicalExtremePeriods(i).StartMonth = PMonth;
    7674          177 :                             state.dataWeather->TypicalExtremePeriods(i).StartDay = PDay;
    7675              :                         }
    7676              :                     } else {
    7677            0 :                         ShowSevereError(
    7678            0 :                             state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
    7679            0 :                         ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
    7680            0 :                         ErrorsFound = true;
    7681              :                     }
    7682          177 :                     Line.erase(0, Pos + 1);
    7683          177 :                 }
    7684          177 :                 Pos = index(Line, ',');
    7685          177 :                 if (Pos != std::string::npos) {
    7686          148 :                     std::string dateStringUC = Line.substr(0, Pos);
    7687          148 :                     dateStringUC = uppercase(dateStringUC);
    7688          148 :                     General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
    7689          148 :                     if (dateType != DateType::Invalid) {
    7690          148 :                         if (PMonth != 0 && PDay != 0) {
    7691          148 :                             state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
    7692          148 :                             state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
    7693              :                         }
    7694              :                     } else {
    7695            0 :                         ShowSevereError(
    7696            0 :                             state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
    7697            0 :                         ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
    7698            0 :                         ErrorsFound = true;
    7699              :                     }
    7700          148 :                     Line.erase(0, Pos + 1);
    7701          148 :                 } else { // Pos=0, probably last one
    7702           29 :                     std::string const dateStringUC = uppercase(Line);
    7703           29 :                     General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
    7704           29 :                     if (dateType != DateType::Invalid) {
    7705           29 :                         if (PMonth != 0 && PDay != 0) {
    7706           29 :                             state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
    7707           29 :                             state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
    7708              :                         }
    7709              :                     } else {
    7710            0 :                         ShowSevereError(
    7711            0 :                             state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
    7712            0 :                         ErrorsFound = true;
    7713              :                     }
    7714           29 :                 }
    7715              :             }
    7716              :             // Process periods to set up other values.
    7717          207 :             for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
    7718          177 :                 auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
    7719              :                 // JulianDay (Month,Day,LeapYearValue)
    7720          177 :                 std::string const ExtremePeriodTitle = Util::makeUPPER(typicalExtPer.ShortTitle);
    7721          177 :                 if (ExtremePeriodTitle == "SUMMER") {
    7722           58 :                     if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
    7723           29 :                         typicalExtPer.MatchValue = "SummerExtreme";
    7724           29 :                         typicalExtPer.MatchValue1 = "TropicalHot";
    7725           29 :                         typicalExtPer.MatchValue2 = "NoDrySeasonMax";
    7726              :                     } else {
    7727           29 :                         typicalExtPer.MatchValue = "SummerTypical";
    7728              :                     }
    7729              : 
    7730          119 :                 } else if (ExtremePeriodTitle == "WINTER") {
    7731           58 :                     if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
    7732           29 :                         typicalExtPer.MatchValue = "WinterExtreme";
    7733           29 :                         typicalExtPer.MatchValue1 = "TropicalCold";
    7734           29 :                         typicalExtPer.MatchValue2 = "NoDrySeasonMin";
    7735              :                     } else {
    7736           29 :                         typicalExtPer.MatchValue = "WinterTypical";
    7737              :                     }
    7738              : 
    7739           61 :                 } else if (ExtremePeriodTitle == "AUTUMN") {
    7740           29 :                     typicalExtPer.MatchValue = "AutumnTypical";
    7741              : 
    7742           32 :                 } else if (ExtremePeriodTitle == "SPRING") {
    7743           29 :                     typicalExtPer.MatchValue = "SpringTypical";
    7744              : 
    7745            3 :                 } else if (ExtremePeriodTitle == "WETSEASON") {
    7746            0 :                     typicalExtPer.MatchValue = "WetSeason";
    7747              : 
    7748            3 :                 } else if (ExtremePeriodTitle == "DRYSEASON") {
    7749            0 :                     typicalExtPer.MatchValue = "DrySeason";
    7750              : 
    7751            3 :                 } else if (ExtremePeriodTitle == "NOWETSEASON") {
    7752            0 :                     typicalExtPer.MatchValue = "NoWetSeason";
    7753              : 
    7754            3 :                 } else if (ExtremePeriodTitle == "NODRYSEASON") {
    7755            1 :                     typicalExtPer.MatchValue = "NoDrySeason";
    7756              : 
    7757            2 :                 } else if ((ExtremePeriodTitle == "NODRYSEASONMAX") || (ExtremePeriodTitle == "NOWETSEASONMAX")) {
    7758            1 :                     typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
    7759            1 :                     typicalExtPer.MatchValue1 = "TropicalHot";
    7760            1 :                     typicalExtPer.MatchValue2 = "SummerExtreme";
    7761              : 
    7762            1 :                 } else if ((ExtremePeriodTitle == "NODRYSEASONMIN") || (ExtremePeriodTitle == "NOWETSEASONMIN")) {
    7763            1 :                     typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
    7764            1 :                     typicalExtPer.MatchValue1 = "TropicalCold";
    7765            1 :                     typicalExtPer.MatchValue2 = "WinterExtreme";
    7766              : 
    7767            0 :                 } else if (ExtremePeriodTitle == "TROPICALHOT") {
    7768            0 :                     typicalExtPer.MatchValue = "TropicalHot";
    7769            0 :                     typicalExtPer.MatchValue1 = "SummerExtreme";
    7770            0 :                     typicalExtPer.MatchValue2 = "NoDrySeasonMax";
    7771              : 
    7772            0 :                 } else if (ExtremePeriodTitle == "TROPICALCOLD") {
    7773            0 :                     typicalExtPer.MatchValue = "TropicalCold";
    7774            0 :                     typicalExtPer.MatchValue1 = "WinterExtreme";
    7775            0 :                     typicalExtPer.MatchValue2 = "NoDrySeasonMin";
    7776              : 
    7777              :                 } else {
    7778            0 :                     typicalExtPer.MatchValue = "Invalid - no match";
    7779              :                 }
    7780          177 :                 typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, 0);
    7781          177 :                 typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, 0);
    7782          177 :                 if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
    7783          176 :                     typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
    7784              :                 } else {
    7785            1 :                     typicalExtPer.TotalDays =
    7786            1 :                         General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
    7787              :                 }
    7788          177 :             }
    7789           30 :         } break;
    7790           30 :         case Weather::EpwHeaderType::GroundTemperatures: {
    7791              :             // Added for ground surfaces defined with F or c factor method. TH 7/2009
    7792              :             // Assume the 0.5 m set of ground temperatures
    7793              :             // or first set on a weather file, if any.
    7794           30 :             Pos = index(Line, ',');
    7795           30 :             if (Pos != std::string::npos) {
    7796              :                 bool errFlag;
    7797           30 :                 int NumGrndTemps = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
    7798           30 :                 if (!errFlag && NumGrndTemps >= 1) {
    7799           30 :                     Line.erase(0, Pos + 1);
    7800              :                     // skip depth, soil conductivity, soil density, soil specific heat
    7801          150 :                     for (int i = 1; i <= 4; ++i) {
    7802          120 :                         Pos = index(Line, ',');
    7803          120 :                         if (Pos == std::string::npos) {
    7804            0 :                             Line.clear();
    7805            0 :                             break;
    7806              :                         }
    7807          120 :                         Line.erase(0, Pos + 1);
    7808              :                     }
    7809           30 :                     state.dataWeather->GroundTempsFCFromEPWHeader = 0.0;
    7810           30 :                     int actcount = 0;
    7811          390 :                     for (int i = 1; i <= 12; ++i) { // take the first set of ground temperatures.
    7812          360 :                         Pos = index(Line, ',');
    7813          360 :                         if (Pos != std::string::npos) {
    7814          360 :                             state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
    7815          360 :                             ++actcount;
    7816              :                         } else {
    7817            0 :                             if (len(Line) > 0) {
    7818            0 :                                 state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
    7819            0 :                                 ++actcount;
    7820              :                             }
    7821            0 :                             break;
    7822              :                         }
    7823          360 :                         Line.erase(0, Pos + 1);
    7824              :                     }
    7825           30 :                     if (actcount == 12) {
    7826           30 :                         state.dataWeather->wthFCGroundTemps = true;
    7827              :                     }
    7828              :                 }
    7829              :             }
    7830           30 :         } break;
    7831           31 :         case Weather::EpwHeaderType::HolidaysDST: {
    7832              :             // A1, \field LeapYear Observed
    7833              :             // \type choice
    7834              :             // \key Yes
    7835              :             // \key No
    7836              :             // \note Yes if Leap Year will be observed for this file
    7837              :             // \note No if Leap Year days (29 Feb) should be ignored in this file
    7838              :             // A2, \field Daylight Saving Start Day
    7839              :             // A3, \field Daylight Saving End Day
    7840              :             // N1, \field Number of Holidays
    7841              :             // A4, \field Holiday 1 Name
    7842              :             // A5, \field Holiday 1 Day
    7843              :             // etc.
    7844              :             // Start with Minimum number of NumHdArgs
    7845           31 :             uppercase(Line);
    7846           31 :             NumHdArgs = 4;
    7847           31 :             int CurCount = 0;
    7848          155 :             for (int i = 1; i <= NumHdArgs; ++i) {
    7849          124 :                 strip(Line);
    7850          124 :                 Pos = index(Line, ',');
    7851          124 :                 if (Pos == std::string::npos) {
    7852           30 :                     if (len(Line) == 0) {
    7853            0 :                         while (Pos == std::string::npos) {
    7854            0 :                             Line = state.files.inputWeatherFile.readLine().data;
    7855            0 :                             strip(Line);
    7856            0 :                             uppercase(Line);
    7857            0 :                             Pos = index(Line, ',');
    7858              :                         }
    7859              :                     } else {
    7860           30 :                         Pos = len(Line);
    7861              :                     }
    7862              :                 }
    7863              : 
    7864              :                 int PMonth;
    7865              :                 int PDay;
    7866              :                 int PWeekDay;
    7867              :                 bool IOStatus;
    7868          124 :                 if (i == 1) {
    7869           31 :                     state.dataWeather->WFAllowsLeapYears = (Line[0] == 'Y');
    7870           93 :                 } else if (i == 2) {
    7871              :                     // In this section, we call ProcessDateString, and if that fails, we can recover from it
    7872              :                     // by setting DST to false, so we don't affect ErrorsFound
    7873              : 
    7874              :                     // call ProcessDateString with local bool (unused)
    7875              :                     bool errflag1;
    7876           31 :                     General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, errflag1);
    7877           31 :                     if (dateType != DateType::Invalid) {
    7878              :                         // ErrorsFound is still false after ProcessDateString
    7879           31 :                         if (PMonth == 0 && PDay == 0) {
    7880           30 :                             state.dataWeather->EPWDaylightSaving = false;
    7881              :                         } else {
    7882            1 :                             state.dataWeather->EPWDaylightSaving = true;
    7883            1 :                             state.dataWeather->EPWDST.StDateType = dateType;
    7884            1 :                             state.dataWeather->EPWDST.StMon = PMonth;
    7885            1 :                             state.dataWeather->EPWDST.StDay = PDay;
    7886            1 :                             state.dataWeather->EPWDST.StWeekDay = PWeekDay;
    7887              :                         }
    7888              :                     } else {
    7889              :                         // ErrorsFound is untouched
    7890            0 :                         ShowContinueError(
    7891            0 :                             state, format("ProcessEPWHeader: Invalid Daylight Saving Period Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
    7892            0 :                         ShowContinueError(state, format("...invalid header={}", epwHeaders[static_cast<int>(headerType)]));
    7893            0 :                         ShowContinueError(state, "...Setting Weather File DST to false.");
    7894            0 :                         state.dataWeather->EPWDaylightSaving = false;
    7895              :                     }
    7896              : 
    7897           62 :                 } else if (i == 3) {
    7898           31 :                     General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
    7899           31 :                     if (state.dataWeather->EPWDaylightSaving) {
    7900            1 :                         if (dateType != DateType::Invalid) {
    7901            1 :                             state.dataWeather->EPWDST.EnDateType = dateType;
    7902            1 :                             state.dataWeather->EPWDST.EnMon = PMonth;
    7903            1 :                             state.dataWeather->EPWDST.EnDay = PDay;
    7904            1 :                             state.dataWeather->EPWDST.EnWeekDay = PWeekDay;
    7905              :                         } else {
    7906            0 :                             ShowWarningError(
    7907              :                                 state,
    7908            0 :                                 format("ProcessEPWHeader: Invalid Daylight Saving Period End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
    7909            0 :                             ShowContinueError(state, "...Setting Weather File DST to false.");
    7910            0 :                             state.dataWeather->EPWDaylightSaving = false;
    7911              :                         }
    7912            1 :                         state.dataWeather->DST = state.dataWeather->EPWDST;
    7913              :                     }
    7914              : 
    7915           31 :                 } else if (i == 4) {
    7916           31 :                     int NumEPWHolidays = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
    7917           62 :                     state.dataWeather->NumSpecialDays =
    7918           31 :                         NumEPWHolidays + state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriodControl:SpecialDays");
    7919           31 :                     state.dataWeather->SpecialDays.allocate(state.dataWeather->NumSpecialDays);
    7920           31 :                     NumHdArgs = 4 + NumEPWHolidays * 2;
    7921              : 
    7922            0 :                 } else if ((i >= 5)) {
    7923            0 :                     if (mod(i, 2) != 0) {
    7924            0 :                         ++CurCount;
    7925            0 :                         if (CurCount > state.dataWeather->NumSpecialDays) {
    7926            0 :                             ShowSevereError(state, "Too many SpecialDays");
    7927            0 :                             ErrorsFound = true;
    7928              :                         } else {
    7929            0 :                             state.dataWeather->SpecialDays(CurCount).Name = Line.substr(0, Pos);
    7930              :                         }
    7931              :                         // Process name
    7932              :                     } else {
    7933            0 :                         if (CurCount <= state.dataWeather->NumSpecialDays) {
    7934            0 :                             auto &specialDay = state.dataWeather->SpecialDays(CurCount);
    7935              :                             // Process date
    7936            0 :                             General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
    7937            0 :                             if (dateType == DateType::MonthDay) {
    7938            0 :                                 specialDay.dateType = dateType;
    7939            0 :                                 specialDay.Month = PMonth;
    7940            0 :                                 specialDay.Day = PDay;
    7941            0 :                                 specialDay.WeekDay = 0;
    7942            0 :                                 specialDay.CompDate = PMonth * 32 + PDay;
    7943            0 :                                 specialDay.Duration = 1;
    7944            0 :                                 specialDay.DayType = 1;
    7945            0 :                                 specialDay.WthrFile = true;
    7946            0 :                             } else if (dateType != DateType::Invalid) {
    7947            0 :                                 specialDay.dateType = dateType;
    7948            0 :                                 specialDay.Month = PMonth;
    7949            0 :                                 specialDay.Day = PDay;
    7950            0 :                                 specialDay.WeekDay = PWeekDay;
    7951            0 :                                 specialDay.CompDate = 0;
    7952            0 :                                 specialDay.Duration = 1;
    7953            0 :                                 specialDay.DayType = 1;
    7954            0 :                                 specialDay.WthrFile = true;
    7955              :                             } else {
    7956            0 :                                 ShowSevereError(state, format("Invalid SpecialDay Date Field(WeatherFile)={}", Line.substr(0, Pos)));
    7957            0 :                                 ErrorsFound = true;
    7958              :                             }
    7959              :                         }
    7960              :                     }
    7961              :                 }
    7962          124 :                 Line.erase(0, Pos + 1);
    7963              :             }
    7964          211 :             for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
    7965              :                 // General::OrdinalDay (Month,Day,LeapYearValue)
    7966          180 :                 auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
    7967          180 :                 typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, state.dataWeather->LeapYearAdd);
    7968          180 :                 typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, state.dataWeather->LeapYearAdd);
    7969          180 :                 if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
    7970          179 :                     typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
    7971              :                 } else {
    7972            1 :                     typicalExtPer.TotalDays =
    7973            1 :                         General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
    7974              :                 }
    7975              :             }
    7976           31 :         } break;
    7977           90 :         case Weather::EpwHeaderType::Comments1:
    7978              :         case Weather::EpwHeaderType::Comments2:
    7979              :         case Weather::EpwHeaderType::DesignConditions: {
    7980              :             // no action
    7981           90 :         } break;
    7982           30 :         case Weather::EpwHeaderType::DataPeriods: {
    7983              :             //     N1, \field Number of Data Periods
    7984              :             //     N2, \field Number of Records per hour
    7985              :             //     A1, \field Data Period 1 Name/Description
    7986              :             //     A2, \field Data Period 1 Start Day of Week
    7987              :             //       \type choice
    7988              :             //       \key  Sunday
    7989              :             //       \key  Monday
    7990              :             //       \key  Tuesday
    7991              :             //       \key  Wednesday
    7992              :             //       \key  Thursday
    7993              :             //       \key  Friday
    7994              :             //       \key  Saturday
    7995              :             //     A3, \field Data Period 1 Start Day
    7996              :             //     A4, \field Data Period 1 End Day
    7997           30 :             uppercase(Line);
    7998           30 :             NumHdArgs = 2;
    7999           30 :             int CurCount = 0;
    8000          214 :             for (int i = 1; i <= NumHdArgs; ++i) {
    8001          184 :                 strip(Line);
    8002          184 :                 Pos = index(Line, ',');
    8003          184 :                 if (Pos == std::string::npos) {
    8004           29 :                     if (len(Line) == 0) {
    8005            0 :                         while (Pos == std::string::npos) {
    8006            0 :                             Line = state.files.inputWeatherFile.readLine().data;
    8007            0 :                             strip(Line);
    8008            0 :                             uppercase(Line);
    8009            0 :                             Pos = index(Line, ',');
    8010              :                         }
    8011              :                     } else {
    8012           29 :                         Pos = len(Line);
    8013              :                     }
    8014              :                 }
    8015              : 
    8016              :                 bool IOStatus;
    8017          184 :                 if (i == 1) {
    8018           30 :                     state.dataWeather->NumDataPeriods = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
    8019           30 :                     state.dataWeather->DataPeriods.allocate(state.dataWeather->NumDataPeriods);
    8020           30 :                     NumHdArgs += 4 * state.dataWeather->NumDataPeriods;
    8021           30 :                     if (state.dataWeather->NumDataPeriods > 0) {
    8022           61 :                         for (auto &e : state.dataWeather->DataPeriods) {
    8023           31 :                             e.NumDays = 0;
    8024           30 :                         }
    8025              :                     }
    8026              : 
    8027          154 :                 } else if (i == 2) {
    8028           30 :                     state.dataWeather->NumIntervalsPerHour = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
    8029          124 :                 } else if (i >= 3) {
    8030          124 :                     int const CurOne = mod(i - 3, 4);
    8031              :                     int PMonth;
    8032              :                     int PDay;
    8033              :                     int PWeekDay;
    8034              :                     int PYear;
    8035          124 :                     if (CurOne == 0) {
    8036              :                         // Description of Data Period
    8037           31 :                         ++CurCount;
    8038           31 :                         if (CurCount > state.dataWeather->NumDataPeriods) {
    8039            0 :                             ShowSevereError(state, "Too many data periods");
    8040            0 :                             ErrorsFound = true;
    8041              :                         } else {
    8042           31 :                             state.dataWeather->DataPeriods(CurCount).Name = Line.substr(0, Pos);
    8043              :                         }
    8044              : 
    8045           93 :                     } else if (CurOne == 1) {
    8046              :                         // Start Day of Week
    8047           31 :                         if (CurCount <= state.dataWeather->NumDataPeriods) {
    8048           31 :                             auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
    8049           31 :                             dataPeriod.DayOfWeek = Line.substr(0, Pos);
    8050           31 :                             dataPeriod.WeekDay = getEnumValue(Sched::dayTypeNamesUC, dataPeriod.DayOfWeek);
    8051           31 :                             if (dataPeriod.WeekDay < 1 || dataPeriod.WeekDay > 7) {
    8052            0 :                                 ShowSevereError(state,
    8053            0 :                                                 fmt::format("Weather File -- Invalid Start Day of Week for Data Period #{}, Invalid day={}",
    8054              :                                                             CurCount,
    8055            0 :                                                             dataPeriod.DayOfWeek));
    8056            0 :                                 ErrorsFound = true;
    8057              :                             }
    8058              :                         }
    8059              : 
    8060           62 :                     } else if (CurOne == 2) {
    8061              :                         // DataPeriod Start Day
    8062           31 :                         if (CurCount <= state.dataWeather->NumDataPeriods) {
    8063           31 :                             auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
    8064           31 :                             General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
    8065           31 :                             if (dateType == DateType::MonthDay) {
    8066           31 :                                 dataPeriod.StMon = PMonth;
    8067           31 :                                 dataPeriod.StDay = PDay;
    8068           31 :                                 dataPeriod.StYear = PYear;
    8069           31 :                                 if (PYear != 0) {
    8070            1 :                                     dataPeriod.HasYearData = true;
    8071              :                                 }
    8072              :                             } else {
    8073            0 :                                 ShowSevereError(state,
    8074            0 :                                                 format("Data Periods must be of the form <DayOfYear> or <Month Day> (WeatherFile), found={}",
    8075            0 :                                                        Line.substr(0, Pos)));
    8076            0 :                                 ErrorsFound = true;
    8077              :                             }
    8078              :                         }
    8079              : 
    8080           31 :                     } else if (CurOne == 3) {
    8081           31 :                         auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
    8082           31 :                         if (CurCount <= state.dataWeather->NumDataPeriods) {
    8083           31 :                             General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
    8084           31 :                             if (dateType == DateType::MonthDay) {
    8085           31 :                                 dataPeriod.EnMon = PMonth;
    8086           31 :                                 dataPeriod.EnDay = PDay;
    8087           31 :                                 dataPeriod.EnYear = PYear;
    8088           31 :                                 if (PYear == 0 && dataPeriod.HasYearData) {
    8089            0 :                                     ShowWarningError(state, "Data Period (WeatherFile) - Start Date contains year. End Date does not.");
    8090            0 :                                     ShowContinueError(state, "...Assuming same year as Start Date for this data.");
    8091            0 :                                     dataPeriod.EnYear = dataPeriod.StYear;
    8092              :                                 }
    8093              :                             } else {
    8094            0 :                                 ShowSevereError(state,
    8095            0 :                                                 format("Data Periods must be of the form <DayOfYear> or <Month Day>, (WeatherFile) found={}",
    8096            0 :                                                        Line.substr(0, Pos)));
    8097            0 :                                 ErrorsFound = true;
    8098              :                             }
    8099              :                         }
    8100           31 :                         if (dataPeriod.StYear == 0 || dataPeriod.EnYear == 0) {
    8101           30 :                             dataPeriod.DataStJDay = General::OrdinalDay(dataPeriod.StMon, dataPeriod.StDay, state.dataWeather->LeapYearAdd);
    8102           30 :                             dataPeriod.DataEnJDay = General::OrdinalDay(dataPeriod.EnMon, dataPeriod.EnDay, state.dataWeather->LeapYearAdd);
    8103           30 :                             if (dataPeriod.DataStJDay <= dataPeriod.DataEnJDay) {
    8104           30 :                                 dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
    8105              :                             } else {
    8106            0 :                                 dataPeriod.NumDays = (365 - dataPeriod.DataStJDay + 1) + (dataPeriod.DataEnJDay - 1 + 1);
    8107              :                             }
    8108              :                         } else { // weather file has actual year(s)
    8109            1 :                             dataPeriod.DataStJDay = computeJulianDate(dataPeriod.StYear, dataPeriod.StMon, dataPeriod.StDay);
    8110            1 :                             dataPeriod.DataEnJDay = computeJulianDate(dataPeriod.EnYear, dataPeriod.EnMon, dataPeriod.EnDay);
    8111            1 :                             dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
    8112              :                         }
    8113              :                         // Have processed the last item for this, can set up Weekdays for months
    8114           31 :                         dataPeriod.MonWeekDay = 0;
    8115           31 :                         if (!ErrorsFound) {
    8116           93 :                             SetupWeekDaysByMonth(state,
    8117           31 :                                                  state.dataWeather->DataPeriods(CurCount).StMon,
    8118           31 :                                                  state.dataWeather->DataPeriods(CurCount).StDay,
    8119           31 :                                                  state.dataWeather->DataPeriods(CurCount).WeekDay,
    8120           31 :                                                  state.dataWeather->DataPeriods(CurCount).MonWeekDay);
    8121              :                         }
    8122              :                     }
    8123              :                 }
    8124          184 :                 Line.erase(0, Pos + 1);
    8125              :             }
    8126           30 :         } break;
    8127            0 :         default: {
    8128              :             // Invalid header type
    8129            0 :             assert(false);
    8130              :         } break;
    8131              :         }
    8132          241 :     }
    8133              : 
    8134           67 :     void SkipEPlusWFHeader(EnergyPlusData &state)
    8135              :     {
    8136              : 
    8137              :         // SUBROUTINE INFORMATION:
    8138              :         //       AUTHOR         Linda K. Lawrie
    8139              :         //       DATE WRITTEN   August 2000
    8140              : 
    8141              :         // PURPOSE OF THIS SUBROUTINE:
    8142              :         // This subroutine skips the initial header records on the EnergyPlus Weather File (in.epw).
    8143              : 
    8144              :         static constexpr std::string_view Header("DATA PERIODS");
    8145              : 
    8146              :         // Read in Header Information
    8147          134 :         InputFile::ReadResult<std::string> Line{"", true, false};
    8148              : 
    8149              :         // Headers should come in order
    8150              :         while (true) {
    8151          536 :             Line = state.files.inputWeatherFile.readLine();
    8152          536 :             if (Line.eof) {
    8153            0 :                 ShowFatalError(state,
    8154            0 :                                format("Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}", Header),
    8155            0 :                                OptionalOutputFileRef{state.files.eso});
    8156              :             }
    8157          536 :             uppercase(Line.data);
    8158          536 :             if (has(Line.data, Header)) {
    8159           67 :                 break;
    8160              :             }
    8161              :         }
    8162              : 
    8163              :         // Dummy process Data Periods line
    8164              :         //  'DATA PERIODS'
    8165              :         //     N1, \field Number of Data Periods
    8166              :         //     N2, \field Number of Records per hour
    8167              :         //     A1, \field Data Period 1 Name/Description
    8168              :         //     A2, \field Data Period 1 Start Day of Week
    8169              :         //       \type choice
    8170              :         //       \key  Sunday
    8171              :         //       \key  Monday
    8172              :         //       \key  Tuesday
    8173              :         //       \key  Wednesday
    8174              :         //       \key  Thursday
    8175              :         //       \key  Friday
    8176              :         //       \key  Saturday
    8177              :         //     A3, \field Data Period 1 Start Day
    8178              :         //     A4, \field Data Period 1 End Day
    8179              : 
    8180           67 :         int NumHdArgs = 2;
    8181           67 :         int CurCount = 0;
    8182          201 :         for (int i = 1; i <= NumHdArgs; ++i) {
    8183          134 :             strip(Line.data);
    8184          134 :             std::string::size_type Pos = index(Line.data, ',');
    8185          134 :             if (Pos == std::string::npos) {
    8186            0 :                 if (len(Line.data) == 0) {
    8187            0 :                     while (Pos == std::string::npos) {
    8188            0 :                         Line = state.files.inputWeatherFile.readLine();
    8189            0 :                         strip(Line.data);
    8190            0 :                         uppercase(Line.data);
    8191            0 :                         Pos = index(Line.data, ',');
    8192              :                     }
    8193              :                 } else {
    8194            0 :                     Pos = len(Line.data);
    8195              :                 }
    8196              :             }
    8197              : 
    8198          134 :             if (i == 1) {
    8199              :                 bool IOStatus;
    8200           67 :                 int const NumPeriods = Util::ProcessNumber(Line.data.substr(0, Pos), IOStatus);
    8201           67 :                 NumHdArgs += 4 * NumPeriods;
    8202           67 :             } else if ((i >= 3)) {
    8203            0 :                 if (mod(i - 3, 4) == 0) {
    8204            0 :                     ++CurCount;
    8205              :                 }
    8206              :             }
    8207          134 :             Line.data.erase(0, Pos + 1);
    8208              :         }
    8209           67 :     }
    8210              : 
    8211           22 :     void ReportMissing_RangeData(EnergyPlusData &state)
    8212              :     {
    8213              : 
    8214              :         // SUBROUTINE INFORMATION:
    8215              :         //       AUTHOR         Linda Lawrie
    8216              :         //       DATE WRITTEN   January 2002
    8217              : 
    8218              :         // PURPOSE OF THIS SUBROUTINE:
    8219              :         // This subroutine reports the counts of missing/out of range data
    8220              :         // for weather file environments.
    8221              : 
    8222              :         static constexpr std::string_view MissString("Missing Data Found on Weather Data File");
    8223              :         static constexpr std::string_view msFmt("Missing {}, Number of items={:5}");
    8224              :         static constexpr std::string_view InvString("Invalid Data Found on Weather Data File");
    8225              :         static constexpr std::string_view ivFmt("Invalid {}, Number of items={:5}");
    8226              :         static constexpr std::string_view RangeString("Out of Range Data Found on Weather Data File");
    8227              :         static constexpr std::string_view rgFmt("Out of Range {} [{},{}], Number of items={:5}");
    8228              : 
    8229           22 :         if (!state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
    8230           22 :             return;
    8231              :         }
    8232              : 
    8233            0 :         bool MissedHeader = false;
    8234            0 :         auto missedHeaderCheck = [&](Real64 const value, std::string const &description) {
    8235            0 :             if (value > 0) {
    8236            0 :                 if (!MissedHeader) {
    8237            0 :                     ShowWarningError(state, std::string{MissString});
    8238            0 :                     MissedHeader = true;
    8239              :                 }
    8240            0 :                 ShowMessage(state, format(msFmt, "\"" + description + "\"", value));
    8241              :             }
    8242            0 :         };
    8243              : 
    8244            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDryBulbTemp, "Dry Bulb Temperature");
    8245            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutBaroPress, "Atmospheric Pressure");
    8246            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutRelHum, "Relative Humidity");
    8247            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDewPointTemp, "Dew Point Temperatures");
    8248            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindSpeed, "Wind Speed");
    8249            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindDir, "Wind Direction");
    8250            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.BeamSolarRad, "Direct Radiation");
    8251            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.DifSolarRad, "Diffuse Radiation");
    8252            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.TotalSkyCover, "Total Sky Cover");
    8253            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OpaqueSkyCover, "Opaque Sky Cover");
    8254            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.SnowDepth, "Snow Depth");
    8255            0 :         if (state.dataWeather->wvarsMissedCounts.WeathCodes > 0) {
    8256            0 :             ShowWarningError(state, std::string{InvString});
    8257            0 :             ShowMessage(state, format(ivFmt, "\"Weather Codes\" (not equal 9 digits)", state.dataWeather->wvarsMissedCounts.WeathCodes));
    8258              :         }
    8259            0 :         missedHeaderCheck(state.dataWeather->wvarsMissedCounts.LiquidPrecip, "Liquid Precipitation Depth");
    8260              : 
    8261            0 :         bool OutOfRangeHeader = false;
    8262              :         auto outOfRangeHeaderCheck = // (AUTO_OK_LAMBDA)
    8263            0 :             [&](Real64 const value, std::string_view description, std::string_view rangeLow, std::string_view rangeHigh, std::string_view extraMsg) {
    8264            0 :                 if (value > 0) {
    8265            0 :                     if (!OutOfRangeHeader) {
    8266            0 :                         ShowWarningError(state, std::string{RangeString});
    8267            0 :                         OutOfRangeHeader = true;
    8268              :                     }
    8269            0 :                     ShowMessage(state, EnergyPlus::format(rgFmt, description, rangeLow, rangeHigh, value));
    8270            0 :                     if (!extraMsg.empty()) {
    8271            0 :                         ShowMessage(state, std::string{extraMsg});
    8272              :                     }
    8273              :                 }
    8274            0 :             };
    8275            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp, "Dry Bulb Temperatures", ">=-90", "<=70", "");
    8276            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress,
    8277              :                               "Atmospheric Pressure",
    8278              :                               ">31000",
    8279              :                               "<=120000",
    8280              :                               "Out of Range values set to last good value");
    8281            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutRelHum, "Relative Humidity", ">=0", "<=110", "");
    8282            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp, "Dew Point Temperatures", ">=-90", "<=70", "");
    8283            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindSpeed, "Wind Speed", ">=0", "<=40", "");
    8284            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindDir, "Wind Direction", ">=0", "<=360", "");
    8285            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad, "Direct Radiation", ">=0", "NoLimit", "");
    8286            0 :         outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad, "Diffuse Radiation", ">=0", "NoLimit", "");
    8287              :     }
    8288              : 
    8289          112 :     void SetupInterpolationValues(EnergyPlusData &state)
    8290              :     {
    8291              : 
    8292              :         // SUBROUTINE INFORMATION:
    8293              :         //       AUTHOR         Linda Lawrie
    8294              :         //       DATE WRITTEN   November 2002
    8295              : 
    8296              :         // PURPOSE OF THIS SUBROUTINE:
    8297              :         // This subroutine creates the "interpolation" values / weights that are used for
    8298              :         // interpolating weather data from hourly down to the time step level.
    8299              : 
    8300              :         // METHODOLOGY EMPLOYED:
    8301              :         // Create arrays (InterpolationValues, SolarInterpolationValues) dependent on
    8302              :         // Number of Time Steps in Hour.  This will be used in the "SetCurrentWeather" procedure.
    8303              : 
    8304          112 :         state.dataWeather->Interpolation.allocate(state.dataGlobal->TimeStepsInHour);
    8305          112 :         state.dataWeather->SolarInterpolation.allocate(state.dataGlobal->TimeStepsInHour);
    8306          112 :         state.dataWeather->Interpolation = 0.0;
    8307          112 :         state.dataWeather->SolarInterpolation = 0.0;
    8308              : 
    8309          716 :         for (int tloop = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop) {
    8310          604 :             state.dataWeather->Interpolation(tloop) =
    8311          604 :                 (state.dataGlobal->TimeStepsInHour == 1) ? 1.0 : min(1.0, (double(tloop) / double(state.dataGlobal->TimeStepsInHour)));
    8312              :         }
    8313              : 
    8314          112 :         if (mod(state.dataGlobal->TimeStepsInHour, 2) == 0) {
    8315              :             // even number of time steps.
    8316          102 :             int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
    8317              : 
    8318          102 :             state.dataWeather->SolarInterpolation(halfpoint) = 1.0;
    8319          102 :             Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
    8320          399 :             for (int tloop = halfpoint + 1, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
    8321          297 :                 state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
    8322              :             }
    8323          297 :             for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
    8324          195 :                 state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
    8325              :             }
    8326              :         } else { // odd number of time steps
    8327           10 :             if (state.dataGlobal->TimeStepsInHour == 1) {
    8328           10 :                 state.dataWeather->SolarInterpolation(1) = 0.5;
    8329            0 :             } else if (state.dataGlobal->TimeStepsInHour == 3) {
    8330            0 :                 state.dataWeather->SolarInterpolation(1) = 5.0 / 6.0;
    8331            0 :                 state.dataWeather->SolarInterpolation(2) = 5.0 / 6.0;
    8332            0 :                 state.dataWeather->SolarInterpolation(3) = 0.5;
    8333              :             } else {
    8334            0 :                 Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
    8335            0 :                 int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
    8336            0 :                 Real64 tweight1 = 1.0 - tweight / 2.0;
    8337            0 :                 state.dataWeather->SolarInterpolation(halfpoint) = tweight1;
    8338            0 :                 state.dataWeather->SolarInterpolation(halfpoint + 1) = tweight1;
    8339            0 :                 for (int tloop = halfpoint + 2, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
    8340            0 :                     state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
    8341              :                 }
    8342            0 :                 for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
    8343            0 :                     state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
    8344              :                 }
    8345              :             }
    8346              :         }
    8347          112 :     }
    8348              : 
    8349          114 :     void SetupEnvironmentTypes(EnergyPlusData &state)
    8350              :     {
    8351              : 
    8352              :         // SUBROUTINE INFORMATION:
    8353              :         //       AUTHOR         Linda Lawrie
    8354              :         //       DATE WRITTEN   October 2010
    8355              : 
    8356              :         // PURPOSE OF THIS SUBROUTINE:
    8357              :         // Make sure Environment derived type is set prior to getting
    8358              :         // Weather Properties
    8359              : 
    8360              :         // Transfer weather file information to the Environment derived type
    8361          114 :         state.dataWeather->Envrn = state.dataEnvrn->TotDesDays + 1;
    8362              : 
    8363              :         // Sizing Periods from Weather File
    8364          114 :         for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunDesPers; ++iRunPer, ++state.dataWeather->Envrn) {
    8365            0 :             auto const &runPer = state.dataWeather->RunPeriodDesignInput(iRunPer);
    8366            0 :             auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
    8367              : 
    8368            0 :             envCurr.StartMonth = runPer.startMonth;
    8369            0 :             envCurr.StartDay = runPer.startDay;
    8370            0 :             envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, state.dataWeather->LeapYearAdd);
    8371            0 :             envCurr.TotalDays = runPer.totalDays;
    8372            0 :             envCurr.EndMonth = runPer.endMonth;
    8373            0 :             envCurr.EndDay = runPer.endDay;
    8374            0 :             envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, state.dataWeather->LeapYearAdd);
    8375            0 :             envCurr.NumSimYears = runPer.numSimYears;
    8376            0 :             if (envCurr.StartJDay <= envCurr.EndJDay) {
    8377            0 :                 envCurr.TotalDays = (envCurr.EndJDay - envCurr.StartJDay + 1) * envCurr.NumSimYears;
    8378              :             } else {
    8379            0 :                 envCurr.TotalDays =
    8380            0 :                     (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - envCurr.StartJDay + 1 + envCurr.EndJDay) * envCurr.NumSimYears;
    8381              :             }
    8382            0 :             state.dataEnvrn->TotRunDesPersDays += envCurr.TotalDays;
    8383            0 :             envCurr.UseDST = runPer.useDST;
    8384            0 :             envCurr.UseHolidays = runPer.useHolidays;
    8385            0 :             envCurr.Title = runPer.title;
    8386            0 :             envCurr.cKindOfEnvrn = runPer.periodType;
    8387            0 :             envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
    8388            0 :             envCurr.DesignDayNum = 0;
    8389            0 :             envCurr.RunPeriodDesignNum = iRunPer;
    8390            0 :             envCurr.DayOfWeek = runPer.dayOfWeek;
    8391            0 :             envCurr.MonWeekDay = runPer.monWeekDay;
    8392            0 :             envCurr.SetWeekDays = false;
    8393            0 :             envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
    8394            0 :             envCurr.UseRain = runPer.useRain;
    8395            0 :             envCurr.UseSnow = runPer.useSnow;
    8396            0 :             envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // this will just the default
    8397              :         }
    8398              : 
    8399              :         // RunPeriods from weather file
    8400          172 :         for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunPers; ++iRunPer, ++state.dataWeather->Envrn) {
    8401           58 :             auto const &runPer = state.dataWeather->RunPeriodInput(iRunPer);
    8402           58 :             auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
    8403              : 
    8404           58 :             envCurr.StartMonth = runPer.startMonth;
    8405           58 :             envCurr.StartDay = runPer.startDay;
    8406           58 :             envCurr.StartYear = runPer.startYear;
    8407           58 :             envCurr.EndMonth = runPer.endMonth;
    8408           58 :             envCurr.EndDay = runPer.endDay;
    8409           58 :             envCurr.EndYear = runPer.endYear;
    8410           58 :             envCurr.NumSimYears = runPer.numSimYears;
    8411           58 :             envCurr.CurrentYear = runPer.startYear;
    8412           58 :             envCurr.IsLeapYear = runPer.isLeapYear;
    8413           58 :             envCurr.TreatYearsAsConsecutive = true;
    8414           58 :             if (runPer.actualWeather) {
    8415              :                 // This will require leap years to be present, thus Julian days can be used for all the calculations
    8416            0 :                 envCurr.StartJDay = envCurr.StartDate = runPer.startJulianDate;
    8417            0 :                 envCurr.EndJDay = envCurr.EndDate = runPer.endJulianDate;
    8418            0 :                 envCurr.TotalDays = envCurr.EndDate - envCurr.StartDate + 1;
    8419            0 :                 envCurr.RawSimDays = envCurr.EndDate - envCurr.StartDate + 1;
    8420            0 :                 envCurr.MatchYear = true;
    8421            0 :                 envCurr.ActualWeather = true;
    8422              :             } else { // std RunPeriod
    8423           58 :                 envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
    8424           58 :                 if (envCurr.StartYear == envCurr.EndYear) {
    8425              :                     // Short-circuit all the calculations, we're in a single year
    8426              : 
    8427           58 :                     envCurr.IsLeapYear = isLeapYear(envCurr.StartYear) && state.dataWeather->WFAllowsLeapYears;
    8428           58 :                     int LocalLeapYearAdd = (int)envCurr.IsLeapYear;
    8429              : 
    8430           58 :                     envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, LocalLeapYearAdd);
    8431           58 :                     envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, LocalLeapYearAdd);
    8432           58 :                     envCurr.RawSimDays = (envCurr.EndJDay - envCurr.StartJDay + 1);
    8433           58 :                     envCurr.TotalDays = envCurr.RawSimDays;
    8434              :                 } else {
    8435              :                     // Environment crosses year boundaries
    8436            0 :                     envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
    8437            0 :                     envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, (int)runPer.isLeapYear);
    8438            0 :                     envCurr.EndJDay = General::OrdinalDay(
    8439            0 :                         runPer.endMonth, runPer.endDay, (int)(isLeapYear(runPer.endYear) && state.dataWeather->WFAllowsLeapYears));
    8440            0 :                     envCurr.TotalDays = 366 - envCurr.StartJDay + envCurr.EndJDay + 365 * std::max(envCurr.NumSimYears - 2, 0);
    8441            0 :                     if (state.dataWeather->WFAllowsLeapYears) {
    8442              :                         // First year
    8443            0 :                         if (envCurr.StartJDay < 59) {
    8444            0 :                             if (isLeapYear(envCurr.StartYear)) {
    8445            0 :                                 ++envCurr.TotalDays;
    8446              :                             }
    8447              :                         }
    8448              :                         // Middle years
    8449            0 :                         for (int yr = envCurr.StartYear + 1; yr < envCurr.EndYear; ++yr) {
    8450            0 :                             if (isLeapYear(yr)) {
    8451            0 :                                 ++envCurr.TotalDays;
    8452              :                             }
    8453              :                         }
    8454              :                         // Last year not needed, the end ordinal date will take this into account
    8455              :                     }
    8456            0 :                     envCurr.RawSimDays = envCurr.TotalDays;
    8457              :                 }
    8458              :             }
    8459           58 :             envCurr.UseDST = runPer.useDST;
    8460           58 :             envCurr.UseHolidays = runPer.useHolidays;
    8461           58 :             if (runPer.title.empty()) {
    8462            2 :                 envCurr.Title = state.dataEnvrn->WeatherFileLocationTitle;
    8463              :             } else {
    8464           56 :                 envCurr.Title = runPer.title;
    8465              :             }
    8466           58 :             if (envCurr.KindOfEnvrn == Constant::KindOfSim::ReadAllWeatherData) {
    8467            2 :                 envCurr.cKindOfEnvrn = "ReadAllWeatherDataRunPeriod";
    8468              :             } else {
    8469           56 :                 envCurr.cKindOfEnvrn = "WeatherFileRunPeriod";
    8470           56 :                 envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
    8471              :             }
    8472           58 :             envCurr.DayOfWeek = runPer.dayOfWeek;
    8473           58 :             envCurr.MonWeekDay = runPer.monWeekDay;
    8474           58 :             envCurr.SetWeekDays = false;
    8475           58 :             envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
    8476           58 :             envCurr.UseRain = runPer.useRain;
    8477           58 :             envCurr.UseSnow = runPer.useSnow;
    8478           58 :             envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // first hour interpolation choice
    8479              :         } // for (i)
    8480          114 :     }
    8481              : 
    8482          124 :     bool isLeapYear(int const Year)
    8483              :     {
    8484              :         // true if it's a leap year, false if not.
    8485              : 
    8486          124 :         if (mod(Year, 4) == 0) { // Potential Leap Year
    8487            5 :             if (!(mod(Year, 100) == 0 && mod(Year, 400) != 0)) {
    8488            5 :                 return true;
    8489              :             }
    8490              :         }
    8491          119 :         return false;
    8492              :     }
    8493              : 
    8494          320 :     int computeJulianDate(int const gyyyy, // input/output gregorian year, should be specified as 4 digits
    8495              :                           int const gmm,   // input/output gregorian month
    8496              :                           int const gdd    // input/output gregorian day
    8497              :     )
    8498              :     {
    8499              :         // SUBROUTINE INFORMATION:
    8500              :         //       AUTHOR         Jason DeGraw
    8501              :         //       DATE WRITTEN   10/25/2017
    8502              : 
    8503              :         // PURPOSE OF THIS SUBROUTINE:
    8504              :         // Split the former JGDate function in two. Convert a gregorian
    8505              :         // date to actual julian date.  the advantage of storing a julian date
    8506              :         // in the jdate format rather than a 5 digit format is that any
    8507              :         // number of days can be add or subtracted to jdate and
    8508              :         // that result is a proper julian date.
    8509              : 
    8510              :         // REFERENCES:
    8511              :         // for discussion of this algorithm,
    8512              :         // see cacm, vol 11, no 10, oct 1968, page 657
    8513              : 
    8514          320 :         int tyyyy = gyyyy;
    8515          320 :         int tmm = gmm;
    8516          320 :         int tdd = gdd;
    8517          320 :         int l = (tmm - 14) / 12;
    8518          320 :         return tdd - 32075 + 1461 * (tyyyy + 4800 + l) / 4 + 367 * (tmm - 2 - l * 12) / 12 - 3 * ((tyyyy + 4900 + l) / 100) / 4;
    8519              :     }
    8520              : 
    8521            4 :     int computeJulianDate(GregorianDate const &gdate)
    8522              :     {
    8523            4 :         return computeJulianDate(gdate.year, gdate.month, gdate.day);
    8524              :     }
    8525              : 
    8526            4 :     GregorianDate computeGregorianDate(int const jdate)
    8527              :     {
    8528            4 :         int tdate = jdate;
    8529            4 :         int l = tdate + 68569;
    8530            4 :         int n = 4 * l / 146097;
    8531            4 :         l -= (146097 * n + 3) / 4;
    8532            4 :         int tyyyy = 4000 * (l + 1) / 1461001;
    8533            4 :         l = l - 1461 * tyyyy / 4 + 31;
    8534            4 :         int tmm = 80 * l / 2447;
    8535            4 :         int tdd = l - 2447 * tmm / 80;
    8536            4 :         l = tmm / 11;
    8537            4 :         tmm += 2 - 12 * l;
    8538            4 :         tyyyy += 100 * (n - 49) + l;
    8539            4 :         return {tyyyy, tmm, tdd};
    8540              :     }
    8541              : 
    8542            6 :     Sched::DayType calculateDayOfWeek(EnergyPlusData &state, int const year, int const month, int const day)
    8543              :     {
    8544              : 
    8545              :         // FUNCTION INFORMATION:
    8546              :         //       AUTHOR         Linda Lawrie
    8547              :         //       DATE WRITTEN   March 2012
    8548              :         //       MODIFIED       October 2017, Jason DeGraw
    8549              : 
    8550              :         // PURPOSE OF THIS FUNCTION:
    8551              :         // Calculate the correct day of week.
    8552              : 
    8553              :         // METHODOLOGY EMPLOYED:
    8554              :         // Zeller's algorithm.
    8555              : 
    8556              :         // REFERENCES:
    8557              :         // http://en.wikipedia.org/wiki/Zeller%27s_congruence
    8558              :         // and other references around the web.
    8559              : 
    8560            6 :         int Gyyyy(year); // Gregorian yyyy
    8561            6 :         int Gmm(month);  // Gregorian mm
    8562              : 
    8563              :         // Jan, Feb are 13, 14 months of previous year
    8564            6 :         if (Gmm < 3) {
    8565            6 :             Gmm += 12;
    8566            6 :             --Gyyyy;
    8567              :         }
    8568              : 
    8569            6 :         state.dataEnvrn->DayOfWeek = mod(day + (13 * (Gmm + 1) / 5) + Gyyyy + (Gyyyy / 4) + 6 * (Gyyyy / 100) + (Gyyyy / 400), 7);
    8570            6 :         if (state.dataEnvrn->DayOfWeek == 0) {
    8571            1 :             state.dataEnvrn->DayOfWeek = 7;
    8572              :         }
    8573              : 
    8574            6 :         return static_cast<Sched::DayType>(state.dataEnvrn->DayOfWeek);
    8575              :     }
    8576              : 
    8577           57 :     int calculateDayOfYear(int const Month, int const Day, bool const leapYear)
    8578              :     {
    8579              : 
    8580              :         // FUNCTION INFORMATION:
    8581              :         //       AUTHOR         Jason DeGraw
    8582              :         //       DATE WRITTEN   October 10, 2017
    8583              : 
    8584              :         // PURPOSE OF THIS FUNCTION:
    8585              :         // Compute the day of the year for leap and non-leap years.
    8586              : 
    8587              :         static std::array<int, 12> const daysbefore{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}};
    8588              :         static std::array<int, 12> const daysbeforeleap{{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
    8589              : 
    8590              :         // Could probably do some bounds checking here, but for now assume the month is in [1, 12]
    8591           57 :         if (leapYear) {
    8592            2 :             return daysbeforeleap[Month - 1] + Day;
    8593              :         } else {
    8594           55 :             return daysbefore[Month - 1] + Day;
    8595              :         }
    8596              :     }
    8597              : 
    8598          124 :     bool validMonthDay(int const month, int const day, int const leapYearAdd)
    8599              :     {
    8600              : 
    8601              :         // FUNCTION INFORMATION:
    8602              :         //       AUTHOR         Jason DeGraw
    8603              :         //       DATE WRITTEN   October 31, 2017
    8604              : 
    8605              :         // PURPOSE OF THIS FUNCTION:
    8606              :         // Determine if a month/day+leapyear combination is valid.
    8607              : 
    8608          124 :         switch (month) {
    8609          123 :         case 1:
    8610              :         case 3:
    8611              :         case 5:
    8612              :         case 7:
    8613              :         case 8:
    8614              :         case 10:
    8615              :         case 12:
    8616          123 :             if (day > 31) {
    8617            0 :                 return false;
    8618              :             }
    8619          123 :             break;
    8620            0 :         case 4:
    8621              :         case 6:
    8622              :         case 9:
    8623              :         case 11:
    8624            0 :             if (day > 30) {
    8625            0 :                 return false;
    8626              :             }
    8627            0 :             break;
    8628            1 :         case 2:
    8629            1 :             if (day > 28 + leapYearAdd) {
    8630            0 :                 return false;
    8631              :             }
    8632            1 :             break;
    8633            0 :         default:
    8634            0 :             return false;
    8635              :         }
    8636          124 :         return true;
    8637              :     }
    8638              : 
    8639            3 :     void AnnualMonthlyDryBulbWeatherData::CalcAnnualAndMonthlyDryBulbTemp(EnergyPlusData &state)
    8640              :     {
    8641              : 
    8642              :         // PURPOSE OF THIS SUBROUTINE:
    8643              :         // Calculates monthly daily average outdoor air drybulb temperature from
    8644              :         // either weather (*.EPW) file or reads monthly daily average outdoor air
    8645              :         // drybulb temperature from STAT (*.stat) for use to autosize main water
    8646              :         // temperature.
    8647              : 
    8648            3 :         Real64 MonthlyDailyDryBulbMin(200.0);               // monthly-daily minimum outside air dry-bulb temperature
    8649            3 :         Real64 MonthlyDailyDryBulbMax(-200.0);              // monthly-daily maximum outside air dry-bulb temperature
    8650            3 :         Real64 AnnualDailyAverageDryBulbTempSum(0.0);       // annual sum of daily average outside air dry-bulb temperature
    8651            3 :         Array1D<Real64> MonthlyAverageDryBulbTemp(12, 0.0); // monthly-daily average outside air temperature
    8652              : 
    8653            3 :         if (!this->OADryBulbWeatherDataProcessed) {
    8654            3 :             const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
    8655            3 :             const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
    8656            3 :             if (statFileExists) {
    8657            2 :                 auto statFile = state.files.inStatFilePath.try_open();
    8658            2 :                 if (!statFile.good()) {
    8659            0 :                     ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", statFile.filePath));
    8660            0 :                     ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
    8661            0 :                     return;
    8662              :                 }
    8663              : 
    8664            2 :                 std::string lineAvg;
    8665          136 :                 while (statFile.good()) {
    8666          135 :                     auto lineIn = statFile.readLine();
    8667          135 :                     if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
    8668            8 :                         for (int i = 1; i <= 7; ++i) {
    8669            7 :                             lineIn = statFile.readLine();
    8670              :                         }
    8671            1 :                         lineIn = statFile.readLine();
    8672            1 :                         lineAvg = lineIn.data;
    8673            1 :                         break;
    8674              :                     }
    8675          135 :                 }
    8676            2 :                 if (lineAvg.empty()) {
    8677            2 :                     ShowSevereError(
    8678              :                         state,
    8679            2 :                         format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have Monthly Statistics for Dry Bulb temperatures.",
    8680              :                                statFile.filePath));
    8681            2 :                     ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
    8682            1 :                     return;
    8683            1 :                 } else if (lineAvg.find("Daily Avg") == std::string::npos) {
    8684            0 :                     ShowSevereError(state,
    8685            0 :                                     format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have the 'Daily Avg' line in the Monthly "
    8686              :                                            "Statistics for Dry Bulb temperatures.",
    8687              :                                            statFile.filePath));
    8688            0 :                     ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
    8689            0 :                     return;
    8690              :                 } else {
    8691            1 :                     int AnnualNumberOfDays = 0;
    8692           13 :                     for (int i = 1; i <= 12; ++i) {
    8693           12 :                         MonthlyAverageDryBulbTemp(i) = OutputReportTabular::StrToReal(OutputReportTabular::GetColumnUsingTabs(lineAvg, i + 2));
    8694           12 :                         AnnualDailyAverageDryBulbTempSum += MonthlyAverageDryBulbTemp(i) * state.dataWeather->EndDayOfMonth(i);
    8695           12 :                         MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyAverageDryBulbTemp(i));
    8696           12 :                         MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyAverageDryBulbTemp(i));
    8697           12 :                         AnnualNumberOfDays += state.dataWeather->EndDayOfMonth(i);
    8698              :                     }
    8699            1 :                     this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
    8700            1 :                     this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
    8701            1 :                     this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
    8702            1 :                     this->OADryBulbWeatherDataProcessed = true;
    8703              :                 }
    8704            4 :             } else if (epwFileExists) {
    8705            1 :                 auto epwFile = state.files.inputWeatherFilePath.try_open();
    8706            1 :                 bool epwHasLeapYear(false);
    8707            1 :                 if (!epwFile.good()) {
    8708            0 :                     ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", epwFile.filePath));
    8709            0 :                     ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
    8710            0 :                     return;
    8711              :                 }
    8712            9 :                 for (int i = 1; i <= 8; ++i) { // Headers
    8713            8 :                     auto epwLine = epwFile.readLine();
    8714              : 
    8715            8 :                     if (i == 5) {
    8716              :                         // HOLIDAYS/DAYLIGHT SAVINGS,Yes,0,0,0
    8717            1 :                         std::string::size_type pos = index(epwLine.data, ',');
    8718            1 :                         epwLine.data.erase(0, pos + 1);
    8719            1 :                         pos = index(epwLine.data, ',');
    8720            1 :                         std::string LeapYear = Util::makeUPPER(epwLine.data.substr(0, pos));
    8721            1 :                         if (LeapYear[0] == 'Y') {
    8722            0 :                             epwHasLeapYear = true;
    8723              :                         }
    8724            1 :                     }
    8725            8 :                 }
    8726            1 :                 Array1D<int> EndDayOfMonthLocal;
    8727            1 :                 EndDayOfMonthLocal = state.dataWeather->EndDayOfMonth;
    8728            1 :                 if (epwHasLeapYear) {
    8729              :                     // increase number of days for february by one day if weather data has leap year
    8730            0 :                     EndDayOfMonthLocal(2) = EndDayOfMonthLocal(2) + 1;
    8731              :                 }
    8732           13 :                 for (int i = 1; i <= 12; ++i) {
    8733           12 :                     Real64 MonthlyDailyDryBulbAvg = 0.0;
    8734           12 :                     int DaysCountOfMonth = EndDayOfMonthLocal(i);
    8735          377 :                     for (int DayNum = 1; DayNum <= DaysCountOfMonth; ++DayNum) {
    8736          365 :                         Real64 DailyAverageDryBulbTemp = 0.0;
    8737              :                         std::string::size_type pos;
    8738         9125 :                         for (int j = 1; j <= 24; ++j) {
    8739         8760 :                             auto epwLine = epwFile.readLine();
    8740        61320 :                             for (int ind = 1; ind <= 6; ++ind) {
    8741        52560 :                                 pos = index(epwLine.data, ',');
    8742        52560 :                                 epwLine.data.erase(0, pos + 1);
    8743              :                             }
    8744         8760 :                             pos = index(epwLine.data, ',');
    8745         8760 :                             Real64 HourlyDryBulbTemp = OutputReportTabular::StrToReal(epwLine.data.substr(0, pos));
    8746         8760 :                             DailyAverageDryBulbTemp += (HourlyDryBulbTemp / 24.0);
    8747         8760 :                         }
    8748          365 :                         AnnualDailyAverageDryBulbTempSum += DailyAverageDryBulbTemp;
    8749          365 :                         MonthlyDailyDryBulbAvg += (DailyAverageDryBulbTemp / DaysCountOfMonth);
    8750              :                     }
    8751           12 :                     MonthlyAverageDryBulbTemp(i) = MonthlyDailyDryBulbAvg;
    8752           12 :                     MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyDailyDryBulbAvg);
    8753           12 :                     MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyDailyDryBulbAvg);
    8754              :                 }
    8755              :                 // calculate annual average outdoor air dry-bulb temperature and monthly daily average
    8756              :                 // outdoor air temperature maximum difference
    8757            1 :                 int AnnualNumberOfDays = 365;
    8758            1 :                 if (epwHasLeapYear) {
    8759            0 :                     AnnualNumberOfDays++;
    8760              :                 }
    8761            1 :                 this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
    8762            1 :                 this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
    8763            1 :                 this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
    8764            1 :                 this->OADryBulbWeatherDataProcessed = true;
    8765            1 :             } else {
    8766            0 :                 ShowSevereError(state, "CalcAnnualAndMonthlyDryBulbTemp: weather file or stat file does not exist.");
    8767            0 :                 ShowContinueError(state, format("Weather file: {}.", state.files.inputWeatherFilePath.filePath));
    8768            0 :                 ShowContinueError(state, format("Stat file: {}.", state.files.inStatFilePath.filePath));
    8769            0 :                 ShowContinueError(state, "Water Mains Monthly Temperature cannot be calculated using CorrelationFromWeatherFile method.");
    8770            0 :                 ShowContinueError(state, "Instead a fixed default value of 10.0 C will be used.");
    8771              :             }
    8772              :         }
    8773            3 :     }
    8774              : 
    8775          104 :     void ReportWaterMainsTempParameters(EnergyPlusData &state)
    8776              :     {
    8777              :         // PURPOSE OF THIS SUBROUTINE:
    8778              :         // report site water mains temperature object user inputs and/or parameters calculated
    8779              :         // from weather or stat file
    8780              : 
    8781          104 :         if (!state.files.eio.good()) {
    8782            0 :             return;
    8783              :         }
    8784              : 
    8785          104 :         std::stringstream ss;
    8786          104 :         auto *eiostream = &ss;
    8787              : 
    8788              :         // Write annual average OA temperature and maximum difference in monthly-daily average outdoor air temperature
    8789              :         *eiostream << "! <Site Water Mains Temperature Information>"
    8790              :                       ",Calculation Method{}"
    8791              :                       ",Water Mains Temperature Schedule Name{}"
    8792              :                       ",Annual Average Outdoor Air Temperature{C}"
    8793              :                       ",Maximum Difference In Monthly Average Outdoor Air Temperatures{deltaC}"
    8794          104 :                       ",Fixed Default Water Mains Temperature{C}\n";
    8795              : 
    8796          104 :         switch (state.dataWeather->WaterMainsTempsMethod) {
    8797            0 :         case WaterMainsTempCalcMethod::Schedule:
    8798            0 :             *eiostream << "Site Water Mains Temperature Information,";
    8799            0 :             *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
    8800            0 :                        << state.dataWeather->waterMainsTempSched->Name << ",";
    8801            0 :             *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
    8802            0 :                        << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
    8803            0 :             *eiostream << "NA\n";
    8804            0 :             break;
    8805            6 :         case WaterMainsTempCalcMethod::Correlation:
    8806            6 :             *eiostream << "Site Water Mains Temperature Information,";
    8807            6 :             *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << "," << "NA" << ",";
    8808            6 :             *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
    8809           12 :                        << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
    8810            6 :             *eiostream << "NA\n";
    8811            6 :             break;
    8812            1 :         case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
    8813            1 :             if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
    8814            1 :                 *eiostream << "Site Water Mains Temperature Information,";
    8815            1 :                 *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << "," << "NA" << ",";
    8816            2 :                 *eiostream << format("{:.2R}", state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp) << ","
    8817            2 :                            << format("{:.2R}", state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff) << "," << "NA\n";
    8818              :             } else {
    8819            0 :                 *eiostream << "Site Water Mains Temperature Information,";
    8820            0 :                 *eiostream << "FixedDefault" << "," << "NA" << "," << "NA" << "," << "NA" << "," << format("{:.1R}", 10.0) << '\n';
    8821              :             }
    8822            1 :             break;
    8823           97 :         default:
    8824           97 :             *eiostream << "Site Water Mains Temperature Information,";
    8825           97 :             *eiostream << "FixedDefault" << "," << "NA" << "," << "NA" << "," << "NA" << "," << format("{:.1R}", 10.0) << '\n';
    8826           97 :             break;
    8827              :         }
    8828              : 
    8829          104 :         print(state.files.eio, "{}", ss.str());
    8830          104 :     }
    8831              : 
    8832        90504 :     void calcSky(EnergyPlusData &state,
    8833              :                  Real64 &HorizIRSky,
    8834              :                  Real64 &SkyTemp,
    8835              :                  Real64 OpaqueSkyCover,
    8836              :                  Real64 DryBulb,
    8837              :                  Real64 DewPoint,
    8838              :                  Real64 RelHum,
    8839              :                  Real64 IRHoriz)
    8840              :     {
    8841        90504 :         if (IRHoriz <= 0.0) {
    8842            0 :             IRHoriz = 9999.0;
    8843              :         }
    8844              : 
    8845        90504 :         auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
    8846        90504 :         if (!envCurr.UseWeatherFileHorizontalIR || IRHoriz >= 9999.0) {
    8847              :             // Missing or user defined to not use IRHoriz from weather, using sky cover and clear sky emissivity
    8848          384 :             Real64 ESky = CalcSkyEmissivity(state, envCurr.skyTempModel, OpaqueSkyCover, DryBulb, DewPoint, RelHum);
    8849          384 :             HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
    8850          384 :             if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
    8851          384 :                 envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
    8852          384 :                 SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
    8853              :             } else {
    8854            0 :                 SkyTemp = 0.0; // dealt with later
    8855              :             }
    8856          384 :         } else {
    8857              :             // Valid IR from weather files
    8858        90120 :             HorizIRSky = IRHoriz;
    8859        90120 :             if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
    8860        90120 :                 envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
    8861        90120 :                 SkyTemp = root_4(IRHoriz / Constant::StefanBoltzmann) - Constant::Kelvin;
    8862              :             } else {
    8863            0 :                 SkyTemp = 0.0; // dealt with later
    8864              :             }
    8865              :         }
    8866        90504 :     }
    8867              : 
    8868              : } // namespace Weather
    8869              : 
    8870              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1