Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <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/Fmath.hh>
58 : #include <ObjexxFCL/string.functions.hh>
59 : #include <ObjexxFCL/time.hh>
60 :
61 : // EnergyPlus Headers
62 : #include <EnergyPlus/Data/EnergyPlusData.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataHeatBalance.hh>
65 : #include <EnergyPlus/DataIPShortCuts.hh>
66 : #include <EnergyPlus/DataPrecisionGlobals.hh>
67 : #include <EnergyPlus/DataReportingFlags.hh>
68 : #include <EnergyPlus/DataSurfaces.hh>
69 : #include <EnergyPlus/DataSystemVariables.hh>
70 : #include <EnergyPlus/DataWater.hh>
71 : #include <EnergyPlus/DisplayRoutines.hh>
72 : #include <EnergyPlus/EMSManager.hh>
73 : #include <EnergyPlus/FileSystem.hh>
74 : #include <EnergyPlus/General.hh>
75 : #include <EnergyPlus/GlobalNames.hh>
76 : #include <EnergyPlus/GroundTemperatureModeling/GroundTemperatureModelManager.hh>
77 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
78 : #include <EnergyPlus/OutputProcessor.hh>
79 : #include <EnergyPlus/OutputReportPredefined.hh>
80 : #include <EnergyPlus/OutputReportTabular.hh>
81 : #include <EnergyPlus/Psychrometrics.hh>
82 : #include <EnergyPlus/ScheduleManager.hh>
83 : #include <EnergyPlus/StringUtilities.hh>
84 : #include <EnergyPlus/SurfaceGeometry.hh>
85 : #include <EnergyPlus/ThermalComfort.hh>
86 : #include <EnergyPlus/UtilityRoutines.hh>
87 : #include <EnergyPlus/Vectors.hh>
88 : #include <EnergyPlus/WaterManager.hh>
89 : #include <EnergyPlus/WeatherManager.hh>
90 :
91 : namespace EnergyPlus {
92 :
93 : namespace Weather {
94 :
95 : // MODULE INFORMATION:
96 : // AUTHOR Rick Strand
97 : // DATE WRITTEN May 1997
98 : // MODIFIED December 1998, FW; December 1999, LKL.
99 :
100 : // PURPOSE OF THIS MODULE:
101 : // This module contains all of the weather handling routines for
102 : // EnergyPlus. That includes getting user input, defining design day
103 : // weather, retrieving data from weather files, and supplying the
104 : // outdoor environment for each time step.
105 :
106 : constexpr std::array<std::string_view, (int)EpwHeaderType::Num> epwHeaders = {"LOCATION",
107 : "DESIGN CONDITIONS",
108 : "TYPICAL/EXTREME PERIODS",
109 : "GROUND TEMPERATURES",
110 : "HOLIDAYS/DAYLIGHT SAVING",
111 : "COMMENTS 1",
112 : "COMMENTS 2",
113 : "DATA PERIODS"};
114 :
115 : static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNames{
116 : "Schedule", "Correlation", "CorrelationFromWeatherFile", "FixedDefault"};
117 :
118 : static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNamesUC{
119 : "SCHEDULE", "CORRELATION", "CORRELATIONFROMWEATHERFILE", "FIXEDDEFAULT"};
120 :
121 : static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNamesUC{
122 : "CLARKALLEN", "SCHEDULEVALUE", "DIFFERENCESCHEDULEDRYBULBVALUE", "DIFFERENCESCHEDULEDEWPOINTVALUE", "BRUNT", "IDSO", "BERDAHLMARTIN"};
123 :
124 : static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNames{"Clark and Allen",
125 : "Schedule Value",
126 : "DryBulb Difference Schedule Value",
127 : "Dewpoint Difference Schedule Value",
128 : "Brunt",
129 : "Idso",
130 : "Berdahl and Martin"};
131 :
132 2900842 : void ManageWeather(EnergyPlusData &state)
133 : {
134 :
135 : // SUBROUTINE INFORMATION:
136 : // AUTHOR Rick Strand
137 : // DATE WRITTEN May 1997
138 : // MODIFIED June 1997 (general clean-up)
139 :
140 : // PURPOSE OF THIS SUBROUTINE:
141 : // This subroutine is the main driver of the weather manager module.
142 : // It controls the assignment of weather related global variables as
143 : // well as the reads and writes for weather information.
144 :
145 2900842 : InitializeWeather(state, state.dataWeather->PrintEnvrnStamp);
146 :
147 2900842 : bool anyEMSRan = false;
148 : // Cannot call this during sizing, because EMS will not initialize properly until after simulation kickoff
149 2900842 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
150 1976977 : EMSManager::ManageEMS(state,
151 : EMSManager::EMSCallFrom::BeginZoneTimestepBeforeSetCurrentWeather,
152 : anyEMSRan,
153 3953954 : ObjexxFCL::Optional_int_const()); // calling point
154 : }
155 2900842 : SetCurrentWeather(state);
156 :
157 2900842 : ReportWeatherAndTimeInformation(state, state.dataWeather->PrintEnvrnStamp);
158 2900842 : }
159 :
160 2834 : void ResetEnvironmentCounter(EnergyPlusData &state)
161 : {
162 2834 : state.dataWeather->Envrn = 0;
163 2834 : }
164 :
165 794 : bool CheckIfAnyUnderwaterBoundaries(EnergyPlusData &state)
166 : {
167 794 : bool errorsFound = false;
168 794 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
169 :
170 794 : constexpr std::string_view routineName = "CheckIfAnyUnderwaterBoundaries";
171 :
172 794 : auto const &ipsc = state.dataIPShortCut;
173 794 : ipsc->cCurrentModuleObject = "SurfaceProperty:Underwater";
174 794 : int Num = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
175 795 : for (int i = 1; i <= Num; i++) {
176 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
177 1 : ipsc->cCurrentModuleObject,
178 : i,
179 1 : ipsc->cAlphaArgs,
180 : NumAlpha,
181 1 : ipsc->rNumericArgs,
182 : NumNumber,
183 : IOStat,
184 1 : ipsc->lNumericFieldBlanks,
185 1 : ipsc->lAlphaFieldBlanks,
186 1 : ipsc->cAlphaFieldNames,
187 1 : ipsc->cNumericFieldNames);
188 1 : state.dataWeather->underwaterBoundaries.emplace_back();
189 1 : auto &underwaterBoundary = state.dataWeather->underwaterBoundaries[i - 1];
190 1 : underwaterBoundary.Name = ipsc->cAlphaArgs(1);
191 :
192 1 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, underwaterBoundary.Name};
193 :
194 1 : underwaterBoundary.distanceFromLeadingEdge = ipsc->rNumericArgs(1);
195 1 : underwaterBoundary.OSCMIndex = Util::FindItemInList(underwaterBoundary.Name, state.dataSurface->OSCM);
196 1 : if (underwaterBoundary.OSCMIndex <= 0) {
197 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
198 0 : errorsFound = true;
199 : }
200 1 : underwaterBoundary.WaterTempScheduleIndex = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(2));
201 1 : if (underwaterBoundary.WaterTempScheduleIndex == 0) {
202 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
203 0 : errorsFound = true;
204 : }
205 :
206 1 : if (ipsc->lAlphaFieldBlanks(3)) {
207 : // that's OK, we can have a blank schedule, the water will just have no free stream velocity
208 0 : underwaterBoundary.VelocityScheduleIndex = 0;
209 1 : } else if ((underwaterBoundary.VelocityScheduleIndex = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(3))) == 0) {
210 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
211 0 : errorsFound = true;
212 : }
213 1 : if (errorsFound) break;
214 : }
215 794 : if (errorsFound) {
216 0 : ShowFatalError(state, "Previous input problems cause program termination");
217 : }
218 794 : return (Num > 0);
219 : }
220 :
221 : Real64
222 1343 : calculateWaterBoundaryConvectionCoefficient(Real64 const curWaterTemp, Real64 const freeStreamVelocity, Real64 const distanceFromLeadingEdge)
223 : {
224 1343 : Real64 constexpr waterKinematicViscosity = 1e-6; // m2/s
225 1343 : Real64 constexpr waterPrandtlNumber = 6; // -
226 1343 : Real64 constexpr waterThermalConductivity = 0.6; // W/mK
227 : // do some calculation for forced convection from the leading edge of the ship
228 1343 : Real64 const localReynoldsNumber = freeStreamVelocity * distanceFromLeadingEdge / waterKinematicViscosity;
229 1343 : Real64 const localNusseltNumber = 0.0296 * pow(localReynoldsNumber, 0.8) * pow(waterPrandtlNumber, 1.0 / 3.0);
230 1343 : Real64 const localConvectionCoeff = localNusseltNumber * waterThermalConductivity / distanceFromLeadingEdge;
231 :
232 : // do some calculations for natural convection from the bottom of the ship
233 1343 : Real64 constexpr distanceFromBottomOfHull = 12; // meters, assumed for now
234 : // this Prandtl correction is from Incropera & Dewitt, Intro to HT, eq 9.20
235 1343 : Real64 const prandtlCorrection =
236 : (0.75 * pow(waterPrandtlNumber, 0.5)) / pow(0.609 + 1.221 * pow(waterPrandtlNumber, 0.5) + 1.238 * waterPrandtlNumber, 0.25);
237 : // calculate the Grashof number
238 1343 : Real64 constexpr gravity = 9.81; // m/s2
239 1343 : Real64 constexpr beta = 0.000214; // water thermal expansion coefficient, from engineeringtoolbox.com, 1/C
240 1343 : Real64 constexpr assumedSurfaceTemp = 25; // Grashof requires a surface temp, this should suffice
241 : Real64 const localGrashofNumber =
242 1343 : (gravity * beta * std::abs(assumedSurfaceTemp - curWaterTemp) * pow(distanceFromBottomOfHull, 3)) / pow(waterKinematicViscosity, 2);
243 1343 : Real64 const localNusseltFreeConvection = pow(localGrashofNumber / 4, 0.25) * prandtlCorrection;
244 1343 : Real64 const localConvectionCoeffFreeConv = localNusseltFreeConvection * waterThermalConductivity / distanceFromBottomOfHull;
245 1343 : return max(localConvectionCoeff, localConvectionCoeffFreeConv);
246 : }
247 :
248 1343 : void UpdateUnderwaterBoundaries(EnergyPlusData &state)
249 : {
250 2686 : for (auto &thisBoundary : state.dataWeather->underwaterBoundaries) {
251 1343 : Real64 const curWaterTemp = ScheduleManager::GetCurrentScheduleValue(state, thisBoundary.WaterTempScheduleIndex); // C
252 1343 : Real64 freeStreamVelocity = 0;
253 1343 : if (thisBoundary.VelocityScheduleIndex > 0) {
254 1343 : freeStreamVelocity = ScheduleManager::GetCurrentScheduleValue(state, thisBoundary.VelocityScheduleIndex); // m/s
255 : }
256 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TConv = curWaterTemp;
257 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HConv =
258 1343 : Weather::calculateWaterBoundaryConvectionCoefficient(curWaterTemp, freeStreamVelocity, thisBoundary.distanceFromLeadingEdge);
259 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TRad = curWaterTemp;
260 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HRad = 0.0;
261 1343 : }
262 1343 : }
263 :
264 795 : void ReadVariableLocationOrientation(EnergyPlusData &state)
265 : {
266 795 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
267 795 : auto const &ipsc = state.dataIPShortCut;
268 :
269 795 : ipsc->cCurrentModuleObject = "Site:VariableLocation";
270 795 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject) == 0) return;
271 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
272 1 : ipsc->cCurrentModuleObject,
273 : 1,
274 1 : ipsc->cAlphaArgs,
275 : NumAlpha,
276 1 : ipsc->rNumericArgs,
277 : NumNumber,
278 : IOStat,
279 1 : ipsc->lNumericFieldBlanks,
280 1 : ipsc->lAlphaFieldBlanks,
281 1 : ipsc->cAlphaFieldNames,
282 1 : ipsc->cNumericFieldNames);
283 1 : state.dataEnvrn->varyingLocationSchedIndexLat = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(1));
284 1 : state.dataEnvrn->varyingLocationSchedIndexLong = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(2));
285 1 : state.dataEnvrn->varyingOrientationSchedIndex = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(3));
286 : }
287 :
288 1344 : void UpdateLocationAndOrientation(EnergyPlusData &state)
289 : {
290 1344 : if (state.dataEnvrn->varyingLocationSchedIndexLat > 0) {
291 1344 : state.dataEnvrn->Latitude = ScheduleManager::GetCurrentScheduleValue(state, state.dataEnvrn->varyingLocationSchedIndexLat);
292 : }
293 1344 : if (state.dataEnvrn->varyingLocationSchedIndexLong > 0) {
294 1344 : state.dataEnvrn->Longitude = ScheduleManager::GetCurrentScheduleValue(state, state.dataEnvrn->varyingLocationSchedIndexLong);
295 : }
296 1344 : CheckLocationValidity(state);
297 1344 : if (state.dataEnvrn->varyingOrientationSchedIndex > 0) {
298 1344 : state.dataHeatBal->BuildingAzimuth =
299 1344 : mod(ScheduleManager::GetCurrentScheduleValue(state, state.dataEnvrn->varyingOrientationSchedIndex), 360.0);
300 2688 : state.dataSurfaceGeometry->CosBldgRelNorth =
301 1344 : std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
302 2688 : state.dataSurfaceGeometry->SinBldgRelNorth =
303 1344 : std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
304 8064 : for (size_t SurfNum = 1; SurfNum < state.dataSurface->Surface.size(); ++SurfNum) {
305 6720 : auto &surf = state.dataSurface->Surface(SurfNum);
306 33600 : for (int n = 1; n <= surf.Sides; ++n) {
307 26880 : Real64 Xb = surf.Vertex(n).x;
308 26880 : Real64 Yb = surf.Vertex(n).y;
309 26880 : surf.NewVertex(n).x = Xb * state.dataSurfaceGeometry->CosBldgRelNorth - Yb * state.dataSurfaceGeometry->SinBldgRelNorth;
310 26880 : surf.NewVertex(n).y = Xb * state.dataSurfaceGeometry->SinBldgRelNorth + Yb * state.dataSurfaceGeometry->CosBldgRelNorth;
311 26880 : surf.NewVertex(n).z = surf.Vertex(n).z;
312 : }
313 6720 : Vectors::CreateNewellSurfaceNormalVector(surf.NewVertex, surf.Sides, surf.NewellSurfaceNormalVector);
314 6720 : Real64 SurfWorldAz = 0.0;
315 6720 : Real64 SurfTilt = 0.0;
316 6720 : Vectors::DetermineAzimuthAndTilt(
317 6720 : surf.NewVertex, SurfWorldAz, SurfTilt, surf.lcsx, surf.lcsy, surf.lcsz, surf.NewellSurfaceNormalVector);
318 6720 : surf.Azimuth = SurfWorldAz;
319 6720 : surf.SinAzim = std::sin(SurfWorldAz * Constant::DegToRadians);
320 6720 : surf.CosAzim = std::cos(SurfWorldAz * Constant::DegToRadians);
321 6720 : surf.OutNormVec = surf.NewellSurfaceNormalVector;
322 : }
323 : }
324 1344 : }
325 :
326 11261 : bool GetNextEnvironment(EnergyPlusData &state, bool &Available, bool &ErrorsFound)
327 : {
328 :
329 : // SUBROUTINE INFORMATION:
330 : // AUTHOR Linda Lawrie
331 : // DATE WRITTEN August 2000
332 :
333 : // PURPOSE OF THIS SUBROUTINE:
334 : // This subroutine is called from the outer simulation manager and determines
335 : // if another environment is available in the "run list" or if the end has been
336 : // reached.
337 :
338 : static constexpr std::string_view RoutineName("GetNextEnvironment: ");
339 : static constexpr std::string_view EnvNameFormat("Environment,{},{},{},{},{},{},{},{},{},{},{},{},{}\n");
340 : static constexpr std::string_view EnvDSTNFormat("Environment:Daylight Saving,No,{}\n");
341 : static constexpr std::string_view EnvDSTYFormat("Environment:Daylight Saving,Yes,{},{},{}\n");
342 : static constexpr std::string_view DateFormat("{:02}/{:02}");
343 : static constexpr std::string_view DateFormatWithYear("{:02}/{:02}/{:04}");
344 11261 : std::string StDate;
345 11261 : std::string EnDate;
346 : int DSTActStMon;
347 : int DSTActStDay;
348 : int DSTActEnMon;
349 : int DSTActEnDay;
350 :
351 11261 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->GetEnvironmentFirstCall) {
352 :
353 796 : state.dataReportFlag->PrintEndDataDictionary = true;
354 :
355 796 : ReportOutputFileHeaders(state); // Write the output file header information
356 :
357 : // Setup Output Variables, CurrentModuleObject='All Simulations'
358 :
359 1592 : SetupOutputVariable(state,
360 : "Site Outdoor Air Drybulb Temperature",
361 : Constant::Units::C,
362 796 : state.dataEnvrn->OutDryBulbTemp,
363 : OutputProcessor::TimeStepType::Zone,
364 : OutputProcessor::StoreType::Average,
365 : "Environment");
366 1592 : SetupOutputVariable(state,
367 : "Site Outdoor Air Dewpoint Temperature",
368 : Constant::Units::C,
369 796 : state.dataEnvrn->OutDewPointTemp,
370 : OutputProcessor::TimeStepType::Zone,
371 : OutputProcessor::StoreType::Average,
372 : "Environment");
373 1592 : SetupOutputVariable(state,
374 : "Site Outdoor Air Wetbulb Temperature",
375 : Constant::Units::C,
376 796 : state.dataEnvrn->OutWetBulbTemp,
377 : OutputProcessor::TimeStepType::Zone,
378 : OutputProcessor::StoreType::Average,
379 : "Environment");
380 1592 : SetupOutputVariable(state,
381 : "Site Outdoor Air Humidity Ratio",
382 : Constant::Units::kgWater_kgDryAir,
383 796 : state.dataEnvrn->OutHumRat,
384 : OutputProcessor::TimeStepType::Zone,
385 : OutputProcessor::StoreType::Average,
386 : "Environment");
387 1592 : SetupOutputVariable(state,
388 : "Site Outdoor Air Relative Humidity",
389 : Constant::Units::Perc,
390 796 : state.dataEnvrn->OutRelHum,
391 : OutputProcessor::TimeStepType::Zone,
392 : OutputProcessor::StoreType::Average,
393 : "Environment");
394 1592 : SetupOutputVariable(state,
395 : "Site Outdoor Air Barometric Pressure",
396 : Constant::Units::Pa,
397 796 : state.dataEnvrn->OutBaroPress,
398 : OutputProcessor::TimeStepType::Zone,
399 : OutputProcessor::StoreType::Average,
400 : "Environment");
401 1592 : SetupOutputVariable(state,
402 : "Site Wind Speed",
403 : Constant::Units::m_s,
404 796 : state.dataEnvrn->WindSpeed,
405 : OutputProcessor::TimeStepType::Zone,
406 : OutputProcessor::StoreType::Average,
407 : "Environment");
408 1592 : SetupOutputVariable(state,
409 : "Site Wind Direction",
410 : Constant::Units::deg,
411 796 : state.dataEnvrn->WindDir,
412 : OutputProcessor::TimeStepType::Zone,
413 : OutputProcessor::StoreType::Average,
414 : "Environment");
415 1592 : SetupOutputVariable(state,
416 : "Site Sky Temperature",
417 : Constant::Units::C,
418 796 : state.dataEnvrn->SkyTemp,
419 : OutputProcessor::TimeStepType::Zone,
420 : OutputProcessor::StoreType::Average,
421 : "Environment");
422 1592 : SetupOutputVariable(state,
423 : "Site Horizontal Infrared Radiation Rate per Area",
424 : Constant::Units::W_m2,
425 796 : state.dataWeather->HorizIRSky,
426 : OutputProcessor::TimeStepType::Zone,
427 : OutputProcessor::StoreType::Average,
428 : "Environment");
429 1592 : SetupOutputVariable(state,
430 : "Site Diffuse Solar Radiation Rate per Area",
431 : Constant::Units::W_m2,
432 796 : state.dataEnvrn->DifSolarRad,
433 : OutputProcessor::TimeStepType::Zone,
434 : OutputProcessor::StoreType::Average,
435 : "Environment");
436 1592 : SetupOutputVariable(state,
437 : "Site Direct Solar Radiation Rate per Area",
438 : Constant::Units::W_m2,
439 796 : state.dataEnvrn->BeamSolarRad,
440 : OutputProcessor::TimeStepType::Zone,
441 : OutputProcessor::StoreType::Average,
442 : "Environment");
443 1592 : SetupOutputVariable(state,
444 : "Liquid Precipitation Depth",
445 : Constant::Units::m,
446 796 : state.dataEnvrn->LiquidPrecipitation,
447 : OutputProcessor::TimeStepType::Zone,
448 : OutputProcessor::StoreType::Sum,
449 : "Environment");
450 1592 : SetupOutputVariable(state,
451 : "Site Precipitation Rate",
452 : Constant::Units::m_s,
453 796 : state.dataWaterData->RainFall.CurrentRate,
454 : OutputProcessor::TimeStepType::Zone,
455 : OutputProcessor::StoreType::Average,
456 : "Environment");
457 1592 : SetupOutputVariable(state,
458 : "Site Precipitation Depth",
459 : Constant::Units::m,
460 796 : state.dataWaterData->RainFall.CurrentAmount,
461 : OutputProcessor::TimeStepType::Zone,
462 : OutputProcessor::StoreType::Sum,
463 : "Environment");
464 1592 : SetupOutputVariable(state,
465 : "Site Ground Reflected Solar Radiation Rate per Area",
466 : Constant::Units::W_m2,
467 796 : state.dataEnvrn->GndSolarRad,
468 : OutputProcessor::TimeStepType::Zone,
469 : OutputProcessor::StoreType::Average,
470 : "Environment");
471 1592 : SetupOutputVariable(state,
472 : "Site Ground Temperature",
473 : Constant::Units::C,
474 796 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface],
475 : OutputProcessor::TimeStepType::Zone,
476 : OutputProcessor::StoreType::Average,
477 : "Environment");
478 1592 : SetupOutputVariable(state,
479 : "Site Surface Ground Temperature",
480 : Constant::Units::C,
481 796 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow],
482 : OutputProcessor::TimeStepType::Zone,
483 : OutputProcessor::StoreType::Average,
484 : "Environment");
485 1592 : SetupOutputVariable(state,
486 : "Site Deep Ground Temperature",
487 : Constant::Units::C,
488 796 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep],
489 : OutputProcessor::TimeStepType::Zone,
490 : OutputProcessor::StoreType::Average,
491 : "Environment");
492 1592 : SetupOutputVariable(state,
493 : "Site Simple Factor Model Ground Temperature",
494 : Constant::Units::C,
495 796 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod],
496 : OutputProcessor::TimeStepType::Zone,
497 : OutputProcessor::StoreType::Average,
498 : "Environment");
499 1592 : SetupOutputVariable(state,
500 : "Site Total Sky Cover",
501 : Constant::Units::None,
502 796 : state.dataEnvrn->TotalCloudCover,
503 : OutputProcessor::TimeStepType::Zone,
504 : OutputProcessor::StoreType::Average,
505 : "Environment");
506 1592 : SetupOutputVariable(state,
507 : "Site Opaque Sky Cover",
508 : Constant::Units::None,
509 796 : state.dataEnvrn->OpaqueCloudCover,
510 : OutputProcessor::TimeStepType::Zone,
511 : OutputProcessor::StoreType::Average,
512 : "Environment");
513 1592 : SetupOutputVariable(state,
514 : "Site Outdoor Air Enthalpy",
515 : Constant::Units::J_kg,
516 796 : state.dataEnvrn->OutEnthalpy,
517 : OutputProcessor::TimeStepType::Zone,
518 : OutputProcessor::StoreType::Average,
519 : "Environment");
520 1592 : SetupOutputVariable(state,
521 : "Site Outdoor Air Density",
522 : Constant::Units::kg_m3,
523 796 : state.dataEnvrn->OutAirDensity,
524 : OutputProcessor::TimeStepType::Zone,
525 : OutputProcessor::StoreType::Average,
526 : "Environment");
527 1592 : SetupOutputVariable(state,
528 : "Site Solar Azimuth Angle",
529 : Constant::Units::deg,
530 796 : state.dataWeather->SolarAzimuthAngle,
531 : OutputProcessor::TimeStepType::Zone,
532 : OutputProcessor::StoreType::Average,
533 : "Environment");
534 1592 : SetupOutputVariable(state,
535 : "Site Solar Altitude Angle",
536 : Constant::Units::deg,
537 796 : state.dataWeather->SolarAltitudeAngle,
538 : OutputProcessor::TimeStepType::Zone,
539 : OutputProcessor::StoreType::Average,
540 : "Environment");
541 1592 : SetupOutputVariable(state,
542 : "Site Solar Hour Angle",
543 : Constant::Units::deg,
544 796 : state.dataWeather->HrAngle,
545 : OutputProcessor::TimeStepType::Zone,
546 : OutputProcessor::StoreType::Average,
547 : "Environment");
548 1592 : SetupOutputVariable(state,
549 : "Site Rain Status",
550 : Constant::Units::None,
551 796 : state.dataWeather->RptIsRain,
552 : OutputProcessor::TimeStepType::Zone,
553 : OutputProcessor::StoreType::Average,
554 : "Environment");
555 1592 : SetupOutputVariable(state,
556 : "Site Snow on Ground Status",
557 : Constant::Units::None,
558 796 : state.dataWeather->RptIsSnow,
559 : OutputProcessor::TimeStepType::Zone,
560 : OutputProcessor::StoreType::Average,
561 : "Environment");
562 1592 : SetupOutputVariable(state,
563 : "Site Exterior Horizontal Sky Illuminance",
564 : Constant::Units::lux,
565 796 : state.dataEnvrn->HISKF,
566 : OutputProcessor::TimeStepType::Zone,
567 : OutputProcessor::StoreType::Average,
568 : "Environment");
569 1592 : SetupOutputVariable(state,
570 : "Site Exterior Horizontal Beam Illuminance",
571 : Constant::Units::lux,
572 796 : state.dataEnvrn->HISUNF,
573 : OutputProcessor::TimeStepType::Zone,
574 : OutputProcessor::StoreType::Average,
575 : "Environment");
576 1592 : SetupOutputVariable(state,
577 : "Site Exterior Beam Normal Illuminance",
578 : Constant::Units::lux,
579 796 : state.dataEnvrn->HISUNFnorm,
580 : OutputProcessor::TimeStepType::Zone,
581 : OutputProcessor::StoreType::Average,
582 : "Environment");
583 1592 : SetupOutputVariable(state,
584 : "Site Sky Diffuse Solar Radiation Luminous Efficacy",
585 : Constant::Units::lum_W,
586 796 : state.dataEnvrn->PDIFLW,
587 : OutputProcessor::TimeStepType::Zone,
588 : OutputProcessor::StoreType::Average,
589 : "Environment");
590 1592 : SetupOutputVariable(state,
591 : "Site Beam Solar Radiation Luminous Efficacy",
592 : Constant::Units::lum_W,
593 796 : state.dataEnvrn->PDIRLW,
594 : OutputProcessor::TimeStepType::Zone,
595 : OutputProcessor::StoreType::Average,
596 : "Environment");
597 1592 : SetupOutputVariable(state,
598 : "Site Daylighting Model Sky Clearness",
599 : Constant::Units::None,
600 796 : state.dataEnvrn->SkyClearness,
601 : OutputProcessor::TimeStepType::Zone,
602 : OutputProcessor::StoreType::Average,
603 : "Environment");
604 1592 : SetupOutputVariable(state,
605 : "Site Daylighting Model Sky Brightness",
606 : Constant::Units::None,
607 796 : state.dataEnvrn->SkyBrightness,
608 : OutputProcessor::TimeStepType::Zone,
609 : OutputProcessor::StoreType::Average,
610 : "Environment");
611 1592 : SetupOutputVariable(state,
612 : "Site Daylight Saving Time Status",
613 : Constant::Units::None,
614 796 : state.dataEnvrn->DSTIndicator,
615 : OutputProcessor::TimeStepType::Zone,
616 : OutputProcessor::StoreType::Average,
617 : "Environment");
618 1592 : SetupOutputVariable(state,
619 : "Site Day Type Index",
620 : Constant::Units::None,
621 796 : state.dataWeather->RptDayType,
622 : OutputProcessor::TimeStepType::Zone,
623 : OutputProcessor::StoreType::Average,
624 : "Environment");
625 1592 : SetupOutputVariable(state,
626 : "Site Mains Water Temperature",
627 : Constant::Units::C,
628 796 : state.dataEnvrn->WaterMainsTemp,
629 : OutputProcessor::TimeStepType::Zone,
630 : OutputProcessor::StoreType::Average,
631 : "Environment");
632 :
633 796 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
634 73 : SetupEMSActuator(state,
635 : "Weather Data",
636 : "Environment",
637 : "Outdoor Dry Bulb",
638 : "[C]",
639 73 : state.dataEnvrn->EMSOutDryBulbOverrideOn,
640 73 : state.dataEnvrn->EMSOutDryBulbOverrideValue);
641 73 : SetupEMSActuator(state,
642 : "Weather Data",
643 : "Environment",
644 : "Outdoor Dew Point",
645 : "[C]",
646 73 : state.dataEnvrn->EMSOutDewPointTempOverrideOn,
647 73 : state.dataEnvrn->EMSOutDewPointTempOverrideValue);
648 73 : SetupEMSActuator(state,
649 : "Weather Data",
650 : "Environment",
651 : "Outdoor Relative Humidity",
652 : "[%]",
653 73 : state.dataEnvrn->EMSOutRelHumOverrideOn,
654 73 : state.dataEnvrn->EMSOutRelHumOverrideValue);
655 73 : SetupEMSActuator(state,
656 : "Weather Data",
657 : "Environment",
658 : "Diffuse Solar",
659 : "[W/m2]",
660 73 : state.dataEnvrn->EMSDifSolarRadOverrideOn,
661 73 : state.dataEnvrn->EMSDifSolarRadOverrideValue);
662 73 : SetupEMSActuator(state,
663 : "Weather Data",
664 : "Environment",
665 : "Direct Solar",
666 : "[W/m2]",
667 73 : state.dataEnvrn->EMSBeamSolarRadOverrideOn,
668 73 : state.dataEnvrn->EMSBeamSolarRadOverrideValue);
669 73 : SetupEMSActuator(state,
670 : "Weather Data",
671 : "Environment",
672 : "Wind Speed",
673 : "[m/s]",
674 73 : state.dataEnvrn->EMSWindSpeedOverrideOn,
675 73 : state.dataEnvrn->EMSWindSpeedOverrideValue);
676 73 : SetupEMSActuator(state,
677 : "Weather Data",
678 : "Environment",
679 : "Wind Direction",
680 : "[deg]",
681 73 : state.dataEnvrn->EMSWindDirOverrideOn,
682 73 : state.dataEnvrn->EMSWindDirOverrideValue);
683 : }
684 796 : state.dataWeather->GetEnvironmentFirstCall = false;
685 :
686 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
687 :
688 11261 : if (state.dataWeather->GetBranchInputOneTimeFlag) {
689 :
690 796 : SetupInterpolationValues(state);
691 796 : state.dataWeather->TimeStepFraction = 1.0 / double(state.dataGlobal->NumOfTimeStepInHour);
692 796 : state.dataEnvrn->rhoAirSTP = Psychrometrics::PsyRhoAirFnPbTdbW(
693 : state, DataEnvironment::StdPressureSeaLevel, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
694 796 : OpenWeatherFile(state, ErrorsFound); // moved here because of possibility of special days on EPW file
695 796 : CloseWeatherFile(state);
696 796 : ReadUserWeatherInput(state);
697 796 : AllocateWeatherData(state);
698 796 : if (state.dataWeather->NumIntervalsPerHour != 1) {
699 0 : if (state.dataWeather->NumIntervalsPerHour != state.dataGlobal->NumOfTimeStepInHour) {
700 0 : ShowSevereError(
701 : state,
702 0 : format("{}Number of intervals per hour on Weather file does not match specified number of Time Steps Per Hour", RoutineName));
703 0 : ErrorsFound = true;
704 : }
705 : }
706 796 : state.dataWeather->GetBranchInputOneTimeFlag = false;
707 796 : state.dataWeather->Envrn = 0;
708 796 : if (state.dataWeather->NumOfEnvrn > 0) {
709 796 : ResolveLocationInformation(state, ErrorsFound); // Obtain weather related info from input file
710 796 : CheckLocationValidity(state);
711 1567 : if ((state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
712 771 : (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
713 771 : CheckWeatherFileValidity(state);
714 : }
715 796 : if (ErrorsFound) {
716 0 : ShowSevereError(state, format("{}No location specified, program will terminate.", RoutineName));
717 : }
718 : } else {
719 0 : ErrorsFound = true;
720 0 : ShowSevereError(state, format("{}No Design Days or Run Period(s) specified, program will terminate.", RoutineName));
721 : }
722 796 : if (state.dataSysVars->DDOnly && state.dataEnvrn->TotDesDays == 0) {
723 0 : ErrorsFound = true;
724 0 : ShowSevereError(
725 : state,
726 0 : format("{}Requested Design Days only (DataSystemVariables::DDOnly) but no Design Days specified, program will terminate.",
727 : RoutineName));
728 : }
729 796 : if (state.dataSysVars->ReverseDD && state.dataEnvrn->TotDesDays == 1) {
730 0 : ErrorsFound = true;
731 0 : ShowSevereError(
732 : state,
733 0 : format(
734 : "{}Requested Reverse Design Days (DataSystemVariables::ReverseDD) but only 1 Design Day specified, program will terminate.",
735 : RoutineName));
736 : }
737 :
738 : // Throw a Fatal now that we have said it'll terminalte
739 796 : if (ErrorsFound) {
740 0 : CloseWeatherFile(state); // will only close if opened.
741 0 : ShowFatalError(state, format("{}Errors found in Weather Data Input. Program terminates.", RoutineName));
742 : }
743 :
744 796 : state.dataEnvrn->CurrentOverallSimDay = 0;
745 796 : state.dataEnvrn->TotalOverallSimDays = 0;
746 796 : state.dataEnvrn->MaxNumberSimYears = 1;
747 3652 : for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
748 2856 : state.dataEnvrn->TotalOverallSimDays += state.dataWeather->Environment(i).TotalDays;
749 2856 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
750 1185 : state.dataEnvrn->MaxNumberSimYears = max(state.dataEnvrn->MaxNumberSimYears, state.dataWeather->Environment(i).NumSimYears);
751 : }
752 : }
753 796 : DisplaySimDaysProgress(state, state.dataEnvrn->CurrentOverallSimDay, state.dataEnvrn->TotalOverallSimDays);
754 : }
755 :
756 11261 : CloseWeatherFile(state); // will only close if opened.
757 11261 : ++state.dataWeather->Envrn;
758 11261 : state.dataWeather->DatesShouldBeReset = false;
759 11261 : if (state.dataWeather->Envrn > state.dataWeather->NumOfEnvrn) {
760 1593 : Available = false;
761 1593 : state.dataWeather->Envrn = 0;
762 1593 : state.dataEnvrn->CurEnvirNum = 0;
763 : } else {
764 9668 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
765 9668 : state.dataGlobal->KindOfSim = envCurr.KindOfEnvrn;
766 9668 : state.dataEnvrn->DayOfYear = envCurr.StartJDay;
767 9668 : state.dataEnvrn->DayOfMonth = envCurr.StartDay;
768 9668 : state.dataGlobal->CalendarYear = envCurr.StartYear;
769 9668 : state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
770 9668 : state.dataEnvrn->Month = envCurr.StartMonth;
771 9668 : state.dataGlobal->NumOfDayInEnvrn = envCurr.TotalDays; // Set day loop maximum from DataGlobals
772 :
773 12655 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
774 2987 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
775 13 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) {
776 8 : if (state.dataGlobal->DoDesDaySim) {
777 8 : ShowWarningError(state, format("{}Adaptive Comfort being reported during design day.", RoutineName));
778 8 : Real64 GrossApproxAvgDryBulb = (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb +
779 8 : (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb -
780 8 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).DailyDBRange)) /
781 8 : 2.0;
782 8 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55)
783 8 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, false, GrossApproxAvgDryBulb);
784 8 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251)
785 2 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, false, GrossApproxAvgDryBulb);
786 : }
787 : } else {
788 5 : if (state.dataGlobal->DoWeathSim || state.dataGlobal->DoDesDaySim) {
789 5 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55)
790 5 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, true, 0.0);
791 5 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251)
792 1 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, true, 0.0);
793 : }
794 : }
795 : }
796 :
797 9668 : if (state.dataWeather->Envrn > state.dataEnvrn->TotDesDays && state.dataWeather->WeatherFileExists) {
798 3749 : OpenEPlusWeatherFile(state, ErrorsFound, false);
799 : }
800 9668 : Available = true;
801 13289 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
802 3621 : (!state.dataWeather->WeatherFileExists && state.dataGlobal->DoWeathSim)) {
803 0 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
804 0 : ShowSevereError(state, "Weather Simulation requested, but no weather file attached.");
805 0 : ErrorsFound = true;
806 : }
807 0 : if (!state.dataGlobal->DoingHVACSizingSimulations) state.dataWeather->Envrn = 0;
808 0 : Available = false;
809 13289 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
810 3621 : (!state.dataWeather->WeatherFileExists && !state.dataGlobal->DoWeathSim)) {
811 20 : Available = false;
812 20 : if (!state.dataGlobal->DoingHVACSizingSimulations) state.dataWeather->Envrn = 0;
813 9648 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoingSizing) {
814 1208 : Available = false;
815 1208 : state.dataWeather->Envrn = 0;
816 : }
817 :
818 9668 : if (!ErrorsFound && Available && state.dataWeather->Envrn > 0) {
819 8440 : state.dataEnvrn->EnvironmentName = envCurr.Title;
820 8440 : state.dataEnvrn->CurEnvirNum = state.dataWeather->Envrn;
821 8440 : state.dataEnvrn->RunPeriodStartDayOfWeek = 0;
822 10879 : if ((state.dataGlobal->DoDesDaySim && (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather)) ||
823 2439 : ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoWeathSim)) {
824 6013 : if (state.dataWeather->PrntEnvHeaders && state.dataReportFlag->DoWeatherInitReporting) {
825 : static constexpr std::string_view EnvironFormat(
826 : "! <Environment>,Environment Name,Environment Type, Start Date, End Date, Start DayOfWeek, Duration {#days}, "
827 : "Source:Start DayOfWeek, Use Daylight Saving, Use Holidays, Apply Weekend Holiday Rule, Use Rain Values, Use Snow "
828 : "Values, Sky Temperature Model\n! <Environment:Special Days>, Special Day Name, Special Day Type, Source, Start Date, "
829 : "Duration {#days}\n! "
830 : "<Environment:Daylight Saving>, Daylight Saving Indicator, Source, Start Date, End Date\n! <Environment:WarmupDays>, "
831 : "NumberofWarmupDays");
832 795 : print(state.files.eio, "{}\n", EnvironFormat);
833 795 : state.dataWeather->PrntEnvHeaders = false;
834 : }
835 :
836 12014 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) ||
837 6001 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodDesign)) {
838 34 : std::string kindOfRunPeriod = envCurr.cKindOfEnvrn;
839 34 : state.dataEnvrn->RunPeriodEnvironment = state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather;
840 34 : state.dataEnvrn->CurrentYearIsLeapYear = state.dataWeather->Environment(state.dataWeather->Envrn).IsLeapYear;
841 34 : if (state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears) {
842 2 : state.dataWeather->LeapYearAdd = 1;
843 : } else {
844 32 : state.dataWeather->LeapYearAdd = 0;
845 : }
846 34 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
847 2 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
848 : }
849 34 : state.dataWeather->UseDaylightSaving = envCurr.UseDST;
850 34 : state.dataWeather->UseSpecialDays = envCurr.UseHolidays;
851 34 : state.dataWeather->UseRainValues = envCurr.UseRain;
852 34 : state.dataWeather->UseSnowValues = envCurr.UseSnow;
853 :
854 34 : bool missingLeap(false); // Defer acting on anything found here until after the other range checks (see below)
855 :
856 34 : if (envCurr.ActualWeather && !state.dataWeather->WFAllowsLeapYears) {
857 0 : for (int year = envCurr.StartYear; year <= envCurr.EndYear; year++) {
858 0 : if (!isLeapYear(year)) continue;
859 :
860 0 : ShowSevereError(
861 : state,
862 0 : format("{}Weatherfile does not support leap years but runperiod includes a leap year ({})", RoutineName, year));
863 0 : missingLeap = true;
864 : }
865 : }
866 :
867 34 : bool OkRun = false;
868 :
869 34 : if (envCurr.ActualWeather) {
870 : // Actual weather
871 0 : for (auto &dataperiod : state.dataWeather->DataPeriods) {
872 0 : int runStartJulian = dataperiod.DataStJDay;
873 0 : int runEndJulian = dataperiod.DataEnJDay;
874 0 : if (!dataperiod.HasYearData) {
875 0 : ShowSevereError(state,
876 0 : format("{}Actual weather runperiod has been entered but weatherfile DATA PERIOD does not have "
877 : "year included in start/end date.",
878 : RoutineName));
879 0 : ShowContinueError(state, "...to match the RunPeriod, the DATA PERIOD should be mm/dd/yyyy for both, or");
880 0 : ShowContinueError(state, "(...set \"Treat Weather as Actual\" to \"No\".)");
881 : }
882 0 : if (!General::BetweenDates(envCurr.StartDate, runStartJulian, runEndJulian)) continue;
883 0 : if (!General::BetweenDates(envCurr.EndDate, runStartJulian, runEndJulian)) continue;
884 0 : OkRun = true;
885 0 : break;
886 0 : }
887 : } else {
888 : // Typical (or just non-actual) weather
889 34 : for (auto &dataperiod : state.dataWeather->DataPeriods) {
890 : // Since this is not actual weather, there may be issues with this calculation
891 : // Assume the weather data starts the same year as the simulation, so LeapYearAdd is what
892 : // should be used.
893 34 : int runStartOrdinal = General::OrdinalDay(dataperiod.StMon, dataperiod.StDay, state.dataWeather->LeapYearAdd);
894 : // This one is harder, leave as is for now. What about multiple years of data?
895 34 : int runEndOrdinal = General::OrdinalDay(dataperiod.EnMon, dataperiod.EnDay, state.dataWeather->LeapYearAdd);
896 34 : if (runStartOrdinal == 1 && (runEndOrdinal == 366 || runEndOrdinal == 365)) {
897 : // Complete year(s) of weather data, will wrap around
898 34 : OkRun = true;
899 34 : break;
900 : }
901 0 : if (!General::BetweenDates(envCurr.StartJDay, runStartOrdinal, runEndOrdinal)) continue;
902 0 : if (!General::BetweenDates(envCurr.EndJDay, runStartOrdinal, runEndOrdinal)) continue;
903 0 : OkRun = true;
904 34 : }
905 : }
906 :
907 34 : if (!OkRun) {
908 0 : if (!envCurr.ActualWeather) {
909 0 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
910 0 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
911 0 : ShowSevereError(state,
912 0 : format("{}Runperiod [mm/dd] (Start={},End={}) requested not within Data Period(s) from Weather File",
913 : RoutineName,
914 : StDate,
915 : EnDate));
916 : } else {
917 0 : StDate = format(DateFormatWithYear, envCurr.StartMonth, envCurr.StartDay, envCurr.StartYear);
918 0 : EnDate = format(DateFormatWithYear, envCurr.EndMonth, envCurr.EndDay, envCurr.EndYear);
919 0 : ShowSevereError(
920 : state,
921 0 : format("{}Runperiod [mm/dd/yyyy] (Start={},End={}) requested not within Data Period(s) from Weather File",
922 : RoutineName,
923 : StDate,
924 : EnDate));
925 : }
926 :
927 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
928 0 : StDate = format(DateFormat, dataPeriod1.StMon, dataPeriod1.StDay);
929 0 : EnDate = format(DateFormat, dataPeriod1.EnMon, dataPeriod1.EnDay);
930 0 : if (dataPeriod1.StYear > 0) {
931 0 : StDate += format("/{}", dataPeriod1.StYear);
932 : } else {
933 0 : StDate += "/<noyear>";
934 : }
935 0 : if (dataPeriod1.EnYear > 0) {
936 0 : EnDate += format("/{}", dataPeriod1.EnYear);
937 : } else {
938 0 : EnDate += "/<noyear>";
939 : }
940 0 : if (state.dataWeather->NumDataPeriods == 1) {
941 0 : ShowContinueError(state, format("Weather Data Period (Start={},End={})", StDate, EnDate));
942 : } else {
943 0 : ShowContinueError(state, format("Multiple Weather Data Periods 1st (Start={},End={})", StDate, EnDate));
944 : }
945 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
946 : }
947 :
948 34 : if (missingLeap) {
949 : // Bail out now if we still need to
950 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
951 : }
952 :
953 : // Following builds Environment start/end for ASHRAE 55 warnings
954 34 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
955 34 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
956 34 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
957 12 : StDate += format("/{}", envCurr.StartYear);
958 12 : EnDate += format("/{}", envCurr.EndYear);
959 : }
960 34 : state.dataEnvrn->EnvironmentStartEnd = StDate + " - " + EnDate;
961 34 : state.dataEnvrn->StartYear = envCurr.StartYear;
962 34 : state.dataEnvrn->EndYear = envCurr.EndYear;
963 :
964 34 : int TWeekDay = (envCurr.DayOfWeek == 0) ? 1 : envCurr.DayOfWeek;
965 34 : auto const &MonWeekDay = envCurr.MonWeekDay;
966 :
967 34 : if (state.dataReportFlag->DoWeatherInitReporting) {
968 11 : std::string_view const AlpUseDST = (envCurr.UseDST) ? "Yes" : "No";
969 11 : std::string_view const AlpUseSpec = (envCurr.UseHolidays) ? "Yes" : "No";
970 11 : std::string_view const ApWkRule = (envCurr.ApplyWeekendRule) ? "Yes" : "No";
971 11 : std::string_view const AlpUseRain = (envCurr.UseRain) ? "Yes" : "No";
972 11 : std::string_view const AlpUseSnow = (envCurr.UseSnow) ? "Yes" : "No";
973 :
974 11 : print(state.files.eio,
975 : EnvNameFormat,
976 11 : envCurr.Title,
977 : kindOfRunPeriod,
978 : StDate,
979 : EnDate,
980 11 : ScheduleManager::dayTypeNames[TWeekDay],
981 22 : fmt::to_string(envCurr.TotalDays),
982 : "Use RunPeriod Specified Day",
983 : AlpUseDST,
984 : AlpUseSpec,
985 : ApWkRule,
986 : AlpUseRain,
987 : AlpUseSnow,
988 11 : SkyTempModelNames[(int)envCurr.skyTempModel]);
989 : }
990 :
991 56 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
992 62 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather && state.dataGlobal->DoWeathSim) &&
993 6 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
994 0 : if (state.dataWeather->WFAllowsLeapYears) {
995 0 : ShowSevereError(
996 : state,
997 0 : format("{}AdaptiveComfort Reporting does not work correctly with leap years in weather files.", RoutineName));
998 0 : ErrorsFound = true;
999 : }
1000 0 : if (state.dataWeather->NumDataPeriods != 1) {
1001 0 : ShowSevereError(
1002 : state,
1003 0 : format("{}AdaptiveComfort Reporting does not work correctly with multiple dataperiods in weather files.",
1004 : RoutineName));
1005 0 : ErrorsFound = true;
1006 : }
1007 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
1008 0 : if (dataPeriod1.StMon == 1 && dataPeriod1.StDay == 1) {
1009 0 : int RunStJDay = General::OrdinalDay(dataPeriod1.StMon, dataPeriod1.StDay, state.dataWeather->LeapYearAdd);
1010 0 : int RunEnJDay = General::OrdinalDay(dataPeriod1.EnMon, dataPeriod1.EnDay, state.dataWeather->LeapYearAdd);
1011 0 : if (RunEnJDay - RunStJDay + 1 != 365) {
1012 0 : ShowSevereError(state,
1013 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do "
1014 : "not contain 365 days.",
1015 : RoutineName));
1016 0 : ErrorsFound = true;
1017 : }
1018 0 : } else {
1019 0 : ShowSevereError(state,
1020 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do not "
1021 : "start on 1 January.",
1022 : RoutineName));
1023 0 : ErrorsFound = true;
1024 : }
1025 0 : if (state.dataWeather->NumIntervalsPerHour != 1) {
1026 0 : ShowSevereError(state,
1027 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that have "
1028 : "multiple interval records per hour.",
1029 : RoutineName));
1030 0 : ErrorsFound = true;
1031 : }
1032 : } // if
1033 :
1034 : // Only need to set Week days for Run Days
1035 34 : state.dataEnvrn->RunPeriodStartDayOfWeek = TWeekDay;
1036 34 : state.dataWeather->WeekDayTypes = 0;
1037 34 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1038 34 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1039 :
1040 34 : state.dataWeather->curSimDayForEndOfRunPeriod = envCurr.TotalDays;
1041 :
1042 34 : int i = JDay5Start;
1043 : while (true) {
1044 4514 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1045 4514 : TWeekDay = mod(TWeekDay, 7) + 1;
1046 4514 : ++i;
1047 4514 : if (i > 366) i = 1;
1048 4514 : if (i == JDay5End) break;
1049 : }
1050 :
1051 34 : state.dataWeather->DaylightSavingIsActive =
1052 34 : (state.dataWeather->UseDaylightSaving && state.dataWeather->EPWDaylightSaving) || state.dataWeather->IDFDaylightSaving;
1053 :
1054 34 : envCurr.SetWeekDays = false;
1055 :
1056 34 : if (state.dataWeather->DaylightSavingIsActive) {
1057 2 : SetDSTDateRanges(state, MonWeekDay, state.dataWeather->DSTIndex, DSTActStMon, DSTActStDay, DSTActEnMon, DSTActEnDay);
1058 : }
1059 :
1060 34 : SetSpecialDayDates(state, MonWeekDay);
1061 :
1062 34 : if (envCurr.StartMonth != 1 || envCurr.StartDay != 1) {
1063 22 : state.dataWeather->StartDatesCycleShouldBeReset = true;
1064 22 : state.dataWeather->Jan1DatesShouldBeReset = true;
1065 : }
1066 :
1067 34 : if (envCurr.StartMonth == 1 && envCurr.StartDay == 1) {
1068 12 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1069 12 : state.dataWeather->Jan1DatesShouldBeReset = true;
1070 : }
1071 :
1072 34 : if (envCurr.ActualWeather) {
1073 0 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1074 0 : state.dataWeather->Jan1DatesShouldBeReset = true;
1075 : }
1076 :
1077 : // Report Actual Dates for Daylight Saving and Special Days
1078 34 : if (!state.dataGlobal->KickOffSimulation) {
1079 23 : std::string Source;
1080 23 : if (state.dataWeather->UseDaylightSaving) {
1081 6 : if (state.dataWeather->EPWDaylightSaving) {
1082 1 : Source = "WeatherFile";
1083 : }
1084 : } else {
1085 17 : Source = "RunPeriod Object";
1086 : }
1087 23 : if (state.dataWeather->IDFDaylightSaving) {
1088 0 : Source = "InputFile";
1089 : }
1090 23 : if (state.dataWeather->DaylightSavingIsActive && state.dataReportFlag->DoWeatherInitReporting) {
1091 1 : StDate = format(DateFormat, DSTActStMon, DSTActStDay);
1092 1 : EnDate = format(DateFormat, DSTActEnMon, DSTActEnDay);
1093 1 : print(state.files.eio, EnvDSTYFormat, Source, StDate, EnDate);
1094 22 : } else if (state.dataGlobal->DoOutputReporting) {
1095 10 : print(state.files.eio, EnvDSTNFormat, Source);
1096 : }
1097 44 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1098 21 : auto &specialDay = state.dataWeather->SpecialDays(i);
1099 : static constexpr std::string_view EnvSpDyFormat("Environment:Special Days,{},{},{},{},{:3}\n");
1100 21 : if (specialDay.WthrFile && state.dataWeather->UseSpecialDays && state.dataReportFlag->DoWeatherInitReporting) {
1101 11 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1102 11 : print(state.files.eio,
1103 : EnvSpDyFormat,
1104 11 : specialDay.Name,
1105 11 : ScheduleManager::dayTypeNames[specialDay.DayType],
1106 : "WeatherFile",
1107 : StDate,
1108 11 : specialDay.Duration);
1109 : }
1110 21 : if (!specialDay.WthrFile && state.dataReportFlag->DoWeatherInitReporting) {
1111 10 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1112 10 : print(state.files.eio,
1113 : EnvSpDyFormat,
1114 10 : specialDay.Name,
1115 10 : ScheduleManager::dayTypeNames[specialDay.DayType],
1116 : "InputFile",
1117 : StDate,
1118 10 : specialDay.Duration);
1119 : }
1120 : }
1121 23 : }
1122 :
1123 6139 : } else if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay ||
1124 126 : state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay) { // Design Day
1125 5978 : auto const &desDayInput = state.dataWeather->DesDayInput(envCurr.DesignDayNum);
1126 5978 : state.dataEnvrn->RunPeriodEnvironment = false;
1127 5978 : StDate = format(DateFormat, desDayInput.Month, desDayInput.DayOfMonth);
1128 5978 : EnDate = StDate;
1129 5978 : if (state.dataReportFlag->DoWeatherInitReporting) {
1130 1846 : print(state.files.eio,
1131 : EnvNameFormat,
1132 1846 : envCurr.Title,
1133 : "SizingPeriod:DesignDay",
1134 : StDate,
1135 : EnDate,
1136 1846 : ScheduleManager::dayTypeNames[desDayInput.DayType],
1137 : "1",
1138 : "N/A",
1139 : "N/A",
1140 : "N/A",
1141 : "N/A",
1142 : "N/A",
1143 : "N/A",
1144 1846 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1145 : }
1146 5978 : if (desDayInput.DSTIndicator == 0 && state.dataReportFlag->DoWeatherInitReporting) {
1147 1846 : print(state.files.eio, EnvDSTNFormat, "SizingPeriod:DesignDay");
1148 4132 : } else if (state.dataReportFlag->DoWeatherInitReporting) {
1149 0 : print(state.files.eio, EnvDSTYFormat, "SizingPeriod:DesignDay", StDate, EnDate);
1150 : }
1151 : }
1152 : }
1153 : } // ErrorsFound
1154 : }
1155 :
1156 11261 : if (ErrorsFound && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
1157 0 : ShowSevereError(state, format("{}Errors found in getting a new environment", RoutineName));
1158 0 : Available = false;
1159 11261 : } else if (ErrorsFound) {
1160 0 : Available = false;
1161 : }
1162 22522 : return Available && !ErrorsFound;
1163 11261 : }
1164 :
1165 17 : void AddDesignSetToEnvironmentStruct(EnergyPlusData &state, int const HVACSizingIterCount)
1166 : {
1167 17 : int OrigNumOfEnvrn = state.dataWeather->NumOfEnvrn;
1168 :
1169 84 : for (int i = 1; i <= OrigNumOfEnvrn; ++i) {
1170 : // Gotcha: references may no longer be valid after a redimension! Cannot declare reference to Environment(i) here.
1171 67 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1172 35 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1173 35 : auto &envBase = state.dataWeather->Environment(i);
1174 35 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1175 35 : envNew = envBase; // copy over seed data from current array element
1176 35 : envNew.SeedEnvrnNum = i;
1177 35 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeDesignDay;
1178 35 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1179 35 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1180 32 : } else if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
1181 0 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1182 0 : auto &envBase = state.dataWeather->Environment(i);
1183 0 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1184 0 : envNew = envBase; // copy over seed data
1185 0 : envNew.SeedEnvrnNum = i;
1186 0 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeRunPeriodDesign;
1187 0 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1188 0 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1189 : }
1190 : } // for each loop over Environment data strucure
1191 17 : }
1192 :
1193 1978 : void SetupWeekDaysByMonth(EnergyPlusData &state, int const StMon, int const StDay, int const StWeekDay, Array1D_int &WeekDays)
1194 : {
1195 :
1196 : // SUBROUTINE INFORMATION:
1197 : // AUTHOR Linda Lawrie
1198 : // DATE WRITTEN August 2000
1199 :
1200 : // PURPOSE OF THIS SUBROUTINE:
1201 : // This subroutine calculates the weekday for each month based on the start date and
1202 : // weekday specified for that date.
1203 :
1204 : // Argument array dimensioning
1205 1978 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1206 :
1207 : // Set 1st day of Start Month
1208 1978 : int CurWeekDay{StWeekDay};
1209 8168 : for (int i = 1; i <= StDay - 1; ++i) {
1210 6190 : --CurWeekDay;
1211 6190 : if (CurWeekDay == 0) CurWeekDay = 7;
1212 : }
1213 :
1214 1978 : WeekDays(StMon) = CurWeekDay;
1215 20940 : for (int i = StMon + 1; i <= 12; ++i) {
1216 :
1217 18962 : if (i == 2) {
1218 1473 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1219 7377 : while (CurWeekDay > 7) {
1220 5904 : CurWeekDay -= 7;
1221 : }
1222 1473 : WeekDays(i) = CurWeekDay;
1223 17489 : } else if (i == 3) {
1224 1489 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + state.dataWeather->LeapYearAdd;
1225 7445 : while (CurWeekDay > 7) {
1226 5956 : CurWeekDay -= 7;
1227 : }
1228 1489 : WeekDays(i) = CurWeekDay;
1229 16000 : } else if ((i >= 4) && (i <= 12)) {
1230 16000 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1231 85906 : while (CurWeekDay > 7) {
1232 69906 : CurWeekDay -= 7;
1233 : }
1234 16000 : WeekDays(i) = CurWeekDay;
1235 : }
1236 : }
1237 :
1238 1978 : if (any_eq(WeekDays, 0)) {
1239 : // need to start at StMon and go backwards.
1240 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1241 504 : CurWeekDay = StWeekDay;
1242 3125 : for (int i = 1; i <= StDay - 1; ++i) {
1243 2621 : --CurWeekDay;
1244 2621 : if (CurWeekDay == 0) CurWeekDay = 7;
1245 : }
1246 :
1247 3294 : for (int i = StMon - 1; i >= 1; --i) {
1248 :
1249 2790 : if (i == 1) {
1250 504 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1251 2796 : while (CurWeekDay <= 0) {
1252 2292 : CurWeekDay += 7;
1253 : }
1254 504 : WeekDays(i) = CurWeekDay;
1255 2286 : } else if (i == 2) {
1256 488 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
1257 2440 : while (CurWeekDay <= 0) {
1258 1952 : CurWeekDay += 7;
1259 : }
1260 488 : WeekDays(i) = CurWeekDay;
1261 1798 : } else if ((i >= 3) && (i <= 12)) {
1262 1798 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1263 9602 : while (CurWeekDay <= 0) {
1264 7804 : CurWeekDay += 7;
1265 : }
1266 1798 : WeekDays(i) = CurWeekDay;
1267 : }
1268 : }
1269 : }
1270 1978 : }
1271 : #pragma clang diagnostic pop
1272 :
1273 0 : void ResetWeekDaysByMonth(EnergyPlusData &state,
1274 : Array1D_int &WeekDays,
1275 : int const AddLeapYear,
1276 : int const StartMonth,
1277 : int const StartMonthDay,
1278 : int const EndMonth,
1279 : int const EndMonthDay,
1280 : bool const Rollover,
1281 : bool const MidSimReset)
1282 : {
1283 :
1284 : // SUBROUTINE INFORMATION:
1285 : // AUTHOR Linda Lawrie
1286 : // DATE WRITTEN March 2012
1287 :
1288 : // PURPOSE OF THIS SUBROUTINE:
1289 : // This subroutine resets the weekday for each month based on the current weekday
1290 : // and previous weekdays per month.
1291 :
1292 0 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1293 :
1294 0 : Array1D_int WeekDaysCopy(12);
1295 : int CurWeekDay;
1296 :
1297 0 : WeekDaysCopy = WeekDays;
1298 0 : if (!MidSimReset) {
1299 0 : if (Rollover) {
1300 0 : if (StartMonth == 1) {
1301 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1302 : } else {
1303 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1304 : }
1305 : } else { // restart at same as before
1306 0 : CurWeekDay = WeekDays(StartMonth);
1307 : }
1308 0 : while (CurWeekDay > 7) {
1309 0 : CurWeekDay -= 7;
1310 : }
1311 :
1312 0 : WeekDays = 0;
1313 0 : WeekDays(StartMonth) = CurWeekDay;
1314 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1315 0 : if (i == 2) {
1316 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1317 0 : while (CurWeekDay > 7) {
1318 0 : CurWeekDay -= 7;
1319 : }
1320 0 : WeekDays(i) = CurWeekDay;
1321 0 : } else if (i == 3) {
1322 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1323 0 : while (CurWeekDay > 7) {
1324 0 : CurWeekDay -= 7;
1325 : }
1326 0 : WeekDays(i) = CurWeekDay;
1327 0 : } else if ((i >= 4) && (i <= 12)) {
1328 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1329 0 : while (CurWeekDay > 7) {
1330 0 : CurWeekDay -= 7;
1331 : }
1332 0 : WeekDays(i) = CurWeekDay;
1333 : }
1334 : }
1335 :
1336 0 : if (any_eq(WeekDays, 0)) {
1337 : // need to start at StMon and go backwards.
1338 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1339 0 : CurWeekDay = WeekDays(StartMonth);
1340 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1341 0 : --CurWeekDay;
1342 0 : if (CurWeekDay == 0) CurWeekDay = 7;
1343 : }
1344 :
1345 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1346 :
1347 0 : if (i == 1) {
1348 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1349 0 : while (CurWeekDay <= 0) {
1350 0 : CurWeekDay += 7;
1351 : }
1352 0 : WeekDays(i) = CurWeekDay;
1353 0 : } else if (i == 2) {
1354 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1355 0 : while (CurWeekDay <= 0) {
1356 0 : CurWeekDay += 7;
1357 : }
1358 0 : WeekDays(i) = CurWeekDay;
1359 0 : } else if ((i >= 3) && (i <= 12)) {
1360 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1361 0 : while (CurWeekDay <= 0) {
1362 0 : CurWeekDay += 7;
1363 : }
1364 0 : WeekDays(i) = CurWeekDay;
1365 : }
1366 : }
1367 : }
1368 :
1369 : } else {
1370 0 : if (Rollover) {
1371 0 : if (StartMonth == 1) {
1372 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1373 : } else {
1374 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1375 : }
1376 : } else { // restart at same as before
1377 0 : CurWeekDay = WeekDays(StartMonth);
1378 : }
1379 0 : while (CurWeekDay > 7) {
1380 0 : CurWeekDay -= 7;
1381 : }
1382 0 : WeekDays = 0;
1383 0 : if (StartMonth != 1) {
1384 0 : CurWeekDay = WeekDaysCopy(12) + state.dataWeather->EndDayOfMonth(12);
1385 0 : while (CurWeekDay > 7) {
1386 0 : CurWeekDay -= 7;
1387 : }
1388 0 : WeekDays(1) = CurWeekDay;
1389 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1390 0 : while (CurWeekDay > 7) {
1391 0 : CurWeekDay -= 7;
1392 : }
1393 0 : WeekDays(2) = CurWeekDay;
1394 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1395 0 : while (CurWeekDay > 7) {
1396 0 : CurWeekDay -= 7;
1397 : }
1398 0 : WeekDays(3) = CurWeekDay;
1399 0 : for (int i = 4; i <= 12; ++i) {
1400 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1401 0 : while (CurWeekDay > 7) {
1402 0 : CurWeekDay -= 7;
1403 : }
1404 0 : WeekDays(i) = CurWeekDay;
1405 : }
1406 : } else {
1407 0 : WeekDays = 0;
1408 0 : WeekDays(StartMonth) = CurWeekDay;
1409 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1410 0 : if (i == 2) {
1411 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1412 0 : while (CurWeekDay > 7) {
1413 0 : CurWeekDay -= 7;
1414 : }
1415 0 : WeekDays(i) = CurWeekDay;
1416 0 : } else if (i == 3) {
1417 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1418 0 : while (CurWeekDay > 7) {
1419 0 : CurWeekDay -= 7;
1420 : }
1421 0 : WeekDays(i) = CurWeekDay;
1422 0 : } else if ((i >= 4) && (i <= 12)) {
1423 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1424 0 : while (CurWeekDay > 7) {
1425 0 : CurWeekDay -= 7;
1426 : }
1427 0 : WeekDays(i) = CurWeekDay;
1428 : }
1429 : }
1430 :
1431 0 : if (any_eq(WeekDays, 0)) {
1432 : // need to start at StMon and go backwards.
1433 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1434 0 : CurWeekDay = WeekDays(StartMonth);
1435 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1436 0 : --CurWeekDay;
1437 0 : if (CurWeekDay == 0) CurWeekDay = 7;
1438 : }
1439 :
1440 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1441 :
1442 0 : if (i == 1) {
1443 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1444 0 : while (CurWeekDay <= 0) {
1445 0 : CurWeekDay += 7;
1446 : }
1447 0 : WeekDays(i) = CurWeekDay;
1448 0 : } else if (i == 2) {
1449 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1450 0 : while (CurWeekDay <= 0) {
1451 0 : CurWeekDay += 7;
1452 : }
1453 0 : WeekDays(i) = CurWeekDay;
1454 0 : } else if ((i >= 3) && (i <= 12)) {
1455 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1456 0 : while (CurWeekDay <= 0) {
1457 0 : CurWeekDay += 7;
1458 : }
1459 0 : WeekDays(i) = CurWeekDay;
1460 : }
1461 : }
1462 : }
1463 : }
1464 : }
1465 0 : }
1466 :
1467 3 : void SetDSTDateRanges(EnergyPlusData &state,
1468 : Array1D_int const &MonWeekDay, // Weekday of each day 1 of month
1469 : Array1D_int &DSTIdx, // DST Index for each julian day (1:366)
1470 : ObjexxFCL::Optional_int DSTActStMon,
1471 : ObjexxFCL::Optional_int DSTActStDay,
1472 : ObjexxFCL::Optional_int DSTActEnMon,
1473 : ObjexxFCL::Optional_int DSTActEnDay)
1474 : {
1475 :
1476 : // SUBROUTINE INFORMATION:
1477 : // AUTHOR Linda Lawrie
1478 : // DATE WRITTEN March 2012
1479 :
1480 : // PURPOSE OF THIS SUBROUTINE:
1481 : // With multiple year weather files (or repeating weather files that rollover day),
1482 : // need to set DST (Daylight Saving Time) dates at start of environment or year.
1483 : // DST is only projected for one year.
1484 :
1485 : static constexpr std::string_view RoutineName("SetDSTDateRanges: ");
1486 :
1487 : int ActStartMonth; // Actual Start Month
1488 : int ActStartDay; // Actual Start Day of Month
1489 : int ActEndMonth; // Actual End Month
1490 : int ActEndDay; // Actual End Day of Month
1491 :
1492 3 : bool ErrorsFound = false;
1493 3 : if (state.dataWeather->DST.StDateType == DateType::MonthDay) {
1494 0 : ActStartMonth = state.dataWeather->DST.StMon;
1495 0 : ActStartDay = state.dataWeather->DST.StDay;
1496 3 : } else if (state.dataWeather->DST.StDateType == DateType::NthDayInMonth) {
1497 3 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1498 6 : while (ThisDay <= 0) {
1499 3 : ThisDay += 7;
1500 : }
1501 3 : ThisDay += 7 * (state.dataWeather->DST.StDay - 1);
1502 3 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1503 0 : ShowSevereError(state, format("{}Determining DST: DST Start Date, Nth Day of Month, not enough Nths", RoutineName));
1504 0 : ErrorsFound = true;
1505 : } else {
1506 3 : ActStartMonth = state.dataWeather->DST.StMon;
1507 3 : ActStartDay = ThisDay;
1508 : }
1509 : } else { // LastWeekDayInMonth
1510 0 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1511 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1512 0 : ThisDay += 7;
1513 : }
1514 0 : ActStartMonth = state.dataWeather->DST.StMon;
1515 0 : ActStartDay = ThisDay;
1516 : }
1517 :
1518 3 : if (state.dataWeather->DST.EnDateType == DateType::MonthDay) {
1519 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1520 0 : ActEndDay = state.dataWeather->DST.EnDay;
1521 3 : } else if (state.dataWeather->DST.EnDateType == DateType::NthDayInMonth) {
1522 3 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1523 6 : while (ThisDay <= 0) {
1524 3 : ThisDay += 7;
1525 : }
1526 3 : ThisDay += 7 * (state.dataWeather->DST.EnDay - 1);
1527 3 : if (ThisDay >> state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1528 0 : ActEndMonth = 0; // Suppress uninitialized warning
1529 0 : ActEndDay = 0; // Suppress uninitialized warning
1530 0 : ShowSevereError(state, format("{}Determining DST: DST End Date, Nth Day of Month, not enough Nths", RoutineName));
1531 0 : ErrorsFound = true;
1532 : } else {
1533 3 : ActEndMonth = state.dataWeather->DST.EnMon;
1534 3 : ActEndDay = ThisDay;
1535 : }
1536 : } else { // LastWeekDayInMonth
1537 0 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1538 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1539 0 : ThisDay += 7;
1540 : }
1541 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1542 0 : ActEndDay = ThisDay;
1543 : }
1544 :
1545 3 : if (ErrorsFound) {
1546 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1547 : }
1548 :
1549 3 : if (present(DSTActStMon)) {
1550 2 : DSTActStMon = ActStartMonth;
1551 2 : DSTActStDay = ActStartDay;
1552 2 : DSTActEnMon = ActEndMonth;
1553 2 : DSTActEnDay = ActEndDay;
1554 : }
1555 :
1556 3 : DSTIdx = 0;
1557 3 : int JDay = General::OrdinalDay(ActStartMonth, ActStartDay, state.dataWeather->LeapYearAdd);
1558 3 : int JDay1 = General::OrdinalDay(ActEndMonth, ActEndDay, state.dataWeather->LeapYearAdd);
1559 3 : if (JDay1 >= JDay) {
1560 3 : DSTIdx({JDay, JDay1}) = 1;
1561 : } else {
1562 0 : DSTIdx({JDay, 366}) = 1;
1563 0 : DSTIdx({1, JDay1}) = 1;
1564 : }
1565 3 : }
1566 :
1567 40 : void SetSpecialDayDates(EnergyPlusData &state, Array1D_int const &MonWeekDay) // Weekday of each day 1 of month
1568 : {
1569 :
1570 : // SUBROUTINE INFORMATION:
1571 : // AUTHOR Linda Lawrie
1572 : // DATE WRITTEN March 2012
1573 :
1574 : // PURPOSE OF THIS SUBROUTINE:
1575 : // With multiple year weather files (or repeating weather files that rollover day),
1576 : // need to set Special Day dates at start of environment or year.
1577 : // Special Days are only projected for one year.
1578 :
1579 : static constexpr std::string_view RoutineName("SetSpecialDayDates: ");
1580 :
1581 : int JDay;
1582 :
1583 40 : bool ErrorsFound = false;
1584 40 : state.dataWeather->SpecialDayTypes = 0;
1585 103 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1586 63 : auto &specialDay = state.dataWeather->SpecialDays(i);
1587 63 : if (specialDay.WthrFile && !state.dataWeather->UseSpecialDays) continue;
1588 63 : if (specialDay.dateType <= DateType::MonthDay) {
1589 30 : JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd);
1590 30 : if (specialDay.Duration == 1 && state.dataWeather->Environment(state.dataWeather->Envrn).ApplyWeekendRule) {
1591 18 : if (state.dataWeather->WeekDayTypes(JDay) == static_cast<int>(ScheduleManager::DayType::Sunday)) {
1592 : // Sunday, must go to Monday
1593 6 : ++JDay;
1594 6 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) JDay = 1;
1595 12 : } else if (state.dataWeather->WeekDayTypes(JDay) == (int)ScheduleManager::DayType::Saturday) {
1596 0 : ++JDay;
1597 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) JDay = 1;
1598 0 : ++JDay;
1599 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) JDay = 1;
1600 : }
1601 : }
1602 30 : General::InvOrdinalDay(JDay, specialDay.ActStMon, specialDay.ActStDay, state.dataWeather->LeapYearAdd);
1603 33 : } else if (specialDay.dateType == DateType::NthDayInMonth) {
1604 27 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1605 27 : if (specialDay.WeekDay < MonWeekDay(specialDay.Month)) {
1606 18 : ThisDay += 7;
1607 : }
1608 27 : ThisDay += 7 * (specialDay.Day - 1);
1609 27 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1610 0 : ShowSevereError(state,
1611 0 : format("{}Special Day Date, Nth Day of Month, not enough Nths, for SpecialDay={}", RoutineName, specialDay.Name));
1612 0 : ErrorsFound = true;
1613 0 : continue;
1614 : }
1615 27 : specialDay.ActStMon = specialDay.Month;
1616 27 : specialDay.ActStDay = ThisDay;
1617 27 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1618 : } else { // LastWeekDayInMonth
1619 6 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1620 33 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1621 27 : ThisDay += 7;
1622 : }
1623 6 : specialDay.ActStMon = specialDay.Month;
1624 6 : specialDay.ActStDay = ThisDay;
1625 6 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1626 : }
1627 63 : if (state.dataWeather->SpecialDayTypes(JDay) != 0) {
1628 0 : ShowWarningError(
1629 : state,
1630 0 : format("{}Special Day definition ({}) is overwriting previously entered special day period", RoutineName, specialDay.Name));
1631 0 : if (state.dataWeather->UseSpecialDays) {
1632 0 : ShowContinueError(state, "...This could be caused by definitions on the Weather File.");
1633 : }
1634 0 : ShowContinueError(state, "...This could be caused by duplicate definitions in the Input File.");
1635 : }
1636 63 : int JDay1 = JDay - 1;
1637 126 : for (int j = 0; j <= specialDay.Duration - 1; ++j) {
1638 63 : ++JDay1;
1639 63 : if (JDay1 == 366 && state.dataWeather->LeapYearAdd == 0) JDay1 = 1;
1640 63 : if (JDay1 == 367) JDay1 = 1;
1641 63 : state.dataWeather->SpecialDayTypes(JDay1) = specialDay.DayType;
1642 : }
1643 : }
1644 :
1645 40 : if (ErrorsFound) {
1646 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1647 : }
1648 40 : }
1649 :
1650 2900842 : void InitializeWeather(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
1651 : {
1652 :
1653 : // SUBROUTINE INFORMATION:
1654 : // AUTHOR Rick Strand
1655 : // DATE WRITTEN June 1997
1656 :
1657 : // PURPOSE OF THIS SUBROUTINE:
1658 : // This subroutine is the main driver of the weather initializations.
1659 : // Most of the weather handling can be described as "initializations"
1660 : // so most of the work is done via this subroutine.
1661 :
1662 2900842 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->FirstCall) {
1663 :
1664 796 : state.dataWeather->FirstCall = false;
1665 796 : state.dataEnvrn->EndMonthFlag = false;
1666 :
1667 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
1668 :
1669 2900842 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
1670 2900842 : if (state.dataGlobal->BeginEnvrnFlag) {
1671 :
1672 : // Call and setup the Design Day environment
1673 7158 : if (envCurr.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
1674 5955 : if (envCurr.DesignDayNum > 0) {
1675 5932 : SetUpDesignDay(state, envCurr.DesignDayNum);
1676 5932 : state.dataEnvrn->EnvironmentName = envCurr.Title;
1677 : }
1678 : }
1679 :
1680 : // Only used in Weather file environments
1681 : // Start over missing values with each environment
1682 7158 : state.dataWeather->wvarsMissing.OutBaroPress = state.dataEnvrn->StdBaroPress; // Initial "missing" value
1683 7158 : state.dataWeather->wvarsMissing.OutDryBulbTemp = 6.0; // Initial "missing" value
1684 7158 : state.dataWeather->wvarsMissing.OutDewPointTemp = 3.0; // Initial "missing" value
1685 7158 : state.dataWeather->wvarsMissing.OutRelHum = 50.0; // Initial "missing" value
1686 7158 : state.dataWeather->wvarsMissing.WindSpeed = 2.5; // Initial "missing" value
1687 7158 : state.dataWeather->wvarsMissing.WindDir = 180; // Initial "missing" value
1688 7158 : state.dataWeather->wvarsMissing.TotalSkyCover = 5; // Initial "missing" value
1689 7158 : state.dataWeather->wvarsMissing.OpaqueSkyCover = 5; // Initial "missing" value
1690 7158 : state.dataWeather->wvarsMissing.Visibility = 777.7; // Initial "missing" value
1691 7158 : state.dataWeather->wvarsMissing.Ceiling = 77777; // Initial "missing" value
1692 7158 : state.dataWeather->wvarsMissing.AerOptDepth = 0.0; // Initial "missing" value
1693 7158 : state.dataWeather->wvarsMissing.SnowDepth = 0; // Initial "missing" value
1694 7158 : state.dataWeather->wvarsMissing.DaysLastSnow = 88; // Initial "missing" value
1695 7158 : state.dataWeather->wvarsMissing.Albedo = 0.0; // Initial "missing" value
1696 7158 : state.dataWeather->wvarsMissing.LiquidPrecip = 0.0; // Initial "missing" value
1697 : // Counts set to 0 for each environment
1698 7158 : state.dataWeather->wvarsMissedCounts = Weather::WeatherVarCounts();
1699 :
1700 : // Counts set to 0 for each environment
1701 7158 : state.dataWeather->wvarsOutOfRangeCounts = Weather::WeatherVarCounts();
1702 :
1703 7158 : state.dataWeather->IsRainThreshold = 0.8 / double(state.dataGlobal->NumOfTimeStepInHour); // [mm]
1704 :
1705 7158 : if (!state.dataWeather->RPReadAllWeatherData) {
1706 7157 : printEnvrnStamp = true; // Set this to true so that on first non-warmup day (only) the environment header will print out
1707 : }
1708 :
1709 23296 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1710 16138 : state.dataWeather->SpecialDays(i).Used = false;
1711 : }
1712 :
1713 8464 : if ((state.dataGlobal->KindOfSim != Constant::KindOfSim::DesignDay) &&
1714 1306 : (state.dataGlobal->KindOfSim != Constant::KindOfSim::HVACSizeDesignDay)) {
1715 1226 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read first day's weather
1716 : } else {
1717 5932 : state.dataWeather->TomorrowVariables = state.dataWeather->DesignDay(envCurr.DesignDayNum);
1718 : }
1719 :
1720 : } // ... end of DataGlobals::BeginEnvrnFlag IF-THEN block.
1721 :
1722 2900842 : if (state.dataGlobal->BeginDayFlag) {
1723 :
1724 : // Check Holidays, Daylight Saving Time, Ground Temperatures, etc.
1725 :
1726 26502 : UpdateWeatherData(state); // Update daily weather info
1727 :
1728 : // Read tomorrow's weather only if necessary. This means that the
1729 : // simulation is out of warmup, is using a weather tape for this
1730 : // environment, and is not on the last day (day after last day is
1731 : // assumed to be equal to last day).
1732 :
1733 : // Following code checks whether the present day of simulation matches the start month and start day.
1734 : // In a multi year simulation with run period less than 365, we need to position the weather line
1735 : // appropriately.
1736 :
1737 32467 : if ((!state.dataGlobal->WarmupFlag) &&
1738 5965 : ((envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) && (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay))) {
1739 2646 : if (state.dataGlobal->DayOfSim < state.dataGlobal->NumOfDayInEnvrn) {
1740 2627 : if (state.dataGlobal->DayOfSim == state.dataWeather->curSimDayForEndOfRunPeriod) {
1741 0 : state.dataWeather->curSimDayForEndOfRunPeriod += envCurr.RawSimDays;
1742 0 : if (state.dataWeather->StartDatesCycleShouldBeReset) {
1743 0 : ResetWeekDaysByMonth(state,
1744 0 : envCurr.MonWeekDay,
1745 0 : state.dataWeather->LeapYearAdd,
1746 : envCurr.StartMonth,
1747 : envCurr.StartDay,
1748 : envCurr.EndMonth,
1749 : envCurr.EndDay,
1750 0 : envCurr.RollDayTypeOnRepeat);
1751 0 : if (state.dataWeather->DaylightSavingIsActive) {
1752 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1753 : }
1754 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1755 : }
1756 0 : ++state.dataWeather->YearOfSim;
1757 0 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1758 : } else {
1759 2627 : ReadWeatherForDay(state, state.dataGlobal->DayOfSim + 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1760 : }
1761 : }
1762 : }
1763 :
1764 26502 : state.dataEnvrn->EndYearFlag = false;
1765 26502 : if (state.dataEnvrn->DayOfMonth == state.dataWeather->EndDayOfMonthWithLeapDay(state.dataEnvrn->Month)) {
1766 91 : state.dataEnvrn->EndMonthFlag = true;
1767 91 : state.dataEnvrn->EndYearFlag = (state.dataEnvrn->Month == 12);
1768 : }
1769 :
1770 : // Set Tomorrow's date data
1771 26502 : state.dataEnvrn->MonthTomorrow = state.dataWeather->TomorrowVariables.Month;
1772 26502 : state.dataEnvrn->DayOfMonthTomorrow = state.dataWeather->TomorrowVariables.DayOfMonth;
1773 26502 : state.dataEnvrn->DayOfWeekTomorrow = state.dataWeather->TomorrowVariables.DayOfWeek;
1774 26502 : state.dataEnvrn->HolidayIndexTomorrow = state.dataWeather->TomorrowVariables.HolidayIndex;
1775 26502 : state.dataEnvrn->YearTomorrow = state.dataWeather->TomorrowVariables.Year;
1776 :
1777 26502 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1778 3435 : if (state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1 && envCurr.ActualWeather) {
1779 0 : if (state.dataWeather->DatesShouldBeReset) {
1780 0 : if (envCurr.TreatYearsAsConsecutive) {
1781 0 : ++envCurr.CurrentYear;
1782 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1783 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1784 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1785 :
1786 : // need to reset MonWeekDay and WeekDayTypes
1787 0 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1788 0 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1789 0 : if (!envCurr.ActualWeather)
1790 0 : state.dataWeather->curSimDayForEndOfRunPeriod =
1791 0 : state.dataGlobal->DayOfSim + envCurr.RawSimDays + state.dataWeather->LeapYearAdd - 1;
1792 :
1793 : {
1794 0 : int i = JDay5Start;
1795 0 : int TWeekDay = state.dataEnvrn->DayOfWeek;
1796 : while (true) {
1797 0 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1798 0 : TWeekDay = mod(TWeekDay, 7) + 1;
1799 0 : ++i;
1800 0 : if (i > 366) i = 1;
1801 0 : if (i == JDay5End) break;
1802 : }
1803 : }
1804 0 : ResetWeekDaysByMonth(state,
1805 0 : envCurr.MonWeekDay,
1806 0 : state.dataWeather->LeapYearAdd,
1807 : envCurr.StartMonth,
1808 : envCurr.StartDay,
1809 : envCurr.EndMonth,
1810 : envCurr.EndDay,
1811 0 : envCurr.RollDayTypeOnRepeat);
1812 0 : if (state.dataWeather->DaylightSavingIsActive) {
1813 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1814 : }
1815 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1816 : }
1817 : }
1818 3435 : } else if ((state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1) && state.dataWeather->DatesShouldBeReset &&
1819 0 : (state.dataWeather->Jan1DatesShouldBeReset)) {
1820 0 : if (envCurr.TreatYearsAsConsecutive) {
1821 0 : ++envCurr.CurrentYear;
1822 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1823 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1824 0 : if (state.dataEnvrn->CurrentYearIsLeapYear && !state.dataWeather->WFAllowsLeapYears)
1825 0 : state.dataEnvrn->CurrentYearIsLeapYear = false;
1826 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod && state.dataEnvrn->CurrentYearIsLeapYear)
1827 0 : ++state.dataWeather->curSimDayForEndOfRunPeriod;
1828 : }
1829 :
1830 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1831 :
1832 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod) {
1833 0 : ResetWeekDaysByMonth(state,
1834 0 : envCurr.MonWeekDay,
1835 0 : state.dataWeather->LeapYearAdd,
1836 : envCurr.StartMonth,
1837 : envCurr.StartDay,
1838 : envCurr.EndMonth,
1839 : envCurr.EndDay,
1840 0 : envCurr.RollDayTypeOnRepeat,
1841 0 : envCurr.RollDayTypeOnRepeat || state.dataEnvrn->CurrentYearIsLeapYear);
1842 0 : if (state.dataWeather->DaylightSavingIsActive) {
1843 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1844 : }
1845 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1846 : }
1847 : }
1848 : }
1849 :
1850 : // at the end of each day find the min/max weather used for DOAS sizing
1851 26502 : if (state.dataGlobal->AirLoopHVACDOASUsedInSim) {
1852 35 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign || envCurr.KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1853 850 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr) {
1854 5712 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS) {
1855 4896 : Real64 Tdb = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDryBulbTemp;
1856 4896 : Real64 Tdp = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDewPointTemp;
1857 4896 : if (Tdb > envCurr.maxCoolingOATSizing) {
1858 42 : envCurr.maxCoolingOATSizing = Tdb;
1859 42 : envCurr.maxCoolingOADPSizing = Tdp;
1860 : }
1861 4896 : if (Tdb < envCurr.minHeatingOATSizing) {
1862 31 : envCurr.minHeatingOATSizing = Tdb;
1863 31 : envCurr.minHeatingOADPSizing = Tdp;
1864 : }
1865 : } // for (iTS)
1866 : } // for (iHr)
1867 : }
1868 : }
1869 :
1870 : } // ... end of DataGlobals::BeginDayFlag IF-THEN block.
1871 :
1872 5775182 : if (!state.dataGlobal->BeginDayFlag && !state.dataGlobal->WarmupFlag &&
1873 691979 : (state.dataEnvrn->Month != envCurr.StartMonth || state.dataEnvrn->DayOfMonth != envCurr.StartDay) &&
1874 5775182 : !state.dataWeather->DatesShouldBeReset && envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1875 6 : state.dataWeather->DatesShouldBeReset = true;
1876 : }
1877 :
1878 2902148 : if (state.dataGlobal->EndEnvrnFlag && (envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
1879 1306 : (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
1880 1226 : state.files.inputWeatherFile.rewind();
1881 1226 : SkipEPlusWFHeader(state);
1882 1226 : ReportMissing_RangeData(state);
1883 : }
1884 :
1885 : // set the EndDesignDayEnvrnsFlag (dataGlobal)
1886 : // 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)
1887 2900842 : state.dataGlobal->EndDesignDayEnvrnsFlag = false;
1888 2900842 : if (state.dataGlobal->EndEnvrnFlag) {
1889 7157 : if (state.dataWeather->Envrn < state.dataWeather->NumOfEnvrn) {
1890 6301 : if (envCurr.KindOfEnvrn != state.dataWeather->Environment(state.dataWeather->Envrn + 1).KindOfEnvrn) {
1891 2792 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1892 : }
1893 : } else {
1894 : // if the last environment set the flag to true.
1895 856 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1896 : }
1897 : }
1898 :
1899 2900842 : if (state.dataWeather->WaterMainsParameterReport) {
1900 : // this is done only once
1901 796 : if (state.dataWeather->WaterMainsTempsMethod == WaterMainsTempCalcMethod::CorrelationFromWeatherFile) {
1902 0 : if (!state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
1903 0 : state.dataWeather->OADryBulbAverage.CalcAnnualAndMonthlyDryBulbTemp(state);
1904 : }
1905 : }
1906 : // reports to eio file
1907 796 : ReportWaterMainsTempParameters(state);
1908 796 : state.dataWeather->WaterMainsParameterReport = false;
1909 : }
1910 2900842 : }
1911 :
1912 26502 : void UpdateWeatherData(EnergyPlusData &state)
1913 : {
1914 :
1915 : // SUBROUTINE INFORMATION:
1916 : // AUTHOR Rick Strand
1917 : // DATE WRITTEN June 1997
1918 :
1919 : // PURPOSE OF THIS SUBROUTINE:
1920 : // This subroutine updates all of the daily weather data in the local
1921 : // module level variables and the global variables.
1922 : // This subroutine will temporarily transfer the weather data for the
1923 : // current day to the old data structure contained in envdat.inc until
1924 : // enough reengineering has taken place to eliminate the need for this
1925 : // include.
1926 :
1927 26502 : state.dataWeather->TodayVariables = state.dataWeather->TomorrowVariables; // Transfer Tomorrow's Daily Weather Variables to Today
1928 :
1929 26502 : if (state.dataGlobal->BeginEnvrnFlag) {
1930 7158 : state.dataGlobal->PreviousHour = 24;
1931 : }
1932 :
1933 26502 : state.dataWeather->wvarsHrTsToday = state.dataWeather->wvarsHrTsTomorrow; // What a waste
1934 :
1935 : // Update Global Data
1936 :
1937 26502 : state.dataEnvrn->DayOfYear = state.dataWeather->TodayVariables.DayOfYear;
1938 26502 : state.dataEnvrn->Year = state.dataWeather->TodayVariables.Year;
1939 26502 : state.dataEnvrn->Month = state.dataWeather->TodayVariables.Month;
1940 26502 : state.dataEnvrn->DayOfMonth = state.dataWeather->TodayVariables.DayOfMonth;
1941 26502 : state.dataEnvrn->DayOfWeek = state.dataWeather->TodayVariables.DayOfWeek;
1942 26502 : state.dataEnvrn->HolidayIndex = state.dataWeather->TodayVariables.HolidayIndex;
1943 26502 : if (state.dataEnvrn->HolidayIndex > 0) {
1944 22375 : state.dataWeather->RptDayType = state.dataEnvrn->HolidayIndex;
1945 : } else {
1946 4127 : state.dataWeather->RptDayType = state.dataEnvrn->DayOfWeek;
1947 : }
1948 26502 : state.dataEnvrn->DSTIndicator = state.dataWeather->TodayVariables.DaylightSavingIndex;
1949 26502 : state.dataEnvrn->EquationOfTime = state.dataWeather->TodayVariables.EquationOfTime;
1950 26502 : state.dataEnvrn->CosSolarDeclinAngle = state.dataWeather->TodayVariables.CosSolarDeclinAngle;
1951 26502 : state.dataEnvrn->SinSolarDeclinAngle = state.dataWeather->TodayVariables.SinSolarDeclinAngle;
1952 26502 : }
1953 :
1954 2900842 : void SetCurrentWeather(EnergyPlusData &state)
1955 : {
1956 :
1957 : // SUBROUTINE INFORMATION:
1958 : // AUTHOR Russ Taylor
1959 : // DATE WRITTEN March 1990
1960 : // MODIFIED Aug94 (LKL) Fixed improper weighting
1961 : // Nov98 (FCW) Added call to get exterior illuminances
1962 : // Jan02 (FCW) Changed how ground reflectance for daylighting is set
1963 : // Mar12 (LKL) Changed settings for leap years/ current years.
1964 : // RE-ENGINEERED Apr97,May97 (RKS)
1965 :
1966 : // PURPOSE OF THIS SUBROUTINE:
1967 : // The purpose of this subroutine is to interpolate the hourly
1968 : // environment data for the sub-hourly time steps in EnergyPlus. In
1969 : // other words, this subroutine puts the current weather conditions
1970 : // into the proper variables. Rather than using the same data for
1971 : // each time step, environment data is interpolated as a continuum
1972 : // throughout the day.
1973 :
1974 : // METHODOLOGY EMPLOYED:
1975 : // The current hour (DataGlobals::HourOfDay) as well as the next hour are used
1976 : // to come up with environment data per time step interval. Method
1977 : // used is to assign a weighting for the current hour's data and
1978 : // (1-that weighting) to the next hour's data. Actual method is: if
1979 : // the current time step is 15 minutes into hour, the interpolated dry
1980 : // bulb temperature should be 3/4*dry bulb temperature of current hour
1981 : // and 1/4*dry bulb temperature of next environment hourly data. At
1982 : // day boundary (current hour = 24), the next hour is hour 1 of next
1983 : // weather data day (Tomorrow%).
1984 :
1985 : static constexpr std::string_view RoutineName("SetCurrentWeather");
1986 :
1987 2900842 : state.dataWeather->NextHour = state.dataGlobal->HourOfDay + 1;
1988 :
1989 2900842 : if (state.dataGlobal->HourOfDay == 24) { // Should investigate whether EndDayFlag is always set here and use that instead
1990 124210 : state.dataWeather->NextHour = 1;
1991 : }
1992 :
1993 2900842 : if (state.dataGlobal->HourOfDay == 1) { // Should investigate whether DataGlobals::BeginDayFlag is always set here and use that instead
1994 128030 : state.dataEnvrn->DayOfYear_Schedule = General::OrdinalDay(state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, 1);
1995 : }
1996 :
1997 2900842 : ScheduleManager::UpdateScheduleValues(state);
1998 :
1999 2900842 : state.dataEnvrn->CurMnDyHr =
2000 5801684 : format("{:02d}/{:02d} {:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, (unsigned short)(state.dataGlobal->HourOfDay - 1));
2001 2900842 : state.dataEnvrn->CurMnDy = format("{:02d}/{:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth);
2002 2900842 : state.dataEnvrn->CurMnDyYr =
2003 5801684 : format("{:02d}/{:02d}/{:04d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->CalendarYear);
2004 :
2005 2900842 : state.dataGlobal->WeightNow = state.dataWeather->Interpolation(state.dataGlobal->TimeStep);
2006 2900842 : state.dataGlobal->WeightPreviousHour = 1.0 - state.dataGlobal->WeightNow;
2007 :
2008 2900842 : state.dataGlobal->CurrentTime = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * (state.dataWeather->TimeStepFraction);
2009 2900842 : state.dataGlobal->SimTimeSteps = (state.dataGlobal->DayOfSim - 1) * 24 * state.dataGlobal->NumOfTimeStepInHour +
2010 2900842 : (state.dataGlobal->HourOfDay - 1) * state.dataGlobal->NumOfTimeStepInHour + state.dataGlobal->TimeStep;
2011 :
2012 2900842 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] =
2013 2900842 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2014 2900842 : state.dataEnvrn->GroundTempKelvin = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] + Constant::Kelvin;
2015 2900842 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod] =
2016 2900842 : state.dataWeather->siteFCFactorMethodGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2017 2900842 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow] =
2018 2900842 : state.dataWeather->siteShallowGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2019 2900842 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep] =
2020 2900842 : state.dataWeather->siteDeepGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2021 2900842 : state.dataEnvrn->GndReflectance = state.dataWeather->GroundReflectances(state.dataEnvrn->Month);
2022 2900842 : state.dataEnvrn->GndReflectanceForDayltg = state.dataEnvrn->GndReflectance;
2023 :
2024 2900842 : CalcWaterMainsTemp(state);
2025 :
2026 : // Determine if Sun is up or down, set Solar Cosine values for time step.
2027 2900842 : DetermineSunUpDown(state, state.dataEnvrn->SOLCOS);
2028 2900842 : if (state.dataEnvrn->SunIsUp && state.dataWeather->SolarAltitudeAngle < 0.0) {
2029 0 : ShowFatalError(state, format("SetCurrentWeather: At {} Sun is Up but Solar Altitude Angle is < 0.0", state.dataEnvrn->CurMnDyHr));
2030 : }
2031 :
2032 2900842 : auto const &today = state.dataWeather->wvarsHrTsToday(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2033 2900842 : state.dataEnvrn->OutDryBulbTemp = today.OutDryBulbTemp;
2034 2900842 : if (state.dataEnvrn->EMSOutDryBulbOverrideOn) state.dataEnvrn->OutDryBulbTemp = state.dataEnvrn->EMSOutDryBulbOverrideValue;
2035 2900842 : state.dataEnvrn->OutBaroPress = today.OutBaroPress;
2036 2900842 : state.dataEnvrn->OutDewPointTemp = today.OutDewPointTemp;
2037 2900842 : if (state.dataEnvrn->EMSOutDewPointTempOverrideOn) state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->EMSOutDewPointTempOverrideValue;
2038 2900842 : state.dataEnvrn->OutRelHum = today.OutRelHum;
2039 2900842 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->OutRelHum / 100.0;
2040 2900842 : if (state.dataEnvrn->EMSOutRelHumOverrideOn) {
2041 0 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->EMSOutRelHumOverrideValue / 100.0;
2042 0 : state.dataEnvrn->OutRelHum = state.dataEnvrn->EMSOutRelHumOverrideValue;
2043 : }
2044 :
2045 : // Humidity Ratio and Wet Bulb are derived
2046 2900842 : state.dataEnvrn->OutHumRat = Psychrometrics::PsyWFnTdbRhPb(
2047 2900842 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutRelHumValue, state.dataEnvrn->OutBaroPress, RoutineName);
2048 5801684 : state.dataEnvrn->OutWetBulbTemp =
2049 2900842 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat, state.dataEnvrn->OutBaroPress);
2050 2900842 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
2051 950305 : state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2052 1900610 : Real64 TempVal = Psychrometrics::PsyWFnTdbTwbPb(
2053 950305 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutBaroPress);
2054 950305 : state.dataEnvrn->OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, TempVal, state.dataEnvrn->OutBaroPress);
2055 : }
2056 :
2057 2900842 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
2058 993179 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2059 : }
2060 :
2061 3218014 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) ||
2062 317172 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay)) {
2063 :
2064 9262737 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2065 6646340 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2066 : }
2067 :
2068 2616397 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2069 2616397 : int const envrnDayNum = envCurr.DesignDayNum;
2070 2616397 : auto &desDayInput = state.dataWeather->DesDayInput(envrnDayNum);
2071 2616397 : auto &spSiteSchedule = state.dataWeather->spSiteSchedules(envrnDayNum);
2072 2616397 : auto &desDayMod = state.dataWeather->desDayMods(envrnDayNum)(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2073 :
2074 2616397 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Default) {
2075 9234 : spSiteSchedule.OutDryBulbTemp = desDayMod.OutDryBulbTemp;
2076 : }
2077 :
2078 2616397 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
2079 2607622 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
2080 15606 : spSiteSchedule.OutRelHum = desDayMod.OutRelHum;
2081 : }
2082 2616397 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
2083 5289 : spSiteSchedule.BeamSolarRad = desDayMod.BeamSolarRad;
2084 5289 : spSiteSchedule.DifSolarRad = desDayMod.DifSolarRad;
2085 : }
2086 :
2087 2616397 : if (envCurr.skyTempModel == SkyTempModel::ScheduleValue || envCurr.skyTempModel == SkyTempModel::DryBulbDelta ||
2088 2614570 : envCurr.skyTempModel == SkyTempModel::DewPointDelta) {
2089 1827 : spSiteSchedule.SkyTemp = desDayMod.SkyTemp;
2090 : }
2091 284445 : } else if (state.dataEnvrn->TotDesDays > 0) {
2092 853500 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2093 569055 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2094 : }
2095 : }
2096 :
2097 2900842 : state.dataEnvrn->WindSpeed = today.WindSpeed;
2098 2900842 : if (state.dataEnvrn->EMSWindSpeedOverrideOn) state.dataEnvrn->WindSpeed = state.dataEnvrn->EMSWindSpeedOverrideValue;
2099 2900842 : state.dataEnvrn->WindDir = today.WindDir;
2100 2900842 : if (state.dataEnvrn->EMSWindDirOverrideOn) state.dataEnvrn->WindDir = state.dataEnvrn->EMSWindDirOverrideValue;
2101 2900842 : state.dataWeather->HorizIRSky = today.HorizIRSky;
2102 2900842 : state.dataEnvrn->SkyTemp = today.SkyTemp;
2103 2900842 : state.dataEnvrn->SkyTempKelvin = state.dataEnvrn->SkyTemp + Constant::Kelvin;
2104 2900842 : state.dataEnvrn->DifSolarRad = today.DifSolarRad;
2105 2900842 : if (state.dataEnvrn->EMSDifSolarRadOverrideOn) state.dataEnvrn->DifSolarRad = state.dataEnvrn->EMSDifSolarRadOverrideValue;
2106 2900842 : state.dataEnvrn->BeamSolarRad = today.BeamSolarRad;
2107 2900842 : if (state.dataEnvrn->EMSBeamSolarRadOverrideOn) state.dataEnvrn->BeamSolarRad = state.dataEnvrn->EMSBeamSolarRadOverrideValue;
2108 2900842 : state.dataEnvrn->LiquidPrecipitation = today.LiquidPrecip / 1000.0; // convert from mm to m
2109 2900842 : if ((state.dataEnvrn->RunPeriodEnvironment) && (!state.dataGlobal->WarmupFlag)) {
2110 245376 : int month = state.dataEnvrn->Month;
2111 245376 : state.dataWaterData->RainFall.MonthlyTotalPrecInWeather.at(month - 1) += state.dataEnvrn->LiquidPrecipitation * 1000.0;
2112 245376 : if ((state.dataEnvrn->LiquidPrecipitation > 0) && (state.dataGlobal->TimeStep == 1)) {
2113 425 : state.dataWaterData->RainFall.numRainyHoursInWeather.at(month - 1) += 1;
2114 : }
2115 : }
2116 :
2117 2900842 : WaterManager::UpdatePrecipitation(state);
2118 :
2119 2900842 : state.dataEnvrn->TotalCloudCover = today.TotalSkyCover;
2120 2900842 : state.dataEnvrn->OpaqueCloudCover = today.OpaqueSkyCover;
2121 :
2122 2900842 : if (state.dataWeather->UseRainValues) {
2123 : // It is set as LiquidPrecipitation >= .8 mm here: state.dataWeather->TomorrowLiquidPrecip(ts, hour) >=
2124 : // state.dataWeather->IsRainThreshold;
2125 2866795 : state.dataEnvrn->IsRain = today.IsRain;
2126 2866795 : if (state.dataWaterData->RainFall.ModeID == DataWater::RainfallMode::RainSchedDesign && state.dataEnvrn->RunPeriodEnvironment) {
2127 : // CurrentAmount unit: m
2128 0 : state.dataEnvrn->IsRain = state.dataWaterData->RainFall.CurrentAmount >= (state.dataWeather->IsRainThreshold / 1000.0);
2129 : }
2130 : } else {
2131 34047 : state.dataEnvrn->IsRain = false;
2132 : }
2133 2900842 : if (state.dataWeather->UseSnowValues) {
2134 2866795 : state.dataEnvrn->IsSnow = today.IsSnow;
2135 : } else {
2136 34047 : state.dataEnvrn->IsSnow = false;
2137 : }
2138 :
2139 2900842 : if (state.dataEnvrn->IsSnow) {
2140 0 : state.dataEnvrn->GndReflectance = max(min(state.dataEnvrn->GndReflectance * state.dataWeather->SnowGndRefModifier, 1.0), 0.0);
2141 0 : state.dataEnvrn->GndReflectanceForDayltg =
2142 0 : max(min(state.dataEnvrn->GndReflectanceForDayltg * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0);
2143 : }
2144 :
2145 2900842 : state.dataEnvrn->GndSolarRad =
2146 2900842 : max((state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z + state.dataEnvrn->DifSolarRad) * state.dataEnvrn->GndReflectance, 0.0);
2147 :
2148 2900842 : if (!state.dataEnvrn->SunIsUp) {
2149 1450870 : state.dataEnvrn->DifSolarRad = 0.0;
2150 1450870 : state.dataEnvrn->BeamSolarRad = 0.0;
2151 1450870 : state.dataEnvrn->GndSolarRad = 0.0;
2152 : }
2153 :
2154 2900842 : state.dataEnvrn->OutEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2155 5801684 : state.dataEnvrn->OutAirDensity =
2156 2900842 : Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2157 :
2158 2900842 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2159 2900842 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2160 :
2161 2900842 : DayltgCurrentExtHorizIllum(state);
2162 :
2163 2900842 : if (!state.dataEnvrn->IsRain) {
2164 2899514 : state.dataWeather->RptIsRain = 0;
2165 : } else {
2166 1328 : state.dataWeather->RptIsRain = 1;
2167 : }
2168 :
2169 2900842 : if (!state.dataEnvrn->IsSnow) {
2170 2900842 : state.dataWeather->RptIsSnow = 0;
2171 : } else {
2172 0 : state.dataWeather->RptIsSnow = 1;
2173 : }
2174 2900842 : }
2175 :
2176 3853 : void ReadWeatherForDay(EnergyPlusData &state,
2177 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2178 : int const Environ, // Environment being simulated
2179 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2180 : )
2181 : {
2182 :
2183 : // SUBROUTINE INFORMATION:
2184 : // AUTHOR Linda K. Lawrie
2185 : // DATE WRITTEN April 1999
2186 :
2187 : // PURPOSE OF THIS SUBROUTINE:
2188 : // This subroutine is the driving routine behind reading the weather data.
2189 : // Theoretically, several kinds of weather files could be read here. As
2190 : // distributed only EPW files are allowed.
2191 :
2192 3853 : ReadEPlusWeatherForDay(state, DayToRead, Environ, BackSpaceAfterRead);
2193 3853 : }
2194 :
2195 3853 : void ReadEPlusWeatherForDay(EnergyPlusData &state,
2196 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2197 : int const Environ, // Environment being simulated
2198 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2199 : )
2200 : {
2201 :
2202 : // SUBROUTINE INFORMATION:
2203 : // AUTHOR Linda K. Lawrie
2204 : // DATE WRITTEN April 1999
2205 : // MODIFIED March 2012; add actual weather read.
2206 :
2207 : // PURPOSE OF THIS SUBROUTINE:
2208 : // This subroutine reads the appropriate day of EPW weather data.
2209 :
2210 : int WYear;
2211 : int WMonth;
2212 : int WDay;
2213 : int WHour;
2214 : int WMinute;
2215 : Real64 DryBulb;
2216 : Real64 DewPoint;
2217 : Real64 RelHum;
2218 : Real64 AtmPress;
2219 : Real64 ETHoriz;
2220 : Real64 ETDirect;
2221 : Real64 IRHoriz;
2222 : Real64 GLBHoriz;
2223 : Real64 DirectRad;
2224 : Real64 DiffuseRad;
2225 : Real64 GLBHorizIllum;
2226 : Real64 DirectNrmIllum;
2227 : Real64 DiffuseHorizIllum;
2228 : Real64 ZenLum;
2229 : Real64 WindDir;
2230 : Real64 WindSpeed;
2231 : Real64 TotalSkyCover;
2232 : Real64 OpaqueSkyCover;
2233 : Real64 Visibility;
2234 : Real64 CeilHeight;
2235 : Real64 PrecipWater;
2236 : Real64 AerosolOptDepth;
2237 : Real64 SnowDepth;
2238 : Real64 DaysSinceLastSnow;
2239 : Real64 Albedo;
2240 : Real64 LiquidPrecip;
2241 : int PresWeathObs;
2242 3853 : Array1D_int PresWeathConds(9);
2243 :
2244 3853 : constexpr std::string_view routineName = "ReadEPlusWeatherForDay";
2245 :
2246 3853 : Array1D<WeatherVars> wvarsHr = Array1D<WeatherVars>(Constant::HoursInDay);
2247 :
2248 3853 : auto &thisEnviron = state.dataWeather->Environment(Environ);
2249 :
2250 3853 : if (DayToRead == 1) {
2251 :
2252 : // Checks whether Weather file contains just one year of data. If yes then rewind and position to first
2253 : // day of weather file. The rest of code appropriately positions to the start day.
2254 :
2255 1226 : bool Ready = false;
2256 1226 : int NumRewinds = 0;
2257 : // Must position file to proper day
2258 : // File already position to first data record
2259 : // Set Current Day of Week to "start of Data Period"
2260 1226 : state.dataWeather->ReadEPlusWeatherCurTime = 1.0 / double(state.dataWeather->NumIntervalsPerHour);
2261 1226 : state.dataWeather->CurDayOfWeek = state.dataWeather->DataPeriods(1).WeekDay - 1;
2262 1226 : WYear = 0;
2263 1226 : WMonth = 0;
2264 1226 : WDay = 0;
2265 1226 : WHour = 0;
2266 1226 : WMinute = 0;
2267 1226 : state.dataWeather->LastHourSet = false;
2268 2452 : InputFile::ReadResult<std::string> WeatherDataLine{"", true, false};
2269 95989 : while (!Ready) {
2270 94763 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2271 94763 : if (WeatherDataLine.good) {
2272 : bool ErrorFound;
2273 94763 : InterpretWeatherDataLine(state,
2274 : WeatherDataLine.data,
2275 : ErrorFound,
2276 : WYear,
2277 : WMonth,
2278 : WDay,
2279 : WHour,
2280 : WMinute,
2281 : DryBulb,
2282 : DewPoint,
2283 : RelHum,
2284 : AtmPress,
2285 : ETHoriz,
2286 : ETDirect,
2287 : IRHoriz,
2288 : GLBHoriz,
2289 : DirectRad,
2290 : DiffuseRad,
2291 : GLBHorizIllum,
2292 : DirectNrmIllum,
2293 : DiffuseHorizIllum,
2294 : ZenLum,
2295 : WindDir,
2296 : WindSpeed,
2297 : TotalSkyCover,
2298 : OpaqueSkyCover,
2299 : Visibility,
2300 : CeilHeight,
2301 : PresWeathObs,
2302 : PresWeathConds,
2303 : PrecipWater,
2304 : AerosolOptDepth,
2305 : SnowDepth,
2306 : DaysSinceLastSnow,
2307 : Albedo,
2308 : LiquidPrecip);
2309 0 : } else if (WeatherDataLine.eof) {
2310 0 : if (NumRewinds > 0) {
2311 0 : std::string date = fmt::to_string(thisEnviron.StartMonth) + '/' + fmt::to_string(thisEnviron.StartDay);
2312 0 : if (thisEnviron.MatchYear) {
2313 0 : date += '/' + fmt::to_string(thisEnviron.StartYear);
2314 : }
2315 0 : ShowSevereError(state, format("Multiple rewinds on EPW while searching for first day {}", date));
2316 0 : } else {
2317 0 : state.files.inputWeatherFile.rewind();
2318 0 : ++NumRewinds;
2319 0 : SkipEPlusWFHeader(state);
2320 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2321 : bool ErrorFound;
2322 0 : InterpretWeatherDataLine(state,
2323 : WeatherDataLine.data,
2324 : ErrorFound,
2325 : WYear,
2326 : WMonth,
2327 : WDay,
2328 : WHour,
2329 : WMinute,
2330 : DryBulb,
2331 : DewPoint,
2332 : RelHum,
2333 : AtmPress,
2334 : ETHoriz,
2335 : ETDirect,
2336 : IRHoriz,
2337 : GLBHoriz,
2338 : DirectRad,
2339 : DiffuseRad,
2340 : GLBHorizIllum,
2341 : DirectNrmIllum,
2342 : DiffuseHorizIllum,
2343 : ZenLum,
2344 : WindDir,
2345 : WindSpeed,
2346 : TotalSkyCover,
2347 : OpaqueSkyCover,
2348 : Visibility,
2349 : CeilHeight,
2350 : PresWeathObs,
2351 : PresWeathConds,
2352 : PrecipWater,
2353 : AerosolOptDepth,
2354 : SnowDepth,
2355 : DaysSinceLastSnow,
2356 : Albedo,
2357 : LiquidPrecip);
2358 : }
2359 : }
2360 94763 : if (!WeatherDataLine.good) {
2361 0 : ShowFatalError(state,
2362 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2363 : WYear,
2364 : WMonth,
2365 : WDay,
2366 : WHour,
2367 : WMinute,
2368 0 : state.files.inputWeatherFile.error_state_to_string()),
2369 0 : OptionalOutputFileRef{state.files.eso});
2370 : }
2371 94763 : if (state.dataWeather->CurDayOfWeek <= 7) {
2372 94763 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2373 : }
2374 94763 : bool RecordDateMatch =
2375 188300 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && !thisEnviron.MatchYear) ||
2376 93537 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && thisEnviron.MatchYear && WYear == thisEnviron.StartYear);
2377 94763 : if (RecordDateMatch) {
2378 1226 : state.files.inputWeatherFile.backspace();
2379 1226 : Ready = true;
2380 1226 : if (state.dataWeather->CurDayOfWeek <= 7) {
2381 1226 : --state.dataWeather->CurDayOfWeek;
2382 : }
2383 : // Do the range checks on the first set of fields -- no others.
2384 1226 : bool ErrorsFound = false;
2385 1226 : if (DryBulb < 99.9 && (DryBulb < -90.0 || DryBulb > 70.0)) {
2386 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2387 0 : ShowContinueError(state, format("DryBulb Temperature ({:.2R}) is out of range [-90.0, 70.0]", DryBulb));
2388 0 : ErrorsFound = true;
2389 : }
2390 :
2391 1226 : if (DewPoint < 99.9 && (DewPoint < -90.0 || DewPoint > 70.0)) {
2392 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2393 0 : ShowContinueError(state, format("DewPoint Temperature ({:.2R}) is out of range [-90.0, 70.0]", DewPoint));
2394 0 : ErrorsFound = true;
2395 : }
2396 :
2397 1226 : if (RelHum < 999.0 && (RelHum < 0.0 || RelHum > 110.0)) {
2398 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2399 0 : ShowContinueError(state, format("Relative Humidity ({:.2R}) is out of range [0.0, 100.0]", RelHum));
2400 0 : ErrorsFound = true;
2401 : }
2402 :
2403 1226 : if (AtmPress < 999999.0 && (AtmPress <= 31000.0 || AtmPress > 120000.0)) {
2404 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2405 0 : ShowContinueError(state, format("Atmospheric Pressure ({:.0R}) is out of range [31000, 120000]", AtmPress));
2406 0 : ErrorsFound = true;
2407 : }
2408 :
2409 1226 : if (DirectRad < 9999.0 && DirectRad < 0.0) {
2410 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2411 0 : ShowContinueError(state, format("Direct Radiation ({:.2R}) is out of range [0.0, -]", DirectRad));
2412 0 : ErrorsFound = true;
2413 : }
2414 :
2415 1226 : if (DiffuseRad < 9999.0 && DiffuseRad < 0.0) {
2416 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2417 0 : ShowContinueError(state, format("Diffuse Radiation ({:.2R}) is out of range [0.0, -]", DiffuseRad));
2418 0 : ErrorsFound = true;
2419 : }
2420 :
2421 1226 : if (WindDir < 999.0 && (WindDir < 0.0 || WindDir > 360.0)) {
2422 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2423 0 : ShowContinueError(state, format("Wind Direction ({:.2R}) is out of range [0.0, 360.0]", WindDir));
2424 0 : ErrorsFound = true;
2425 : }
2426 :
2427 1226 : if (WindSpeed < 999.0 && (WindSpeed < 0.0 || WindSpeed > 40.0)) {
2428 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2429 0 : ShowContinueError(state, format("Wind Speed ({:.2R}) is out of range [0.0, 40.0]", WindSpeed));
2430 0 : ErrorsFound = true;
2431 : }
2432 :
2433 1226 : if (ErrorsFound) {
2434 0 : ShowSevereError(state, "Out of Range errors found with initial day of WeatherFile");
2435 : }
2436 : } else {
2437 : // Must skip this day
2438 93537 : for (int i = 2; i <= state.dataWeather->NumIntervalsPerHour; ++i) {
2439 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2440 0 : if (!WeatherDataLine.good) {
2441 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2442 0 : ShowFatalError(state,
2443 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2444 : WYear,
2445 : WMonth,
2446 : WDay,
2447 : WHour,
2448 : WMinute,
2449 0 : state.files.inputWeatherFile.error_state_to_string()),
2450 0 : OptionalOutputFileRef{state.files.eso});
2451 : }
2452 : }
2453 2244888 : for (int i = 1; i <= 23 * state.dataWeather->NumIntervalsPerHour; ++i) {
2454 2151351 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2455 2151351 : if (!WeatherDataLine.good) {
2456 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2457 0 : ShowFatalError(state,
2458 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2459 : WYear,
2460 : WMonth,
2461 : WDay,
2462 : WHour,
2463 : WMinute,
2464 0 : state.files.inputWeatherFile.error_state_to_string()),
2465 0 : OptionalOutputFileRef{state.files.eso});
2466 : }
2467 : }
2468 : }
2469 : }
2470 :
2471 1226 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2472 : // Why do some things here use state.dataWeather->Envrn and some the parameter Environ?
2473 :
2474 : // Positioned to proper day
2475 1237 : if (!state.dataGlobal->KickOffSimulation && !state.dataGlobal->DoingSizing &&
2476 11 : thisEnviron.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
2477 6 : ++thisEnviron.CurrentCycle;
2478 6 : if (!thisEnviron.RollDayTypeOnRepeat) {
2479 0 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2480 0 : if (state.dataWeather->DaylightSavingIsActive) {
2481 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2482 : }
2483 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2484 6 : } else if (thisEnviron.CurrentCycle == 1) {
2485 6 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2486 6 : thisEnviron.SetWeekDays = true;
2487 6 : if (state.dataWeather->DaylightSavingIsActive) {
2488 1 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2489 : }
2490 6 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2491 : } else {
2492 0 : state.dataWeather->CurDayOfWeek = state.dataEnvrn->DayOfWeekTomorrow;
2493 : }
2494 : } else {
2495 1220 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2496 : }
2497 1226 : }
2498 :
2499 3853 : bool TryAgain = true;
2500 3853 : bool SkipThisDay = false;
2501 :
2502 7706 : while (TryAgain) {
2503 :
2504 3853 : TryAgain = false;
2505 :
2506 96325 : for (int hour = 1; hour <= 24; ++hour) {
2507 184944 : for (int CurTimeStep = 1; CurTimeStep <= state.dataWeather->NumIntervalsPerHour; ++CurTimeStep) {
2508 92472 : state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour) = WeatherVars();
2509 92472 : auto WeatherDataLine = state.files.inputWeatherFile.readLine();
2510 92472 : if (!WeatherDataLine.good) {
2511 0 : WeatherDataLine.data.clear();
2512 : }
2513 92472 : if (WeatherDataLine.data.empty()) {
2514 0 : if (hour == 1) {
2515 0 : WeatherDataLine.eof = true;
2516 0 : WeatherDataLine.good = false;
2517 : } else {
2518 0 : WeatherDataLine.good = false;
2519 : }
2520 : }
2521 92472 : if (WeatherDataLine.good) {
2522 : bool ErrorFound;
2523 92472 : InterpretWeatherDataLine(state,
2524 : WeatherDataLine.data,
2525 : ErrorFound,
2526 : WYear,
2527 : WMonth,
2528 : WDay,
2529 : WHour,
2530 : WMinute,
2531 : DryBulb,
2532 : DewPoint,
2533 : RelHum,
2534 : AtmPress,
2535 : ETHoriz,
2536 : ETDirect,
2537 : IRHoriz,
2538 : GLBHoriz,
2539 : DirectRad,
2540 : DiffuseRad,
2541 : GLBHorizIllum,
2542 : DirectNrmIllum,
2543 : DiffuseHorizIllum,
2544 : ZenLum,
2545 : WindDir,
2546 : WindSpeed,
2547 : TotalSkyCover,
2548 : OpaqueSkyCover,
2549 : Visibility,
2550 : CeilHeight,
2551 : PresWeathObs,
2552 : PresWeathConds,
2553 : PrecipWater,
2554 : AerosolOptDepth,
2555 : SnowDepth,
2556 : DaysSinceLastSnow,
2557 : Albedo,
2558 : LiquidPrecip);
2559 : } else { // ReadStatus /=0
2560 0 : if (WeatherDataLine.eof &&
2561 0 : state.dataWeather->NumDataPeriods == 1) { // Standard End-of-file, rewind and position to first day...
2562 0 : if (state.dataWeather->DataPeriods(1).NumDays >= state.dataWeather->NumDaysInYear) {
2563 0 : state.files.inputWeatherFile.rewind();
2564 0 : SkipEPlusWFHeader(state);
2565 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2566 : bool ErrorFound;
2567 0 : InterpretWeatherDataLine(state,
2568 : WeatherDataLine.data,
2569 : ErrorFound,
2570 : WYear,
2571 : WMonth,
2572 : WDay,
2573 : WHour,
2574 : WMinute,
2575 : DryBulb,
2576 : DewPoint,
2577 : RelHum,
2578 : AtmPress,
2579 : ETHoriz,
2580 : ETDirect,
2581 : IRHoriz,
2582 : GLBHoriz,
2583 : DirectRad,
2584 : DiffuseRad,
2585 : GLBHorizIllum,
2586 : DirectNrmIllum,
2587 : DiffuseHorizIllum,
2588 : ZenLum,
2589 : WindDir,
2590 : WindSpeed,
2591 : TotalSkyCover,
2592 : OpaqueSkyCover,
2593 : Visibility,
2594 : CeilHeight,
2595 : PresWeathObs,
2596 : PresWeathConds,
2597 : PrecipWater,
2598 : AerosolOptDepth,
2599 : SnowDepth,
2600 : DaysSinceLastSnow,
2601 : Albedo,
2602 : LiquidPrecip);
2603 : } else {
2604 0 : ShowFatalError(state,
2605 0 : format("End-of-File encountered after {}/{}/{} {}:{}, starting from first day of Weather File would "
2606 : "not be \"next day\"",
2607 : WYear,
2608 : WMonth,
2609 : WDay,
2610 : WHour,
2611 : WMinute));
2612 : }
2613 : } else {
2614 0 : ShowFatalError(state,
2615 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2616 : WYear,
2617 : WMonth,
2618 : WDay,
2619 : WHour,
2620 : WMinute),
2621 0 : OptionalOutputFileRef{state.files.eso});
2622 : }
2623 : }
2624 :
2625 92472 : if (hour != WHour) {
2626 0 : ShowFatalError(state,
2627 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2628 : WYear,
2629 : WMonth,
2630 : WDay,
2631 : WHour,
2632 : WMinute),
2633 0 : OptionalOutputFileRef{state.files.eso});
2634 : }
2635 :
2636 : // Set possible missing values
2637 92472 : if (ETHoriz < 0.0) ETHoriz = 9999.0;
2638 92472 : if (ETDirect < 0.0) ETDirect = 9999.0;
2639 92472 : if (IRHoriz <= 0.0) IRHoriz = 9999.0;
2640 92472 : if (GLBHoriz < 0.0) GLBHoriz = 9999.0;
2641 92472 : if (state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
2642 0 : if (DirectRad >= 9999.0) {
2643 0 : ++state.dataWeather->wvarsMissedCounts.BeamSolarRad;
2644 : }
2645 0 : if (DiffuseRad >= 9999.0) {
2646 0 : state.dataWeather->wvarsMissedCounts.DifSolarRad = state.dataWeather->wvarsMissedCounts.BeamSolarRad + 1;
2647 : }
2648 0 : if (DirectRad < 0.0) {
2649 0 : DirectRad = 9999.0;
2650 0 : ++state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad;
2651 : }
2652 0 : if (DiffuseRad < 0.0) {
2653 0 : DiffuseRad = 9999.0;
2654 0 : ++state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad;
2655 : }
2656 : }
2657 92472 : if (GLBHorizIllum < 0.0) GLBHorizIllum = 999999.0;
2658 92472 : if (DirectNrmIllum < 0.0) DirectNrmIllum = 999999.0;
2659 92472 : if (DiffuseHorizIllum < 0.0) DiffuseHorizIllum = 999999.0;
2660 92472 : if (ZenLum < 0.0) ZenLum = 99999.0;
2661 92472 : if (AtmPress < 0.0) AtmPress = 999999.0;
2662 92472 : if (WindSpeed < 0.0) WindSpeed = 999.0;
2663 92472 : if (WindDir < -360.0 || WindDir > 360.0) WindDir = 999.0;
2664 92472 : if (TotalSkyCover < 0.0) TotalSkyCover = 99.0;
2665 92472 : if (RelHum < 0.0) RelHum = 999.0;
2666 92472 : if (OpaqueSkyCover < 0.0) OpaqueSkyCover = 99.0;
2667 92472 : if (Visibility < 0.0) Visibility = 9999.0;
2668 92472 : if (CeilHeight < 0.0) CeilHeight = 9999.0;
2669 92472 : if (PresWeathObs < 0) PresWeathObs = 9;
2670 92472 : if (PrecipWater < 0.0) PrecipWater = 999.0;
2671 92472 : if (AerosolOptDepth < 0.0) AerosolOptDepth = 999.0;
2672 92472 : if (SnowDepth < 0.0) SnowDepth = 999.0;
2673 92472 : if (DaysSinceLastSnow < 0.0) DaysSinceLastSnow = 99.0;
2674 92472 : if (Albedo < 0.0) Albedo = 999.0;
2675 92472 : if (LiquidPrecip < 0.0) LiquidPrecip = 999.0;
2676 :
2677 92472 : if (hour == 1 && CurTimeStep == 1) {
2678 3853 : if (WMonth == 2 && WDay == 29 && (!state.dataEnvrn->CurrentYearIsLeapYear || !state.dataWeather->WFAllowsLeapYears)) {
2679 0 : state.dataWeather->EndDayOfMonth(2) = 28;
2680 0 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = 28;
2681 0 : SkipThisDay = true;
2682 0 : TryAgain = true;
2683 0 : ShowWarningError(state, "ReadEPlusWeatherForDay: Feb29 data encountered but will not be processed.");
2684 0 : if (!state.dataWeather->WFAllowsLeapYears) {
2685 0 : ShowContinueError(
2686 : state, "...WeatherFile does not allow Leap Years. HOLIDAYS/DAYLIGHT SAVINGS header must indicate \"Yes\".");
2687 : }
2688 0 : continue;
2689 : } else {
2690 3853 : TryAgain = false;
2691 3853 : SkipThisDay = false;
2692 : }
2693 :
2694 3853 : if (thisEnviron.ActualWeather && state.dataEnvrn->CurrentYearIsLeapYear) {
2695 0 : if (WMonth == 3 && WDay == 1 && state.dataEnvrn->Month == 2 && state.dataEnvrn->DayOfMonth == 28) {
2696 0 : ShowFatalError(state, "ReadEPlusWeatherForDay: Current year is a leap year, but Feb29 data is missing.");
2697 : }
2698 : }
2699 :
2700 3853 : state.dataWeather->TomorrowVariables.Year = WYear;
2701 3853 : state.dataWeather->TomorrowVariables.Month = WMonth;
2702 3853 : state.dataWeather->TomorrowVariables.DayOfMonth = WDay;
2703 3853 : state.dataWeather->TomorrowVariables.DayOfYear = General::OrdinalDay(WMonth, WDay, state.dataWeather->LeapYearAdd);
2704 3853 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule = General::OrdinalDay(WMonth, WDay, 1);
2705 : Real64 A;
2706 : Real64 B;
2707 : Real64 C;
2708 : Real64 AVSC;
2709 3853 : CalculateDailySolarCoeffs(state,
2710 3853 : state.dataWeather->TomorrowVariables.DayOfYear,
2711 : A,
2712 : B,
2713 : C,
2714 : AVSC,
2715 3853 : state.dataWeather->TomorrowVariables.EquationOfTime,
2716 3853 : state.dataWeather->TomorrowVariables.SinSolarDeclinAngle,
2717 3853 : state.dataWeather->TomorrowVariables.CosSolarDeclinAngle);
2718 3853 : if (state.dataWeather->CurDayOfWeek <= 7) {
2719 3753 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2720 : }
2721 3853 : state.dataWeather->TomorrowVariables.DayOfWeek = state.dataWeather->CurDayOfWeek;
2722 7706 : state.dataWeather->TomorrowVariables.DaylightSavingIndex =
2723 3853 : state.dataWeather->DSTIndex(state.dataWeather->TomorrowVariables.DayOfYear);
2724 3853 : state.dataWeather->TomorrowVariables.HolidayIndex =
2725 3853 : state.dataWeather->SpecialDayTypes(state.dataWeather->TomorrowVariables.DayOfYear);
2726 : }
2727 :
2728 92472 : if (SkipThisDay) continue;
2729 :
2730 : // Check out missing values
2731 :
2732 92472 : if (DryBulb >= 99.9) {
2733 0 : DryBulb = state.dataWeather->wvarsMissing.OutDryBulbTemp;
2734 0 : ++state.dataWeather->wvarsMissedCounts.OutDryBulbTemp;
2735 : }
2736 92472 : if (DryBulb < -90.0 || DryBulb > 70.0) {
2737 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp;
2738 : }
2739 :
2740 92472 : if (DewPoint >= 99.9) {
2741 0 : DewPoint = state.dataWeather->wvarsMissing.OutDewPointTemp;
2742 0 : ++state.dataWeather->wvarsMissedCounts.OutDewPointTemp;
2743 : }
2744 92472 : if (DewPoint < -90.0 || DewPoint > 70.0) {
2745 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp;
2746 : }
2747 :
2748 92472 : if (RelHum >= 999.0) {
2749 0 : RelHum = state.dataWeather->wvarsMissing.OutRelHum;
2750 0 : ++state.dataWeather->wvarsMissedCounts.OutRelHum;
2751 : }
2752 92472 : if (RelHum < 0.0 || RelHum > 110.0) {
2753 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutRelHum;
2754 : }
2755 :
2756 92472 : if (AtmPress >= 999999.0) {
2757 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2758 0 : ++state.dataWeather->wvarsMissedCounts.OutBaroPress;
2759 : }
2760 92472 : if (AtmPress <= 31000.0 || AtmPress > 120000.0) {
2761 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress;
2762 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2763 : }
2764 :
2765 92472 : if (WindDir >= 999.0) {
2766 0 : WindDir = state.dataWeather->wvarsMissing.WindDir;
2767 0 : ++state.dataWeather->wvarsMissedCounts.WindDir;
2768 : }
2769 92472 : if (WindDir < 0.0 || WindDir > 360.0) {
2770 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindDir;
2771 : }
2772 :
2773 92472 : if (WindSpeed >= 999.0) {
2774 0 : WindSpeed = state.dataWeather->wvarsMissing.WindSpeed;
2775 0 : ++state.dataWeather->wvarsMissedCounts.WindSpeed;
2776 : }
2777 92472 : if (WindSpeed < 0.0 || WindSpeed > 40.0) {
2778 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindSpeed;
2779 : }
2780 :
2781 92472 : if (TotalSkyCover >= 99.0) {
2782 0 : TotalSkyCover = state.dataWeather->wvarsMissing.TotalSkyCover;
2783 0 : ++state.dataWeather->wvarsMissedCounts.TotalSkyCover;
2784 : }
2785 :
2786 92472 : if (OpaqueSkyCover >= 99.0) {
2787 0 : OpaqueSkyCover = state.dataWeather->wvarsMissing.OpaqueSkyCover;
2788 0 : ++state.dataWeather->wvarsMissedCounts.OpaqueSkyCover;
2789 : }
2790 :
2791 92472 : if (SnowDepth >= 999.0) {
2792 72 : SnowDepth = state.dataWeather->wvarsMissing.SnowDepth;
2793 72 : ++state.dataWeather->wvarsMissedCounts.SnowDepth;
2794 : }
2795 :
2796 92472 : if (Albedo >= 999.0) {
2797 73920 : Albedo = state.dataWeather->wvarsMissing.Albedo;
2798 73920 : ++state.dataWeather->wvarsMissedCounts.Albedo;
2799 : }
2800 :
2801 92472 : if (LiquidPrecip >= 999.0) {
2802 74625 : LiquidPrecip = state.dataWeather->wvarsMissing.LiquidPrecip;
2803 74625 : ++state.dataWeather->wvarsMissedCounts.LiquidPrecip;
2804 : }
2805 :
2806 92472 : auto &tomorrow = state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour);
2807 92472 : tomorrow.OutDryBulbTemp = DryBulb;
2808 92472 : tomorrow.OutDewPointTemp = DewPoint;
2809 92472 : tomorrow.OutBaroPress = AtmPress;
2810 92472 : tomorrow.OutRelHum = RelHum;
2811 92472 : RelHum *= 0.01;
2812 92472 : tomorrow.WindSpeed = WindSpeed;
2813 92472 : tomorrow.WindDir = WindDir;
2814 92472 : tomorrow.LiquidPrecip = LiquidPrecip;
2815 92472 : tomorrow.TotalSkyCover = TotalSkyCover;
2816 92472 : tomorrow.OpaqueSkyCover = OpaqueSkyCover;
2817 :
2818 92472 : calcSky(state, tomorrow.HorizIRSky, tomorrow.SkyTemp, OpaqueSkyCover, DryBulb, DewPoint, RelHum, IRHoriz);
2819 :
2820 92472 : if (ETHoriz >= 9999.0) ETHoriz = 0.0;
2821 92472 : if (ETDirect >= 9999.0) ETDirect = 0.0;
2822 92472 : if (GLBHoriz >= 9999.0) GLBHoriz = 0.0;
2823 92472 : if (DirectRad >= 9999.0) DirectRad = 0.0;
2824 92472 : if (DiffuseRad >= 9999.0) DiffuseRad = 0.0;
2825 92472 : if (GLBHorizIllum >= 999900.0) GLBHorizIllum = 0.0;
2826 92472 : if (DirectNrmIllum >= 999900.0) DirectNrmIllum = 0.0;
2827 92472 : if (DiffuseHorizIllum >= 999900.0) DiffuseHorizIllum = 0.0;
2828 92472 : if (ZenLum >= 99990.0) ZenLum = 0.0;
2829 92472 : if (state.dataEnvrn->IgnoreSolarRadiation) {
2830 0 : GLBHoriz = 0.0;
2831 0 : DirectRad = 0.0;
2832 0 : DiffuseRad = 0.0;
2833 : }
2834 92472 : if (state.dataEnvrn->IgnoreBeamRadiation) {
2835 0 : DirectRad = 0.0;
2836 : }
2837 92472 : if (state.dataEnvrn->IgnoreDiffuseRadiation) {
2838 0 : DiffuseRad = 0.0;
2839 : }
2840 :
2841 92472 : tomorrow.BeamSolarRad = DirectRad;
2842 92472 : tomorrow.DifSolarRad = DiffuseRad;
2843 :
2844 92472 : tomorrow.IsRain = false;
2845 92472 : if (PresWeathObs == 0) {
2846 144 : if (PresWeathConds(1) < 9 || PresWeathConds(2) < 9 || PresWeathConds(3) < 9) tomorrow.IsRain = true;
2847 : } else {
2848 92328 : tomorrow.IsRain = false;
2849 : }
2850 92472 : tomorrow.IsSnow = (SnowDepth > 0.0);
2851 :
2852 : // default if rain but none on weather file
2853 92472 : if (tomorrow.IsRain && tomorrow.LiquidPrecip == 0.0) tomorrow.LiquidPrecip = 2.0; // 2mm in an hour ~ .08 inch
2854 :
2855 92472 : state.dataWeather->wvarsMissing.OutDryBulbTemp = DryBulb;
2856 92472 : state.dataWeather->wvarsMissing.OutDewPointTemp = DewPoint;
2857 92472 : state.dataWeather->wvarsMissing.OutRelHum = static_cast<int>(std::round(RelHum * 100.0));
2858 92472 : state.dataWeather->wvarsMissing.OutBaroPress = AtmPress;
2859 92472 : state.dataWeather->wvarsMissing.WindDir = WindDir;
2860 92472 : state.dataWeather->wvarsMissing.WindSpeed = WindSpeed;
2861 92472 : state.dataWeather->wvarsMissing.TotalSkyCover = TotalSkyCover;
2862 92472 : state.dataWeather->wvarsMissing.OpaqueSkyCover = OpaqueSkyCover;
2863 92472 : state.dataWeather->wvarsMissing.Visibility = Visibility;
2864 92472 : state.dataWeather->wvarsMissing.Ceiling = CeilHeight;
2865 92472 : state.dataWeather->wvarsMissing.WaterPrecip = PrecipWater;
2866 92472 : state.dataWeather->wvarsMissing.AerOptDepth = AerosolOptDepth;
2867 92472 : state.dataWeather->wvarsMissing.SnowDepth = SnowDepth;
2868 92472 : state.dataWeather->wvarsMissing.DaysLastSnow = DaysSinceLastSnow;
2869 92472 : state.dataWeather->wvarsMissing.Albedo = Albedo;
2870 :
2871 92472 : } // for (CurTimeStep)
2872 :
2873 : } // for (Hour)
2874 :
2875 : } // Try Again While Loop
2876 :
2877 3853 : if (BackSpaceAfterRead) {
2878 0 : state.files.inputWeatherFile.backspace();
2879 : }
2880 :
2881 3853 : if (state.dataWeather->NumIntervalsPerHour == 1 && state.dataGlobal->NumOfTimeStepInHour > 1) {
2882 : // Create interpolated weather for timestep orientation
2883 : // First copy ts=1 (hourly) from data arrays to Wthr structure
2884 86950 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
2885 83472 : wvarsHr(hour) = state.dataWeather->wvarsHrTsTomorrow(1, hour);
2886 : }
2887 :
2888 3478 : if (!state.dataWeather->LastHourSet) {
2889 : // For first day of weather, all time steps of the first hour will be
2890 : // equal to the first hour's value.
2891 : // 2021-06: An additional input is added to here to allow the user to have chosen which hour to use
2892 1215 : int HrUsedtoInterp = thisEnviron.firstHrInterpUseHr1 ? 1 : 24;
2893 1215 : state.dataWeather->wvarsLastHr = wvarsHr(HrUsedtoInterp);
2894 1215 : state.dataWeather->LastHourSet = true;
2895 : }
2896 :
2897 86950 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
2898 :
2899 83472 : int NextHr = (hour == Constant::HoursInDay) ? 1 : hour + 1;
2900 :
2901 83472 : state.dataWeather->wvarsNextHr.BeamSolarRad = wvarsHr(NextHr).BeamSolarRad;
2902 83472 : state.dataWeather->wvarsNextHr.DifSolarRad = wvarsHr(NextHr).DifSolarRad;
2903 83472 : state.dataWeather->wvarsNextHr.LiquidPrecip = wvarsHr(NextHr).LiquidPrecip;
2904 :
2905 500256 : for (int ts = 1; ts <= state.dataGlobal->NumOfTimeStepInHour; ++ts) {
2906 :
2907 416784 : Real64 wgtCurrHr = state.dataWeather->Interpolation(ts);
2908 416784 : Real64 wgtPrevHr = 1.0 - wgtCurrHr;
2909 :
2910 : // Do Solar "weighting"
2911 :
2912 416784 : Real64 wgtCurrHrSolar = state.dataWeather->SolarInterpolation(ts);
2913 : Real64 wgtPrevHrSolar;
2914 : Real64 wgtNextHrSolar;
2915 :
2916 416784 : if (state.dataGlobal->NumOfTimeStepInHour == 1) {
2917 0 : wgtNextHrSolar = 1.0 - wgtCurrHr;
2918 0 : wgtPrevHrSolar = 0.0;
2919 416784 : } else if (wgtCurrHrSolar == 1.0) {
2920 : // It's at the half hour
2921 83472 : wgtPrevHrSolar = 0.0;
2922 83472 : wgtNextHrSolar = 0.0;
2923 333312 : } else if (ts * state.dataWeather->TimeStepFraction < 0.5) {
2924 124920 : wgtPrevHrSolar = 1.0 - wgtCurrHrSolar;
2925 124920 : wgtNextHrSolar = 0.0;
2926 : } else { // After the half hour
2927 208392 : wgtPrevHrSolar = 0.0;
2928 208392 : wgtNextHrSolar = 1.0 - wgtCurrHrSolar;
2929 : }
2930 :
2931 416784 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
2932 416784 : auto const &wvarsH = wvarsHr(hour);
2933 416784 : tomorrowTs.OutDryBulbTemp = state.dataWeather->wvarsLastHr.OutDryBulbTemp * wgtPrevHr + wvarsH.OutDryBulbTemp * wgtCurrHr;
2934 416784 : tomorrowTs.OutBaroPress = state.dataWeather->wvarsLastHr.OutBaroPress * wgtPrevHr + wvarsH.OutBaroPress * wgtCurrHr;
2935 416784 : tomorrowTs.OutDewPointTemp = state.dataWeather->wvarsLastHr.OutDewPointTemp * wgtPrevHr + wvarsH.OutDewPointTemp * wgtCurrHr;
2936 416784 : tomorrowTs.OutRelHum = state.dataWeather->wvarsLastHr.OutRelHum * wgtPrevHr + wvarsH.OutRelHum * wgtCurrHr;
2937 416784 : tomorrowTs.WindSpeed = state.dataWeather->wvarsLastHr.WindSpeed * wgtPrevHr + wvarsH.WindSpeed * wgtCurrHr;
2938 416784 : tomorrowTs.WindDir = interpolateWindDirection(state.dataWeather->wvarsLastHr.WindDir, wvarsH.WindDir, wgtCurrHr);
2939 416784 : tomorrowTs.TotalSkyCover = state.dataWeather->wvarsLastHr.TotalSkyCover * wgtPrevHr + wvarsH.TotalSkyCover * wgtCurrHr;
2940 416784 : tomorrowTs.OpaqueSkyCover = state.dataWeather->wvarsLastHr.OpaqueSkyCover * wgtPrevHr + wvarsH.OpaqueSkyCover * wgtCurrHr;
2941 : // Sky emissivity now takes interpolated timestep inputs rather than interpolated calculation esky results
2942 416784 : calcSky(state,
2943 416784 : tomorrowTs.HorizIRSky,
2944 416784 : tomorrowTs.SkyTemp,
2945 : tomorrowTs.OpaqueSkyCover,
2946 : tomorrowTs.OutDryBulbTemp,
2947 : tomorrowTs.OutDewPointTemp,
2948 416784 : tomorrowTs.OutRelHum * 0.01,
2949 416784 : state.dataWeather->wvarsLastHr.HorizIRSky * wgtPrevHr + wvarsH.HorizIRSky * wgtCurrHr);
2950 :
2951 416784 : tomorrowTs.DifSolarRad = state.dataWeather->wvarsLastHr.DifSolarRad * wgtPrevHrSolar + wvarsH.DifSolarRad * wgtCurrHrSolar +
2952 416784 : state.dataWeather->wvarsNextHr.DifSolarRad * wgtNextHrSolar;
2953 416784 : tomorrowTs.BeamSolarRad = state.dataWeather->wvarsLastHr.BeamSolarRad * wgtPrevHrSolar + wvarsH.BeamSolarRad * wgtCurrHrSolar +
2954 416784 : state.dataWeather->wvarsNextHr.BeamSolarRad * wgtNextHrSolar;
2955 :
2956 416784 : tomorrowTs.LiquidPrecip = state.dataWeather->wvarsLastHr.LiquidPrecip * wgtPrevHr + wvarsH.LiquidPrecip * wgtCurrHr;
2957 416784 : tomorrowTs.LiquidPrecip /= double(state.dataGlobal->NumOfTimeStepInHour);
2958 416784 : tomorrowTs.IsRain = tomorrowTs.LiquidPrecip >= state.dataWeather->IsRainThreshold; // Wthr%IsRain
2959 416784 : tomorrowTs.IsSnow = wvarsH.IsSnow;
2960 : } // End of TS Loop
2961 :
2962 83472 : state.dataWeather->wvarsLastHr = wvarsHr(hour);
2963 : } // End of Hour Loop
2964 : }
2965 :
2966 3853 : if (thisEnviron.WP_Type1 != 0) {
2967 0 : switch (state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).skyTempModel) {
2968 0 : case SkyTempModel::ScheduleValue: {
2969 0 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
2970 :
2971 0 : ScheduleManager::GetScheduleValuesForDay(state,
2972 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).SchedulePtr,
2973 : tmp,
2974 0 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule,
2975 0 : state.dataWeather->CurDayOfWeek);
2976 :
2977 0 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr) {
2978 0 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS) {
2979 0 : state.dataWeather->wvarsHrTsTomorrow(iTS, iHr).SkyTemp = tmp(iTS, iHr);
2980 : }
2981 : }
2982 0 : } break;
2983 0 : case SkyTempModel::DryBulbDelta: {
2984 0 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
2985 0 : ScheduleManager::GetScheduleValuesForDay(state,
2986 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).SchedulePtr,
2987 : tmp,
2988 0 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule,
2989 0 : state.dataWeather->CurDayOfWeek);
2990 :
2991 0 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
2992 0 : for (int ts = 1; ts <= state.dataGlobal->NumOfTimeStepInHour; ++ts) {
2993 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
2994 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDryBulbTemp - tmp(ts, hour);
2995 : }
2996 : }
2997 0 : } break;
2998 0 : case SkyTempModel::DewPointDelta: {
2999 0 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
3000 0 : ScheduleManager::GetScheduleValuesForDay(state,
3001 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).SchedulePtr,
3002 : tmp,
3003 0 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule,
3004 0 : state.dataWeather->CurDayOfWeek);
3005 0 : ForAllHrTs(state, [&state, &tmp](int iHr, int iTS) {
3006 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3007 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDewPointTemp - tmp(iTS, iHr);
3008 0 : });
3009 0 : } break;
3010 0 : default:
3011 0 : break;
3012 : }
3013 : }
3014 3853 : }
3015 :
3016 416784 : Real64 interpolateWindDirection(Real64 const prevHrWindDir, Real64 const curHrWindDir, Real64 const curHrWeight)
3017 : {
3018 : // adapted from http://stackoverflow.com/questions/2708476/rotation-interpolation
3019 416784 : Real64 curAng = curHrWindDir;
3020 416784 : Real64 prevAng = prevHrWindDir;
3021 416784 : Real64 diff = std::abs(curAng - prevAng);
3022 416784 : if (diff > 180.) {
3023 33790 : if (curAng > prevAng) {
3024 14838 : prevAng += 360.;
3025 : } else {
3026 18952 : curAng += 360.;
3027 : }
3028 : }
3029 416784 : Real64 interpAng = prevAng + (curAng - prevAng) * curHrWeight;
3030 416784 : return (fmod(interpAng, 360.)); // fmod is float modulus function
3031 : }
3032 :
3033 778800 : Real64 CalcSkyEmissivity(
3034 : EnergyPlusData &state, SkyTempModel const ESkyCalcType, Real64 const OSky, Real64 const DryBulb, Real64 const DewPoint, Real64 const RelHum)
3035 : {
3036 : // Calculate Sky Emissivity
3037 : // References:
3038 : // M. Li, Y. Jiang and C. F. M. Coimbra,
3039 : // "On the determination of atmospheric longwave irradiance under all-sky conditions,"
3040 : // Solar Energy 144, 2017, pp. 40–48,
3041 : // G. Clark and C. Allen, "The Estimation of Atmospheric Radiation for Clear and
3042 : // Cloudy Skies," Proc. 2nd National Passive Solar Conference (AS/ISES), 1978, pp. 675-678.
3043 :
3044 : Real64 ESky;
3045 :
3046 778800 : if (ESkyCalcType == SkyTempModel::Brunt) {
3047 0 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3048 0 : ESky = 0.618 + 0.056 * pow(PartialPress, 0.5);
3049 778800 : } else if (ESkyCalcType == SkyTempModel::Idso) {
3050 0 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3051 0 : ESky = 0.685 + 0.000032 * PartialPress * exp(1699 / (DryBulb + Constant::Kelvin));
3052 778800 : } else if (ESkyCalcType == SkyTempModel::BerdahlMartin) {
3053 0 : double const TDewC = min(DryBulb, DewPoint);
3054 0 : ESky = 0.758 + 0.521 * (TDewC / 100) + 0.625 * pow_2(TDewC / 100);
3055 : } else {
3056 778800 : ESky = 0.787 + 0.764 * std::log((min(DryBulb, DewPoint) + Constant::Kelvin) / Constant::Kelvin);
3057 : }
3058 778800 : return ESky * (1.0 + 0.0224 * OSky - 0.0035 * pow_2(OSky) + 0.00028 * pow_3(OSky));
3059 : }
3060 :
3061 1226 : void SetDayOfWeekInitialValues(int const EnvironDayOfWeek, // Starting Day of Week for the (Weather) RunPeriod (User Input)
3062 : int ¤tDayOfWeek // Current Day of Week
3063 : )
3064 : {
3065 :
3066 : // SUBROUTINE INFORMATION:
3067 : // AUTHOR Linda Lawrie
3068 : // DATE WRITTEN March 2012
3069 :
3070 : // PURPOSE OF THIS SUBROUTINE:
3071 : // Set of begin day of week for an environment. Similar sets but slightly different
3072 : // conditions. Improve code readability by having three routine calls instead of three
3073 : // IF blocks.
3074 :
3075 1226 : if (EnvironDayOfWeek != 0) {
3076 1226 : if (EnvironDayOfWeek <= 7) {
3077 1204 : currentDayOfWeek = EnvironDayOfWeek - 1;
3078 : } else {
3079 22 : currentDayOfWeek = EnvironDayOfWeek;
3080 : }
3081 : }
3082 1226 : }
3083 :
3084 0 : void ErrorInterpretWeatherDataLine(EnergyPlusData &state,
3085 : int const WYear,
3086 : int const WMonth,
3087 : int const WDay,
3088 : int const WHour,
3089 : int const WMinute,
3090 : std::string_view SaveLine,
3091 : std::string_view Line)
3092 : {
3093 0 : ShowSevereError(state, fmt::format("Invalid Weather Line at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3094 0 : ShowContinueError(state, fmt::format("Full Data Line={}", SaveLine));
3095 0 : ShowContinueError(state, fmt::format("Remainder of line={}", Line));
3096 0 : ShowFatalError(state, "Error in Reading Weather Data");
3097 0 : }
3098 :
3099 266075 : void InterpretWeatherDataLine(EnergyPlusData &state,
3100 : std::string_view Line,
3101 : bool &ErrorFound, // True if an error is found, false otherwise
3102 : int &WYear,
3103 : int &WMonth,
3104 : int &WDay,
3105 : int &WHour,
3106 : int &WMinute,
3107 : Real64 &DryBulb,
3108 : Real64 &DewPoint,
3109 : Real64 &RelHum,
3110 : Real64 &AtmPress,
3111 : Real64 Ðoriz,
3112 : Real64 &ETDirect,
3113 : Real64 &IRHoriz,
3114 : Real64 &GLBHoriz,
3115 : Real64 &DirectRad,
3116 : Real64 &DiffuseRad,
3117 : Real64 &GLBHorizIllum,
3118 : Real64 &DirectNrmIllum,
3119 : Real64 &DiffuseHorizIllum,
3120 : Real64 &ZenLum,
3121 : Real64 &WindDir,
3122 : Real64 &WindSpeed,
3123 : Real64 &TotalSkyCover,
3124 : Real64 &OpaqueSkyCover,
3125 : Real64 &Visibility,
3126 : Real64 &CeilHeight,
3127 : int &WObs, // PresWeathObs
3128 : Array1D_int &WCodesArr, // PresWeathConds
3129 : Real64 &PrecipWater,
3130 : Real64 &AerosolOptDepth,
3131 : Real64 &SnowDepth,
3132 : Real64 &DaysSinceLastSnow,
3133 : Real64 &Albedo,
3134 : Real64 &LiquidPrecip)
3135 : {
3136 :
3137 : // SUBROUTINE INFORMATION:
3138 : // AUTHOR Linda Lawrie
3139 : // DATE WRITTEN April 2001
3140 :
3141 : // PURPOSE OF THIS SUBROUTINE:
3142 : // This subroutine interprets the EPW weather data line because comma delimited fields
3143 : // may cause problems with some compilers. (Particularly character variables in
3144 : // comma delimited lines.
3145 :
3146 : // METHODOLOGY EMPLOYED:
3147 : // Field by field interpretation, eliminating the "data source field" which is also
3148 : // likely to contain blanks. Note that the "Weatherconditions" must be a 9 character
3149 : // alpha field with no intervening blanks.
3150 :
3151 266075 : EP_SIZE_CHECK(WCodesArr, 9); // NOLINT(misc-static-assert)
3152 :
3153 : static constexpr std::string_view ValidDigits("0123456789");
3154 :
3155 266075 : std::string_view::size_type pos = 0;
3156 266075 : std::string_view current_line = Line;
3157 :
3158 266075 : ErrorFound = false;
3159 :
3160 : // Do the first five. (To get to the DataSource field)
3161 : {
3162 266075 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 5); // Returns the position **after** the nth occurrence of ','
3163 266075 : const bool succeeded = readList(current_line.substr(pos, (nth_pos - 1) - pos), WYear, WMonth, WDay, WHour, WMinute);
3164 266075 : if (!succeeded) {
3165 0 : ShowSevereError(state, "Invalid Date info in Weather Line");
3166 0 : ShowContinueError(state, fmt::format("Entire Data Line={}", Line));
3167 0 : ShowFatalError(state, "Error in Reading Weather Data");
3168 : }
3169 : }
3170 :
3171 266075 : bool DateInError = false;
3172 266075 : if (WMonth >= 1 && WMonth <= 12) {
3173 : // Month number is valid
3174 266075 : if (WMonth != 2) {
3175 240571 : if (WDay > state.dataWeather->EndDayOfMonth(WMonth)) {
3176 0 : DateInError = true;
3177 : }
3178 25504 : } else if (WDay > state.dataWeather->EndDayOfMonth(WMonth) + 1) { // Whether actually used is determined by calling routine.
3179 0 : DateInError = true;
3180 : }
3181 : } else {
3182 0 : DateInError = true;
3183 : }
3184 :
3185 266075 : if (DateInError) {
3186 0 : ShowSevereError(state, format("Reading Weather Data Line, Invalid Date, Year={}, Month={}, Day={}", WYear, WMonth, WDay));
3187 0 : ShowFatalError(state, "Program terminates due to previous condition.");
3188 : }
3189 :
3190 : // index, unlike nth_occurrence returns the position of the search char, not the position after it
3191 266075 : pos = index(Line, ','); // WYear
3192 266075 : if (pos == std::string::npos) {
3193 0 : ShowSevereError(
3194 0 : state, format("Invalid Weather Line (no commas) at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3195 0 : ShowContinueError(state, fmt::format("Full Data Line={}", Line));
3196 0 : ShowFatalError(state, "Error in Reading Weather Data");
3197 : }
3198 266075 : current_line.remove_prefix(nth_occurrence(Line, ',', 6)); // remove WYear,WMonth,WDay,WHour,WMinute,Data Source/Integrity
3199 :
3200 : // Now read more numerics with List Directed I/O (note there is another "character" field lurking)
3201 : Real64 RField21;
3202 : {
3203 266075 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 21);
3204 :
3205 266075 : const bool succeeded = readList(current_line.substr(0, nth_pos - 1),
3206 : DryBulb,
3207 : DewPoint,
3208 : RelHum,
3209 : AtmPress,
3210 : ETHoriz,
3211 : ETDirect,
3212 : IRHoriz,
3213 : GLBHoriz,
3214 : DirectRad,
3215 : DiffuseRad,
3216 : GLBHorizIllum,
3217 : DirectNrmIllum,
3218 : DiffuseHorizIllum,
3219 : ZenLum,
3220 : WindDir,
3221 : WindSpeed,
3222 : TotalSkyCover,
3223 : OpaqueSkyCover,
3224 : Visibility,
3225 : CeilHeight,
3226 : RField21);
3227 :
3228 266075 : if (!succeeded) ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3229 266075 : current_line.remove_prefix(nth_pos);
3230 : }
3231 266075 : pos = index(current_line, ',');
3232 266075 : std::string PresWeathCodes;
3233 266075 : if (pos != std::string::npos && pos != 0) {
3234 266075 : PresWeathCodes = current_line.substr(0, pos);
3235 : } else {
3236 0 : PresWeathCodes = "999999999";
3237 : }
3238 266075 : current_line.remove_prefix(pos + 1);
3239 :
3240 266075 : auto readNextNumber = // (AUTO_OK_LAMBDA)
3241 6385500 : [reachedEndOfCommands = false, &state, &WYear, &WMonth, &WDay, &WHour, &WMinute, &Line, ¤t_line]() mutable -> Real64 {
3242 1596450 : if (reachedEndOfCommands) {
3243 100 : return 999.0;
3244 : }
3245 : Real64 target;
3246 1596350 : std::string_view::size_type pos = index(current_line, ',');
3247 : // We found a comma
3248 1596350 : if (pos != std::string::npos) {
3249 : // Content is not empty
3250 1596325 : if (pos != 0) {
3251 1596325 : bool error = false;
3252 1596325 : target = Util::ProcessNumber(current_line.substr(0, pos), error);
3253 1596325 : if (error) {
3254 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3255 : }
3256 : } else {
3257 0 : target = 999.0;
3258 : }
3259 1596325 : current_line.remove_prefix(pos + 1);
3260 : } else {
3261 : // Couldn't find next comma, but we need to process the potential current number
3262 25 : reachedEndOfCommands = true;
3263 25 : if (current_line.empty()) {
3264 0 : target = 999.0;
3265 : } else {
3266 25 : bool error = false;
3267 25 : target = Util::ProcessNumber(current_line, error);
3268 25 : if (error) {
3269 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3270 : }
3271 : }
3272 : }
3273 1596350 : return target;
3274 266075 : };
3275 :
3276 266075 : PrecipWater = readNextNumber();
3277 266075 : AerosolOptDepth = readNextNumber();
3278 266075 : SnowDepth = readNextNumber();
3279 266075 : DaysSinceLastSnow = readNextNumber();
3280 266075 : Albedo = readNextNumber();
3281 266075 : LiquidPrecip = readNextNumber();
3282 :
3283 266075 : WObs = nint(RField21);
3284 266075 : if (WObs == 0) { // Obs Indicator indicates Weather Codes valid
3285 : // Check for miscellaneous characters
3286 350 : pos = index(PresWeathCodes, '\'');
3287 350 : while (pos != std::string::npos) {
3288 0 : PresWeathCodes[pos] = ' ';
3289 0 : pos = index(PresWeathCodes, '\'');
3290 : }
3291 350 : pos = index(PresWeathCodes, '"');
3292 350 : while (pos != std::string::npos) {
3293 0 : PresWeathCodes[pos] = ' ';
3294 0 : pos = index(PresWeathCodes, '"');
3295 : }
3296 350 : strip(PresWeathCodes);
3297 350 : if (len(PresWeathCodes) == 9) {
3298 3500 : for (pos = 0; pos < 9; ++pos) {
3299 3150 : if (!has(ValidDigits, PresWeathCodes[pos])) PresWeathCodes[pos] = '9';
3300 : }
3301 :
3302 : // we are trying to read a string of 9 integers with no spaces, each
3303 : // into its own integer, like:
3304 : // "123456789"
3305 : // becomes
3306 : // std::vector<int>{1,2,3,4,5,6,7,8,9};
3307 350 : std::stringstream reader = stringReader(PresWeathCodes);
3308 3500 : for (auto &value : WCodesArr) {
3309 3150 : char c[2] = {0, 0}; // a string of 2 characters, init both to 0
3310 3150 : reader >> c[0]; // read next char into the first byte
3311 3150 : value = std::atoi(c); // convert this short string into the appropriate int to read
3312 : }
3313 350 : } else {
3314 0 : ++state.dataWeather->wvarsMissedCounts.WeathCodes;
3315 0 : WCodesArr = 9;
3316 : }
3317 : } else {
3318 265725 : WCodesArr = 9;
3319 : }
3320 266075 : }
3321 :
3322 5932 : void SetUpDesignDay(EnergyPlusData &state, int const EnvrnNum) // Environment number passed into the routine
3323 : {
3324 :
3325 : // SUBROUTINE INFORMATION:
3326 : // AUTHOR Linda Lawrie
3327 : // DATE WRITTEN February 1977
3328 : // MODIFIED June 1997 (RKS); May 2013 (LKL) add temperature profile for drybulb.
3329 : // RE-ENGINEERED August 2003;LKL -- to generate timestep weather for design days.
3330 :
3331 : // PURPOSE OF THIS SUBROUTINE:
3332 : // This purpose of this subroutine is to convert the user supplied input
3333 : // values for the design day parameters into an entire weather day
3334 : // record. This now bypasses any file I/O by keeping all of the
3335 : // weather day record information in the local module level derived type
3336 : // called DesignDay.
3337 :
3338 5932 : constexpr Real64 GlobalSolarConstant = 1367.0;
3339 5932 : constexpr Real64 ZHGlobalSolarConstant = 1355.0;
3340 :
3341 5932 : Real64 constexpr ZhangHuang_C0 = 0.5598; // 37.6865d0
3342 5932 : Real64 constexpr ZhangHuang_C1 = 0.4982; // 13.9263d0
3343 5932 : Real64 constexpr ZhangHuang_C2 = -0.6762; // -20.2354d0
3344 5932 : Real64 constexpr ZhangHuang_C3 = 0.02842; // 0.9695d0
3345 5932 : Real64 constexpr ZhangHuang_C4 = -0.00317; // -0.2046d0
3346 5932 : Real64 constexpr ZhangHuang_C5 = 0.014; // -0.0980d0
3347 5932 : Real64 constexpr ZhangHuang_D = -17.853; // -10.8568d0
3348 5932 : Real64 constexpr ZhangHuang_K = 0.843; // 49.3112d0
3349 : static constexpr std::string_view RoutineNamePsyWFnTdbTwbPb("SetUpDesignDay:PsyWFnTdbTwbPb");
3350 : static constexpr std::string_view RoutineNamePsyWFnTdpPb("SetUpDesignDay:PsyWFnTdpPb");
3351 : static constexpr std::string_view RoutineNamePsyWFnTdbH("SetUpDesignDay:PsyWFnTdbH");
3352 : static constexpr std::string_view WeatherManager("WeatherManager");
3353 : static constexpr std::string_view RoutineNameLong("WeatherManager.cc subroutine SetUpDesignDay");
3354 :
3355 5932 : std::string StringOut;
3356 : // For reporting purposes, set year to current system year
3357 :
3358 : struct HourlyWeatherData
3359 : {
3360 : // Members
3361 : Array1D<Real64> BeamSolarRad = Array1D<Real64>(Constant::HoursInDay, 0.0); // Hourly direct normal solar irradiance
3362 : Array1D<Real64> DifSolarRad = Array1D<Real64>(Constant::HoursInDay, 0.0); // Hourly sky diffuse horizontal solar irradiance
3363 : };
3364 :
3365 : // Object Data
3366 5932 : HourlyWeatherData Wthr;
3367 :
3368 5932 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
3369 :
3370 5932 : bool SaveWarmupFlag = state.dataGlobal->WarmupFlag;
3371 5932 : state.dataGlobal->WarmupFlag = true;
3372 :
3373 5932 : Array1D_int Date0(8);
3374 5932 : date_and_time(_, _, _, Date0);
3375 5932 : int CurrentYear = Date0(1);
3376 :
3377 5932 : if (state.dataGlobal->BeginSimFlag) {
3378 796 : state.dataWeather->PrintDDHeader = true;
3379 : }
3380 :
3381 5932 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
3382 5932 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
3383 5932 : designDay.Year = CurrentYear; // f90 date_and_time implemented. full 4 digit year !+ 1900
3384 5932 : designDay.Month = desDayInput.Month;
3385 5932 : designDay.DayOfMonth = desDayInput.DayOfMonth;
3386 5932 : designDay.DayOfYear = General::OrdinalDay(designDay.Month, designDay.DayOfMonth, 0);
3387 : static constexpr std::string_view MnDyFmt("{:02}/{:02}");
3388 5932 : state.dataEnvrn->CurMnDy = format(MnDyFmt, desDayInput.Month, desDayInput.DayOfMonth);
3389 : // EnvironmentName = DesDayInput( EnvrnNum ).Title;
3390 5932 : state.dataEnvrn->RunPeriodEnvironment = false;
3391 : // Following builds Environment start/end for ASHRAE 55 warnings
3392 5932 : state.dataEnvrn->EnvironmentStartEnd = state.dataEnvrn->CurMnDy + " - " + state.dataEnvrn->CurMnDy;
3393 :
3394 : // Check that barometric pressure is within range
3395 5932 : if (desDayInput.PressureEntered) {
3396 5908 : if (std::abs((desDayInput.PressBarom - state.dataEnvrn->StdBaroPress) / state.dataEnvrn->StdBaroPress) > 0.1) { // 10% off
3397 108 : ShowWarningError(state,
3398 108 : format("SetUpDesignDay: Entered DesignDay Barometric Pressure={:.0R} differs by more than 10% from Standard "
3399 : "Barometric Pressure={:.0R}.",
3400 54 : desDayInput.PressBarom,
3401 54 : state.dataEnvrn->StdBaroPress));
3402 108 : ShowContinueError(
3403 : state,
3404 108 : format("...occurs in DesignDay={}, Standard Pressure (based on elevation) will be used.", state.dataEnvrn->EnvironmentName));
3405 54 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3406 : }
3407 : } else {
3408 24 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3409 : }
3410 :
3411 : // verify that design WB or DP <= design DB
3412 5932 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint && desDayInput.DewPointNeedsSet) {
3413 : // dew-point
3414 0 : Real64 testval = Psychrometrics::PsyWFnTdbRhPb(state, desDayInput.MaxDryBulb, 1.0, desDayInput.PressBarom);
3415 0 : desDayInput.HumIndValue = Psychrometrics::PsyTdpFnWPb(state, testval, desDayInput.PressBarom);
3416 : }
3417 :
3418 : // Day of week defaults to Monday, if day type specified, then that is used.
3419 5932 : designDay.DayOfWeek = 2;
3420 5932 : if (desDayInput.DayType <= 7) designDay.DayOfWeek = desDayInput.DayType;
3421 :
3422 : // set Holiday as indicated by user input
3423 5932 : designDay.HolidayIndex = 0;
3424 5932 : if (desDayInput.DayType > 7) designDay.HolidayIndex = desDayInput.DayType;
3425 :
3426 5932 : designDay.DaylightSavingIndex = desDayInput.DSTIndicator;
3427 :
3428 : // Set up Solar parameters for day
3429 : Real64 A; // Apparent solar irradiation at air mass = 0
3430 : Real64 B; // Atmospheric extinction coefficient
3431 : Real64 C; // ASHRAE diffuse radiation factor
3432 : Real64 AVSC; // Annual variation in the solar constant
3433 5932 : CalculateDailySolarCoeffs(
3434 5932 : state, designDay.DayOfYear, A, B, C, AVSC, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle);
3435 :
3436 5932 : if (state.dataWeather->PrintDDHeader && state.dataReportFlag->DoWeatherInitReporting) {
3437 : static constexpr std::string_view EnvDDHdFormat(
3438 : "! <Environment:Design Day Data>, Max Dry-Bulb Temp {C}, Temp Range {dC}, Temp Range Ind Type, "
3439 : "Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units, Pressure {Pa}, Wind Direction {deg CW from N}, Wind "
3440 : "Speed {m/s}, Clearness, Rain, Snow");
3441 789 : print(state.files.eio, "{}\n", EnvDDHdFormat);
3442 : static constexpr std::string_view DDayMiscHdFormat(
3443 : "! <Environment:Design Day Misc>,DayOfYear,ASHRAE A Coeff,ASHRAE B Coeff,ASHRAE C Coeff,Solar "
3444 : "Constant-Annual Variation,Eq of Time {minutes}, Solar Declination Angle {deg}, Solar Model");
3445 789 : print(state.files.eio, "{}\n", DDayMiscHdFormat);
3446 789 : state.dataWeather->PrintDDHeader = false;
3447 : }
3448 5932 : if (state.dataReportFlag->DoWeatherInitReporting) {
3449 1766 : std::string_view const AlpUseRain = (desDayInput.RainInd == 1) ? "Yes" : "No";
3450 1766 : std::string_view const AlpUseSnow = (desDayInput.SnowInd == 1) ? "Yes" : "No";
3451 1766 : print(state.files.eio, "Environment:Design Day Data,");
3452 1766 : print(state.files.eio, "{:.2R},", desDayInput.MaxDryBulb);
3453 1766 : print(state.files.eio, "{:.2R},", desDayInput.DailyDBRange);
3454 :
3455 : static constexpr std::array<std::string_view, (int)DesDayDryBulbRangeType::Num> DesDayDryBulbRangeTypeStrings = {
3456 : "DefaultMultipliers,", "MultiplierSchedule,", "DifferenceSchedule,", "TemperatureProfile,"};
3457 :
3458 1766 : print(state.files.eio, "{}", DesDayDryBulbRangeTypeStrings[(int)desDayInput.dryBulbRangeType]);
3459 :
3460 : static constexpr std::array<std::string_view, (int)DesDayHumIndType::Num> DesDayHumIndTypeStrings = {
3461 : "Wetbulb,{:.2R},{{C}},",
3462 : "Dewpoint,{:.2R},{{C}},",
3463 : "Enthalpy,{:.2R},{{J/kgDryAir}},",
3464 : "HumidityRatio,{:.4R},{{kgWater/kgDryAir}},",
3465 : "Schedule,<schedule values from 0.0 to 100.0>,{{percent}},",
3466 : "WetBulbProfileDefaultMultipliers,{:.2R},{{C}},",
3467 : "WetBulbProfileDifferenceSchedule,{:.2R},{{C}},",
3468 : "WetBulbProfileMultiplierSchedule,{:.2R},{{C}},"};
3469 :
3470 : // Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units
3471 1766 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
3472 3 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType]);
3473 1763 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
3474 12 : print(state.files.eio,
3475 12 : DesDayHumIndTypeStrings[(int)desDayInput.HumIndType],
3476 12 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).HumIndValue);
3477 : } else {
3478 1751 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType], desDayInput.HumIndValue);
3479 : }
3480 :
3481 1766 : print(state.files.eio, "{:.0R},", desDayInput.PressBarom);
3482 1766 : print(state.files.eio, "{:.0R},", desDayInput.WindDir);
3483 1766 : print(state.files.eio, "{:.1R},", desDayInput.WindSpeed);
3484 1766 : print(state.files.eio, "{:.2R},", desDayInput.SkyClear);
3485 :
3486 1766 : print(state.files.eio, "{},{}\n", AlpUseRain, AlpUseSnow);
3487 :
3488 : static constexpr std::string_view DDayMiscFormat("Environment:Design Day Misc,{:3},");
3489 1766 : print(state.files.eio, DDayMiscFormat, designDay.DayOfYear);
3490 1766 : print(state.files.eio, "{:.1R},", A);
3491 1766 : print(state.files.eio, "{:.4R},", B);
3492 1766 : print(state.files.eio, "{:.4R},", C);
3493 1766 : print(state.files.eio, "{:.1R},", AVSC);
3494 1766 : print(state.files.eio, "{:.2R},", designDay.EquationOfTime * 60.0);
3495 1766 : print(state.files.eio, "{:.1R},", std::asin(designDay.SinSolarDeclinAngle) / Constant::DegToRadians);
3496 :
3497 : // Why have a different string for "Schedule" here than the one used for input? Really, why?
3498 : static constexpr std::array<std::string_view, (int)DesDaySolarModel::Num> DesDaySolarModelStrings = {
3499 : "ASHRAEClearSky", "ZhangHuang", "User supplied beam/diffuse from schedules", "ASHRAETau", "ASHRAETau2017"};
3500 :
3501 1766 : print(state.files.eio, "{}\n", DesDaySolarModelStrings[(int)desDayInput.solarModel]);
3502 : }
3503 :
3504 : // Must set up weather values for Design Day. User can specify the "humidity indicator" as
3505 : // Wetbulb, DewPoint or input the relative humidity schedule. For both wetbulb and dewpoint indicators, the
3506 : // humidity for the day will be constant, using the drybulb (max) and humidity indicator temperature to
3507 : // set the values. For the scheduled values, these are already set in the DDxxx array.
3508 :
3509 5932 : state.dataGlobal->CurrentTime = 25.0;
3510 : Real64 HumidityRatio; // Humidity Ratio -- when constant for day
3511 : bool ConstantHumidityRatio;
3512 :
3513 5932 : switch (desDayInput.HumIndType) {
3514 5856 : case DesDayHumIndType::WetBulb: {
3515 5856 : HumidityRatio = Psychrometrics::PsyWFnTdbTwbPb(
3516 : state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdbTwbPb);
3517 5856 : ConstantHumidityRatio = true;
3518 5856 : } break;
3519 25 : case DesDayHumIndType::DewPoint: {
3520 25 : HumidityRatio = Psychrometrics::PsyWFnTdpPb(state, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdpPb);
3521 25 : ConstantHumidityRatio = true;
3522 25 : } break;
3523 0 : case DesDayHumIndType::HumRatio: {
3524 0 : HumidityRatio = desDayInput.HumIndValue;
3525 0 : ConstantHumidityRatio = true;
3526 0 : } break;
3527 15 : case DesDayHumIndType::Enthalpy: {
3528 : // HumIndValue is already in J/kg, so no conversions needed
3529 15 : HumidityRatio = Psychrometrics::PsyWFnTdbH(state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, RoutineNamePsyWFnTdbH);
3530 15 : ConstantHumidityRatio = true;
3531 15 : } break;
3532 6 : case DesDayHumIndType::RelHumSch: {
3533 : // nothing to do -- DDHumIndModifier already contains the scheduled Relative Humidity
3534 6 : ConstantHumidityRatio = false;
3535 6 : ForAllHrTs(state, [&state, EnvrnNum](int iHr, int iTS) {
3536 576 : state.dataWeather->wvarsHrTsTomorrow(iTS, iHr).OutRelHum = state.dataWeather->desDayMods(EnvrnNum)(iTS, iHr).OutRelHum;
3537 576 : });
3538 6 : } break;
3539 30 : case DesDayHumIndType::WBProfDef:
3540 : case DesDayHumIndType::WBProfDif:
3541 : case DesDayHumIndType::WBProfMul: {
3542 30 : ConstantHumidityRatio = false;
3543 30 : } break;
3544 0 : default: {
3545 0 : ShowSevereError(state, "SetUpDesignDay: Invalid Humidity Indicator type");
3546 0 : ShowContinueError(state, format("Occurred in Design Day={}", desDayInput.Title));
3547 0 : } break;
3548 : } // switch
3549 :
3550 : int OSky; // Opaque Sky Cover (tenths)
3551 5932 : if (desDayInput.RainInd != 0) {
3552 0 : OSky = 10;
3553 0 : ForAllHrTs(state, [&state](int iHr, int iTS) {
3554 0 : auto &ts = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3555 0 : ts.IsRain = true;
3556 0 : ts.LiquidPrecip = 3.0;
3557 0 : });
3558 : } else {
3559 5932 : OSky = 0;
3560 5932 : ForAllHrTs(state, [&state](int iHr, int iTS) {
3561 778800 : auto &ts = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3562 778800 : ts.IsRain = false;
3563 778800 : ts.LiquidPrecip = 0.0;
3564 778800 : });
3565 : }
3566 :
3567 : Real64 GndReflet; // Ground Reflectivity
3568 5932 : if (desDayInput.SnowInd == 0) {
3569 5932 : GndReflet = 0.2;
3570 5932 : ForAllHrTs(state, [&state](int iHr, int iTS) {
3571 778800 : auto &ts = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3572 778800 : ts.IsSnow = false;
3573 778800 : });
3574 : } else { // Snow
3575 0 : GndReflet = 0.7;
3576 0 : ForAllHrTs(state, [&state](int iHr, int iTS) {
3577 0 : auto &ts = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3578 0 : ts.IsSnow = true;
3579 0 : });
3580 : }
3581 :
3582 : // Some values are constant
3583 :
3584 5932 : ForAllHrTs(state, [&state, &desDayInput](int iHr, int iTS) {
3585 778800 : auto &ts = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3586 778800 : ts.OutBaroPress = desDayInput.PressBarom;
3587 778800 : ts.WindSpeed = desDayInput.WindSpeed;
3588 778800 : ts.WindDir = desDayInput.WindDir;
3589 778800 : ts.Albedo = 0.0;
3590 778800 : });
3591 :
3592 : // resolve daily ranges
3593 : Real64 DBRange; // working copy of dry-bulb daily range, C (or 1 if input is difference)
3594 5932 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
3595 4 : DBRange = 1.0; // use unscaled multiplier values if difference
3596 5928 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
3597 2 : DBRange = 0.0;
3598 : } else {
3599 5926 : DBRange = desDayInput.DailyDBRange;
3600 : }
3601 : Real64 WBRange; // working copy of wet-bulb daily range. C (or 1 if input is difference)
3602 5932 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
3603 2 : WBRange = 1.0; // use unscaled multiplier values if difference
3604 : } else {
3605 5930 : WBRange = desDayInput.DailyWBRange;
3606 : }
3607 :
3608 5932 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3609 148300 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
3610 921168 : for (int ts = 1; ts <= state.dataGlobal->NumOfTimeStepInHour; ++ts) {
3611 778800 : auto const &desDayModsTS = desDayModsEnvrn(ts, hour);
3612 778800 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
3613 778800 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
3614 : // dry-bulb profile
3615 778608 : tomorrowTs.OutDryBulbTemp = desDayInput.MaxDryBulb - desDayModsTS.OutDryBulbTemp * DBRange;
3616 : } else { // DesDayInput(EnvrnNum)%DBTempRangeType == DesDayDryBulbRangeType::Profile
3617 192 : tomorrowTs.OutDryBulbTemp = desDayModsTS.OutDryBulbTemp;
3618 : }
3619 :
3620 : // wet-bulb - generate from profile, humidity ratio, or dew point
3621 778800 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
3622 776304 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul) {
3623 2880 : Real64 WetBulb = desDayInput.HumIndValue - desDayModsTS.OutRelHum * WBRange;
3624 2880 : WetBulb = min(WetBulb, tomorrowTs.OutDryBulbTemp); // WB must be <= DB
3625 2880 : Real64 OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3626 2880 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3627 2880 : tomorrowTs.OutRelHum =
3628 2880 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3629 778800 : } else if (ConstantHumidityRatio) {
3630 : // Need Dew Point Temperature. Use Relative Humidity to get Humidity Ratio, unless Humidity Ratio is constant
3631 : // BG 9-26-07 moved following inside this IF statment; when HumIndType is 'Schedule' HumidityRatio wasn't being initialized
3632 : Real64 WetBulb =
3633 775344 : Psychrometrics::PsyTwbFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, RoutineNameLong);
3634 :
3635 775344 : Real64 OutHumRat = Psychrometrics::PsyWFnTdpPb(state, tomorrowTs.OutDryBulbTemp, desDayInput.PressBarom);
3636 775344 : if (HumidityRatio > OutHumRat) {
3637 274646 : WetBulb = tomorrowTs.OutDryBulbTemp;
3638 : } else {
3639 500698 : OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3640 : }
3641 775344 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3642 775344 : tomorrowTs.OutRelHum =
3643 775344 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3644 : } else {
3645 : HumidityRatio =
3646 576 : Psychrometrics::PsyWFnTdbRhPb(state, tomorrowTs.OutDryBulbTemp, desDayModsTS.OutRelHum / 100.0, desDayInput.PressBarom);
3647 576 : tomorrowTs.OutRelHum =
3648 576 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, WeatherManager) *
3649 : 100.0;
3650 : // TomorrowOutRelHum values set earlier
3651 576 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, HumidityRatio, desDayInput.PressBarom);
3652 : }
3653 :
3654 778800 : double DryBulb = tomorrowTs.OutDryBulbTemp;
3655 778800 : double RelHum = tomorrowTs.OutRelHum * 0.01;
3656 : Real64 ESky =
3657 778800 : CalcSkyEmissivity(state, envCurr.skyTempModel, OSky, DryBulb, tomorrowTs.OutDewPointTemp, RelHum); // Emissivitity of Sky
3658 778800 : tomorrowTs.HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
3659 :
3660 778800 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
3661 778800 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
3662 : // Design day not scheduled
3663 778608 : tomorrowTs.SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
3664 : }
3665 : // Generate solar values for timestep
3666 : // working results = BeamRad and DiffRad
3667 : // stored to program globals at end of loop
3668 : Real64 BeamRad;
3669 : Real64 DiffRad;
3670 778800 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
3671 : // scheduled: set value unconditionally (whether sun up or not)
3672 576 : BeamRad = desDayModsTS.BeamSolarRad;
3673 576 : DiffRad = desDayModsTS.DifSolarRad;
3674 : } else {
3675 :
3676 : // calc time = fractional hour of day
3677 : Real64 CurTime;
3678 778224 : if (state.dataGlobal->NumOfTimeStepInHour != 1) {
3679 776832 : CurTime = double(hour - 1) + double(ts) * state.dataWeather->TimeStepFraction;
3680 : } else {
3681 1392 : CurTime = double(hour) + state.dataEnvrn->TS1TimeOffset;
3682 : }
3683 :
3684 778224 : Vector3<Real64> SUNCOS; // Sun direction cosines
3685 778224 : CalculateSunDirectionCosines(
3686 : state, CurTime, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle, SUNCOS);
3687 778224 : Real64 CosZenith = SUNCOS.z; // Cosine of Zenith Angle of Sun
3688 778224 : if (CosZenith < DataEnvironment::SunIsUpValue) {
3689 386273 : BeamRad = 0.0;
3690 386273 : DiffRad = 0.0;
3691 : } else {
3692 391951 : Real64 SinSolarAltitude = SUNCOS.z;
3693 :
3694 391951 : switch (desDayInput.solarModel) {
3695 371513 : case DesDaySolarModel::ASHRAE_ClearSky: {
3696 371513 : Real64 Exponent = B / CosZenith;
3697 : Real64 TotHoriz; // Total Radiation on Horizontal Surface
3698 371513 : if (Exponent > 700.0) {
3699 12 : TotHoriz = 0.0;
3700 : } else {
3701 371501 : TotHoriz = desDayInput.SkyClear * A * (C + CosZenith) * std::exp(-B / CosZenith);
3702 : }
3703 : // Radiation on an extraterrestial horizontal surface
3704 371513 : Real64 HO = GlobalSolarConstant * AVSC * CosZenith;
3705 371513 : Real64 KT = TotHoriz / HO; // Radiation ratio
3706 371513 : KT = min(KT, 0.75);
3707 371513 : DiffRad = TotHoriz * (1.0045 + KT * (0.04349 + KT * (-3.5227 + 2.6313 * KT)));
3708 371513 : if (desDayInput.SkyClear > 0.70) DiffRad = TotHoriz * C / (C + CosZenith);
3709 371513 : BeamRad = (TotHoriz - DiffRad) / CosZenith;
3710 371513 : DiffRad = max(0.0, DiffRad);
3711 371513 : BeamRad = max(0.0, BeamRad);
3712 :
3713 371513 : } break;
3714 20248 : case DesDaySolarModel::ASHRAE_Tau:
3715 : case DesDaySolarModel::ASHRAE_Tau2017: {
3716 20248 : Real64 ETR = GlobalSolarConstant * AVSC; // radiation of an extraterrestrial normal surface, W/m2
3717 : Real64 GloHorzRad;
3718 20248 : ASHRAETauModel(
3719 : state, desDayInput.solarModel, ETR, CosZenith, desDayInput.TauB, desDayInput.TauD, BeamRad, DiffRad, GloHorzRad);
3720 20248 : } break;
3721 190 : case DesDaySolarModel::Zhang_Huang: {
3722 190 : int Hour3Ago = mod(hour + 20, 24) + 1; // hour 3 hours before
3723 190 : Real64 const TotSkyCover = max(1.0 - desDayInput.SkyClear, 0.0);
3724 380 : Real64 GloHorzRad = (ZHGlobalSolarConstant * SinSolarAltitude *
3725 190 : (ZhangHuang_C0 + ZhangHuang_C1 * TotSkyCover + ZhangHuang_C2 * pow_2(TotSkyCover) +
3726 380 : ZhangHuang_C3 * (tomorrowTs.OutDryBulbTemp -
3727 190 : state.dataWeather->wvarsHrTsTomorrow(ts, Hour3Ago).OutDryBulbTemp) +
3728 190 : ZhangHuang_C4 * tomorrowTs.OutRelHum + ZhangHuang_C5 * tomorrowTs.WindSpeed) +
3729 : ZhangHuang_D) /
3730 190 : ZhangHuang_K;
3731 190 : GloHorzRad = max(GloHorzRad, 0.0);
3732 190 : Real64 ClearnessIndex_kt = GloHorzRad / (GlobalSolarConstant * SinSolarAltitude);
3733 : // ClearnessIndex_kt=DesDayInput(EnvrnNum)%SkyClear
3734 190 : Real64 ClearnessIndex_ktc = 0.4268 + 0.1934 * SinSolarAltitude;
3735 : Real64 ClearnessIndex_kds;
3736 190 : if (ClearnessIndex_kt < ClearnessIndex_ktc) {
3737 96 : ClearnessIndex_kds = (3.996 - 3.862 * SinSolarAltitude + 1.54 * pow_2(SinSolarAltitude)) * pow_3(ClearnessIndex_kt);
3738 : } else {
3739 188 : ClearnessIndex_kds = ClearnessIndex_kt - (1.107 + 0.03569 * SinSolarAltitude + 1.681 * pow_2(SinSolarAltitude)) *
3740 94 : pow_3(1.0 - ClearnessIndex_kt);
3741 : }
3742 : // Calculate direct normal radiation, W/m2
3743 190 : BeamRad = ZHGlobalSolarConstant * SinSolarAltitude * ClearnessIndex_kds *
3744 190 : ((1.0 - ClearnessIndex_kt) / (1.0 - ClearnessIndex_kds));
3745 : // Calculation diffuse horizontal radiation, W/m2
3746 190 : DiffRad =
3747 190 : ZHGlobalSolarConstant * SinSolarAltitude * ((ClearnessIndex_kt - ClearnessIndex_kds) / (1.0 - ClearnessIndex_kds));
3748 :
3749 190 : } break;
3750 0 : default:
3751 0 : break;
3752 : }
3753 : }
3754 778224 : }
3755 :
3756 : // override result to 0 per environment var (for testing)
3757 778800 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreBeamRadiation) BeamRad = 0.0;
3758 778800 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreDiffuseRadiation) DiffRad = 0.0;
3759 :
3760 778800 : tomorrowTs.BeamSolarRad = BeamRad;
3761 778800 : tomorrowTs.DifSolarRad = DiffRad;
3762 :
3763 : } // Timestep (TS) Loop
3764 : } // Hour Loop
3765 :
3766 : // back-fill hour values from timesteps
3767 : // hour values = integrated over hour ending at time of hour
3768 : // insurance: hourly values not known to be needed
3769 148300 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
3770 142368 : int Hour1Ago = mod(hour + 22, Constant::HoursInDay) + 1;
3771 142368 : auto const &tomorrowHr = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->NumOfTimeStepInHour, hour);
3772 142368 : auto const &tomorrowHr1Ago = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->NumOfTimeStepInHour, Hour1Ago);
3773 :
3774 142368 : Real64 BeamRad = (tomorrowHr1Ago.BeamSolarRad + tomorrowHr.BeamSolarRad) / 2.0;
3775 142368 : Real64 DiffRad = (tomorrowHr1Ago.DifSolarRad + tomorrowHr.DifSolarRad) / 2.0;
3776 142368 : if (state.dataGlobal->NumOfTimeStepInHour > 1) {
3777 777408 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour - 1; ++iTS) {
3778 636432 : BeamRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).BeamSolarRad;
3779 636432 : DiffRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).DifSolarRad;
3780 : }
3781 : }
3782 142368 : Wthr.BeamSolarRad(hour) = BeamRad / state.dataGlobal->NumOfTimeStepInHour;
3783 142368 : Wthr.DifSolarRad(hour) = DiffRad / state.dataGlobal->NumOfTimeStepInHour;
3784 : }
3785 :
3786 5932 : if (envCurr.WP_Type1 != 0) {
3787 :
3788 2 : switch (state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel) {
3789 2 : case SkyTempModel::ScheduleValue: {
3790 4 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
3791 2 : ScheduleManager::GetSingleDayScheduleValues(state, state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).SchedulePtr, tmp);
3792 2 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3793 2 : ForAllHrTs(state, [&state, &tmp, &desDayModsEnvrn](int iHr, int iTS) {
3794 192 : state.dataWeather->wvarsHrTsTomorrow(iTS, iHr).SkyTemp = desDayModsEnvrn(iTS, iHr).SkyTemp = tmp(iTS, iHr);
3795 192 : });
3796 2 : } break;
3797 0 : case SkyTempModel::DryBulbDelta: {
3798 0 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
3799 0 : ScheduleManager::GetSingleDayScheduleValues(state, state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).SchedulePtr, tmp);
3800 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3801 0 : ForAllHrTs(state, [&state, &tmp, &desDayModsEnvrn](int iHr, int iTS) {
3802 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3803 0 : desDayModsEnvrn(iTS, iHr).SkyTemp = tmp(iTS, iHr);
3804 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDryBulbTemp - tmp(iTS, iHr);
3805 0 : });
3806 0 : } break;
3807 0 : case SkyTempModel::DewPointDelta: {
3808 0 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
3809 0 : ScheduleManager::GetSingleDayScheduleValues(state, state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).SchedulePtr, tmp);
3810 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3811 0 : ForAllHrTs(state, [&state, &tmp, &desDayModsEnvrn](int iHr, int iTS) {
3812 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(iTS, iHr);
3813 0 : desDayModsEnvrn(iTS, iHr).SkyTemp = tmp(iTS, iHr);
3814 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDewPointTemp - tmp(iTS, iHr);
3815 0 : });
3816 0 : } break;
3817 0 : default: {
3818 0 : } break;
3819 : } // switch (skyTempModel)
3820 : } // if (envCurr.WP_Type1 != 0)
3821 :
3822 5932 : state.dataGlobal->WarmupFlag = SaveWarmupFlag;
3823 5932 : }
3824 :
3825 20248 : Real64 AirMass(Real64 const CosZen) // COS( solar zenith), 0 - 1
3826 : {
3827 :
3828 : // SUBROUTINE INFORMATION:
3829 : // AUTHOR C Barnaby
3830 : // DATE WRITTEN Nov 2010
3831 :
3832 : // PURPOSE OF THIS SUBROUTINE:
3833 : // Calculate relative air mass using Kasten and Young approximation
3834 :
3835 : // METHODOLOGY EMPLOYED:
3836 : // Eqn (16), ASHRAE HOF 2009, p. 14.9
3837 :
3838 : // REFERENCES:
3839 : // ASHRAE HOF 2009 Chapter 14
3840 : // Kasten, F and T. Young. 1989. Revised optical air mass tables
3841 : // and approximating formula. Applied Optics 28:4735-4738.
3842 :
3843 : Real64 AirMass;
3844 : Real64 SunAltD;
3845 :
3846 20248 : if (CosZen <= 0.001) {
3847 6 : AirMass = 37.07837343; // limit value calc'd with Excel
3848 : // value increases little as CosZen -> 0
3849 20242 : } else if (CosZen >= 1.0) {
3850 0 : AirMass = 1.0;
3851 : } else {
3852 : // note: COS( Zen) = SIN( Alt)
3853 20242 : SunAltD = std::asin(CosZen) / Constant::DegToRadians; // altitude, degrees
3854 20242 : AirMass = 1.0 / (CosZen + 0.50572 * std::pow(6.07995 + SunAltD, -1.6364));
3855 : }
3856 20248 : return AirMass;
3857 : }
3858 :
3859 : //------------------------------------------------------------------------------
3860 :
3861 20248 : void ASHRAETauModel([[maybe_unused]] EnergyPlusData &state,
3862 : DesDaySolarModel const TauModel, // ASHRAETau solar model type ASHRAE_Tau or ASHRAE_Tau2017
3863 : Real64 const ETR, // extraterrestrial normal irradiance, W/m2
3864 : Real64 const CosZen, // COS( solar zenith angle), 0 - 1
3865 : Real64 const TauB, // beam tau factor
3866 : Real64 const TauD, // dif tau factor
3867 : Real64 &IDirN, // returned: direct (beam) irradiance on normal surface, W/m2
3868 : Real64 &IDifH, // returned: diffuse irradiance on horiz surface, W/m2
3869 : Real64 &IGlbH // returned: global irradiance on horiz surface, W/m2
3870 : )
3871 : {
3872 :
3873 : // SUBROUTINE INFORMATION:
3874 : // AUTHOR C Barnaby
3875 : // DATE WRITTEN Nov 2010
3876 :
3877 : // PURPOSE OF THIS SUBROUTINE:
3878 : // Calculate clear-sky direct and diffuse irradiance using ASHRAE "tau" model
3879 :
3880 : // METHODOLOGY EMPLOYED:
3881 : // Eqns (17-18), ASHRAE HOF 2009, p. 14.9
3882 : // Eqns (19-20), ASHRAE HOF 2013 p. 14.9 and 2017 p. 14.10
3883 :
3884 : // REFERENCES:
3885 : // ASHRAE HOF 2009 Chapter 14
3886 :
3887 : Real64 AB; // air mass exponents
3888 : Real64 AD;
3889 : Real64 M; // air mass
3890 :
3891 20248 : if (CosZen < DataEnvironment::SunIsUpValue || TauB <= 0.0 || TauD <= 0.0) {
3892 0 : IDirN = 0.0;
3893 0 : IDifH = 0.0;
3894 0 : IGlbH = 0.0;
3895 : } else {
3896 20248 : if (TauModel == DesDaySolarModel::ASHRAE_Tau) {
3897 20248 : AB = 1.219 - 0.043 * TauB - 0.151 * TauD - 0.204 * TauB * TauD;
3898 20248 : AD = 0.202 + 0.852 * TauB - 0.007 * TauD - 0.357 * TauB * TauD;
3899 : } else {
3900 : // TauModelType == ASHRAE_Tau2017
3901 0 : AB = 1.454 - 0.406 * TauB - 0.268 * TauD + 0.021 * TauB * TauD;
3902 0 : AD = 0.507 + 0.205 * TauB - 0.080 * TauD - 0.190 * TauB * TauD;
3903 : }
3904 20248 : M = AirMass(CosZen);
3905 20248 : IDirN = ETR * std::exp(-TauB * std::pow(M, AB));
3906 20248 : IDifH = ETR * std::exp(-TauD * std::pow(M, AD));
3907 20248 : IGlbH = IDirN * CosZen + IDifH;
3908 : }
3909 20248 : }
3910 :
3911 796 : void AllocateWeatherData(EnergyPlusData &state)
3912 : {
3913 :
3914 : // SUBROUTINE INFORMATION:
3915 : // AUTHOR Linda Lawrie
3916 : // DATE WRITTEN December 2000
3917 :
3918 : // PURPOSE OF THIS SUBROUTINE:
3919 : // This subroutine allocates the weather data structures (Today, Tomorrow,
3920 : // Design Day) to the proper number of "time steps in hour" requested by the user.
3921 : // Interpolation of data is done later after either setting up the design day (hourly
3922 : // data) or reading in hourly weather data.
3923 :
3924 796 : state.dataWeather->wvarsHrTsToday.allocate(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
3925 796 : state.dataWeather->wvarsHrTsTomorrow.allocate(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
3926 796 : }
3927 :
3928 9785 : void CalculateDailySolarCoeffs(EnergyPlusData &state,
3929 : int const DayOfYear, // Day of year (1 - 366)
3930 : Real64 &A, // ASHRAE "A" - Apparent solar irradiation at air mass = 0 [W/M**2]
3931 : Real64 &B, // ASHRAE "B" - Atmospheric extinction coefficient
3932 : Real64 &C, // ASHRAE "C" - Diffuse radiation factor
3933 : Real64 &AnnVarSolConstant, // Annual variation in the solar constant
3934 : Real64 &EquationOfTime, // Equation of Time
3935 : Real64 &SineSolarDeclination, // Sine of Solar Declination
3936 : Real64 &CosineSolarDeclination // Cosine of Solar Declination
3937 : )
3938 : {
3939 :
3940 : // SUBROUTINE INFORMATION:
3941 : // AUTHOR George Walton
3942 : // DATE WRITTEN May 1985
3943 : // MODIFIED 1999 for EnergyPlus
3944 : // RE-ENGINEERED 2001; LKL; Remove need for English -> SI conversion
3945 : // Implement Tarp "fix" for Southern Hemisphere
3946 :
3947 : // PURPOSE OF THIS SUBROUTINE:
3948 : // This subroutine computes the daily solar coefficients used in other
3949 : // calculations. Specifically, this routine computes values of the solar declination, equation
3950 : // of time, and ashrae sky coefficients a, b, and c for a given
3951 : // day of the year.
3952 :
3953 : // METHODOLOGY EMPLOYED:
3954 : // The method is the same as that recommended in the ASHRAE loads
3955 : // algorithms manual, except that the fourier series expressions
3956 : // have been extended by two terms for greater accuracy.
3957 : // coefficients for the new expressions were determined at USACERL
3958 : // using data from the cited references.
3959 :
3960 : // REFERENCES:
3961 : // J. L. Threlkeld, "Thermal Environmental Engineering", 1970,
3962 : // p.316, for declination and equation of time.
3963 : // "ASHRAE Handbook of Fundamentals", 1972, p.387 for sky
3964 : // coefficients a, b, and c.
3965 : // See SUN3 in SolarShading. See SUN2 in BLAST. See SUN3 in Tarp.
3966 :
3967 9785 : Real64 const DayCorrection(Constant::Pi * 2.0 / 366.0);
3968 :
3969 : // Fitted coefficients of Fourier series | Sine of declination coefficients
3970 : static constexpr std::array<Real64, 9> SineSolDeclCoef = {
3971 : 0.00561800, 0.0657911, -0.392779, 0.00064440, -0.00618495, -0.00010101, -0.00007951, -0.00011691, 0.00002096};
3972 : // Fitted coefficients of Fourier Series | Equation of Time coefficients
3973 : static constexpr std::array<Real64, 9> EqOfTimeCoef = {
3974 : 0.00021971, -0.122649, 0.00762856, -0.156308, -0.0530028, -0.00388702, -0.00123978, -0.00270502, -0.00167992};
3975 : // Fitted coefficients of Fourier Series | ASHRAE A Factor coefficients
3976 : 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};
3977 : // Fitted coefficients of Fourier Series | ASHRAE B Factor coefficients
3978 : static constexpr std::array<Real64, 9> ASHRAE_B_Coef = {
3979 : 0.171631, -0.00400448, -0.0344923, 0.00000209, 0.00325428, -0.00085429, 0.00229562, 0.0009034, -0.0011867};
3980 : // Fitted coefficients of Fourier Series | ASHRAE C Factor coefficients
3981 : static constexpr std::array<Real64, 9> ASHRAE_C_Coef = {
3982 : 0.0905151, -0.00322522, -0.0407966, 0.000104164, 0.00745899, -0.00086461, 0.0013111, 0.000808275, -0.00170515};
3983 :
3984 : // Day of Year in Radians (Computed from Input DayOfYear)
3985 9785 : Real64 X = DayCorrection * DayOfYear; // Convert Julian date (Day of Year) to angle X
3986 :
3987 : // Calculate sines and cosines of X
3988 9785 : Real64 SinX = std::sin(X);
3989 9785 : Real64 CosX = std::cos(X);
3990 :
3991 9785 : SineSolarDeclination = SineSolDeclCoef[0] + SineSolDeclCoef[1] * SinX + SineSolDeclCoef[2] * CosX + SineSolDeclCoef[3] * (SinX * CosX * 2.0) +
3992 9785 : SineSolDeclCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
3993 9785 : SineSolDeclCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
3994 9785 : SineSolDeclCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
3995 9785 : SineSolDeclCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
3996 9785 : SineSolDeclCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
3997 9785 : CosineSolarDeclination = std::sqrt(1.0 - pow_2(SineSolarDeclination));
3998 :
3999 9785 : EquationOfTime = EqOfTimeCoef[0] + EqOfTimeCoef[1] * SinX + EqOfTimeCoef[2] * CosX + EqOfTimeCoef[3] * (SinX * CosX * 2.0) +
4000 9785 : EqOfTimeCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4001 9785 : EqOfTimeCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4002 9785 : EqOfTimeCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4003 9785 : EqOfTimeCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4004 9785 : EqOfTimeCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4005 :
4006 9785 : AnnVarSolConstant = 1.000047 + 0.000352615 * SinX + 0.0334454 * CosX;
4007 :
4008 9785 : A = ASHRAE_A_Coef[0] + ASHRAE_A_Coef[1] * SinX + ASHRAE_A_Coef[2] * CosX + ASHRAE_A_Coef[3] * (SinX * CosX * 2.0) +
4009 9785 : 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)) +
4010 9785 : ASHRAE_A_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4011 9785 : ASHRAE_A_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4012 9785 : ASHRAE_A_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4013 :
4014 : // Compute B and C coefficients
4015 :
4016 9785 : if (state.dataEnvrn->Latitude < 0.0) {
4017 : // If in southern hemisphere, compute B and C with a six month time shift.
4018 0 : X -= Constant::Pi;
4019 0 : SinX = std::sin(X);
4020 0 : CosX = std::cos(X);
4021 : }
4022 :
4023 9785 : B = ASHRAE_B_Coef[0] + ASHRAE_B_Coef[1] * SinX + ASHRAE_B_Coef[2] * CosX + ASHRAE_B_Coef[3] * (SinX * CosX * 2.0) +
4024 9785 : 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)) +
4025 9785 : ASHRAE_B_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4026 9785 : ASHRAE_B_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4027 9785 : ASHRAE_B_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4028 :
4029 9785 : C = ASHRAE_C_Coef[0] + ASHRAE_C_Coef[1] * SinX + ASHRAE_C_Coef[2] * CosX + ASHRAE_C_Coef[3] * (SinX * CosX * 2.0) +
4030 9785 : 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)) +
4031 9785 : ASHRAE_C_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4032 9785 : ASHRAE_C_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4033 9785 : ASHRAE_C_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4034 9785 : }
4035 :
4036 778224 : void CalculateSunDirectionCosines(EnergyPlusData &state,
4037 : Real64 const TimeValue, // Current Time of Day
4038 : Real64 const EqOfTime, // Equation of Time
4039 : Real64 const SinSolDeclin, // Sine of Solar Declination
4040 : Real64 const CosSolDeclin, // Cosine of Solar Declination
4041 : Vector3<Real64> &SUNCOS)
4042 : {
4043 :
4044 : // SUBROUTINE INFORMATION:
4045 : // AUTHOR George Walton
4046 : // DATE WRITTEN May 1975
4047 : // MODIFIED 1999 for EnergyPlus
4048 :
4049 : // PURPOSE OF THIS SUBROUTINE:
4050 : // This routine computes the solar direction cosines for hourly
4051 : // radiation calculations.
4052 :
4053 : // REFERENCES:
4054 : // "NECAP Engineering Manual", 1974, p.3-117
4055 :
4056 778224 : EP_SIZE_CHECK(SUNCOS, 3); // NOLINT(misc-static-assert)
4057 :
4058 : // COMPUTE THE HOUR ANGLE
4059 : Real64 H =
4060 778224 : (15.0 * (12.0 - (TimeValue + EqOfTime)) + (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude)) * Constant::DegToRadians;
4061 778224 : Real64 COSH = std::cos(H);
4062 : // COMPUTE THE COSINE OF THE SOLAR ZENITH ANGLE.
4063 : // This is also the Sine of the Solar Altitude Angle
4064 :
4065 778224 : SUNCOS.z = SinSolDeclin * state.dataEnvrn->SinLatitude + CosSolDeclin * state.dataEnvrn->CosLatitude * COSH;
4066 :
4067 778224 : if (SUNCOS.z >= DataEnvironment::SunIsUpValue) { // If Sun above horizon, compute other direction cosines
4068 391951 : SUNCOS.y = SinSolDeclin * state.dataEnvrn->CosLatitude - CosSolDeclin * state.dataEnvrn->SinLatitude * COSH;
4069 391951 : SUNCOS.x = CosSolDeclin * std::sin(H);
4070 : } else { // Sun is down, set to 0.0
4071 386273 : SUNCOS.x = 0.0;
4072 386273 : SUNCOS.y = 0.0;
4073 : }
4074 778224 : }
4075 :
4076 2900842 : void DetermineSunUpDown(EnergyPlusData &state, Vector3<Real64> &SunCOS)
4077 : {
4078 :
4079 : // SUBROUTINE INFORMATION:
4080 : // AUTHOR Linda Lawrie
4081 : // DATE WRITTEN 1999
4082 :
4083 : // PURPOSE OF THIS SUBROUTINE:
4084 : // This subroutine determines if the sun is up or down for the current
4085 : // hour/timestep.
4086 :
4087 : // REFERENCES:
4088 : // Sun routines from IBLAST, authored by Walton.
4089 :
4090 : // COMPUTE THE HOUR ANGLE
4091 2900842 : if (state.dataGlobal->NumOfTimeStepInHour != 1) {
4092 5776448 : state.dataWeather->HrAngle = (15.0 * (12.0 - (state.dataGlobal->CurrentTime + state.dataWeather->TodayVariables.EquationOfTime)) +
4093 2888224 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4094 : } else {
4095 12618 : state.dataWeather->HrAngle =
4096 12618 : (15.0 *
4097 12618 : (12.0 - ((state.dataGlobal->CurrentTime + state.dataEnvrn->TS1TimeOffset) + state.dataWeather->TodayVariables.EquationOfTime)) +
4098 12618 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4099 : }
4100 2900842 : Real64 H = state.dataWeather->HrAngle * Constant::DegToRadians;
4101 :
4102 : // Compute the Cosine of the Solar Zenith (Altitude) Angle.
4103 2900842 : Real64 CosZenith = state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle +
4104 2900842 : state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H);
4105 :
4106 2900842 : Real64 SolarZenith = std::acos(CosZenith);
4107 2900842 : Real64 SinAltitude = state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H) +
4108 2900842 : state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle;
4109 2900842 : Real64 SolarAltitude = std::asin(SinAltitude);
4110 2900842 : Real64 CosAzimuth = -(state.dataEnvrn->SinLatitude * CosZenith - state.dataWeather->TodayVariables.SinSolarDeclinAngle) /
4111 2900842 : (state.dataEnvrn->CosLatitude * std::sin(SolarZenith));
4112 : // Following because above can yield invalid cos value. (e.g. at south pole)
4113 2900842 : CosAzimuth = max(CosAzimuth, -1.0);
4114 2900842 : CosAzimuth = min(1.0, CosAzimuth);
4115 2900842 : Real64 SolarAzimuth = std::acos(CosAzimuth);
4116 :
4117 2900842 : state.dataWeather->SolarAltitudeAngle = SolarAltitude / Constant::DegToRadians;
4118 2900842 : state.dataWeather->SolarAzimuthAngle = SolarAzimuth / Constant::DegToRadians;
4119 2900842 : if (state.dataWeather->HrAngle < 0.0) {
4120 1453014 : state.dataWeather->SolarAzimuthAngle = 360.0 - state.dataWeather->SolarAzimuthAngle;
4121 : }
4122 :
4123 2900842 : SunCOS.z = CosZenith;
4124 2900842 : state.dataEnvrn->SunIsUpPrevTS = state.dataEnvrn->SunIsUp;
4125 2900842 : if (CosZenith < DataEnvironment::SunIsUpValue) {
4126 1450870 : state.dataEnvrn->SunIsUp = false;
4127 1450870 : SunCOS.y = 0.0;
4128 1450870 : SunCOS.x = 0.0;
4129 : } else {
4130 1449972 : state.dataEnvrn->SunIsUp = true;
4131 1449972 : SunCOS.y = state.dataWeather->TodayVariables.SinSolarDeclinAngle * state.dataEnvrn->CosLatitude -
4132 1449972 : state.dataWeather->TodayVariables.CosSolarDeclinAngle * state.dataEnvrn->SinLatitude * std::cos(H);
4133 1449972 : SunCOS.x = state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::sin(H);
4134 : }
4135 2900842 : }
4136 :
4137 796 : void OpenWeatherFile(EnergyPlusData &state, bool &ErrorsFound)
4138 : {
4139 :
4140 : // SUBROUTINE INFORMATION:
4141 : // AUTHOR Linda Lawrie
4142 : // DATE WRITTEN June 1999
4143 :
4144 : // PURPOSE OF THIS SUBROUTINE:
4145 : // This subroutine checks to see if a weather file and what kind of weather file
4146 : // exists in the working directory and calls appropriate routines to
4147 : // open the files and set up for use.
4148 :
4149 796 : state.dataWeather->WeatherFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
4150 796 : if (state.dataWeather->WeatherFileExists) {
4151 788 : OpenEPlusWeatherFile(state, ErrorsFound, true);
4152 : }
4153 796 : }
4154 :
4155 4537 : void OpenEPlusWeatherFile(EnergyPlusData &state,
4156 : bool &ErrorsFound, // Will be set to true if errors found
4157 : bool const ProcessHeader // Set to true when headers should be processed (rather than just read)
4158 : )
4159 : {
4160 :
4161 : // SUBROUTINE INFORMATION:
4162 : // AUTHOR Linda K. Lawrie
4163 : // DATE WRITTEN June 1999
4164 :
4165 : // PURPOSE OF THIS SUBROUTINE:
4166 : // This subroutine opens the EnergyPlus Weather File (in.epw) and processes
4167 : // the initial header records.
4168 :
4169 : // METHODOLOGY EMPLOYED:
4170 : // List directed reads, as possible.
4171 :
4172 4537 : state.files.inputWeatherFile.close();
4173 4537 : state.files.inputWeatherFile.filePath = state.files.inputWeatherFilePath.filePath;
4174 4537 : state.files.inputWeatherFile.open();
4175 4537 : if (!state.files.inputWeatherFile.good()) {
4176 0 : ShowFatalError(state, "OpenWeatherFile: Could not OPEN EPW Weather File", OptionalOutputFileRef(state.files.eso));
4177 : }
4178 :
4179 4537 : if (ProcessHeader) {
4180 : // Read in Header Information
4181 :
4182 : // Headers should come in order
4183 7092 : for (int typeNum = static_cast<int>(EpwHeaderType::Location); typeNum < static_cast<int>(EpwHeaderType::Num); ++typeNum) {
4184 6304 : auto Line = state.files.inputWeatherFile.readLine();
4185 6304 : if (Line.eof) {
4186 0 : ShowFatalError(
4187 : state,
4188 0 : format("OpenWeatherFile: Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}",
4189 0 : epwHeaders[typeNum]),
4190 0 : OptionalOutputFileRef(state.files.eso));
4191 : }
4192 :
4193 6304 : int endcol = len(Line.data);
4194 6304 : if (endcol > 0) {
4195 6304 : if (int(Line.data[endcol - 1]) == DataSystemVariables::iUnicode_end) {
4196 0 : ShowSevereError(state,
4197 : "OpenWeatherFile: EPW Weather File appears to be a Unicode or binary file.",
4198 0 : OptionalOutputFileRef(state.files.eso));
4199 0 : ShowContinueError(state, "...This file cannot be read by this program. Please save as PC or Unix file and try again");
4200 0 : ShowFatalError(state, "Program terminates due to previous condition.");
4201 : }
4202 : }
4203 6304 : std::string::size_type const Pos = FindNonSpace(Line.data);
4204 6304 : std::string::size_type const HdPos = index(Line.data, epwHeaders[typeNum]);
4205 6304 : if (Pos != HdPos) continue;
4206 6304 : ProcessEPWHeader(state, static_cast<EpwHeaderType>(typeNum), Line.data, ErrorsFound);
4207 6304 : }
4208 : } else { // Header already processed, just read
4209 3749 : SkipEPlusWFHeader(state);
4210 : }
4211 4537 : }
4212 :
4213 12057 : void CloseWeatherFile(EnergyPlusData &state)
4214 : {
4215 12057 : state.files.inputWeatherFile.close();
4216 12057 : }
4217 :
4218 796 : void ResolveLocationInformation(EnergyPlusData &state, bool &ErrorsFound) // Set to true if no location evident
4219 : {
4220 :
4221 : // SUBROUTINE INFORMATION:
4222 : // AUTHOR Rick Strand
4223 : // DATE WRITTEN June 1997
4224 :
4225 : // PURPOSE OF THIS SUBROUTINE:
4226 : // This subroutine is currently the main interface between the old data
4227 : // structure on the BLAST Weather file and the new data structure contained
4228 : // in this module. At some point, this subroutine will be converted
4229 : // to read information directly from the new input file.
4230 :
4231 1567 : if (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather &&
4232 771 : state.dataWeather->WeatherFileExists) {
4233 763 : if (state.dataWeather->LocationGathered) {
4234 : // See if "matching" location
4235 756 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4236 1461 : if (std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude) > 1.0 ||
4237 1412 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude) > 1.0 ||
4238 2167 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone) > 0.0 ||
4239 706 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) / max(state.dataEnvrn->Elevation, 1.0) >
4240 : 0.10) {
4241 71 : ShowWarningError(state, "Weather file location will be used rather than entered (IDF) Location object.");
4242 71 : ShowContinueError(state, format("..Location object={}", state.dataWeather->LocationTitle));
4243 71 : ShowContinueError(state, format("..Weather File Location={}", state.dataEnvrn->WeatherFileLocationTitle));
4244 142 : ShowContinueError(
4245 : state,
4246 142 : format("..due to location differences, Latitude difference=[{:.2R}] degrees, Longitude difference=[{:.2R}] degrees.",
4247 71 : std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude),
4248 71 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude)));
4249 142 : ShowContinueError(state,
4250 142 : format("..Time Zone difference=[{:.1R}] hour(s), Elevation difference=[{:.2R}] percent, [{:.2R}] meters.",
4251 71 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone),
4252 142 : std::abs((state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) /
4253 71 : max(state.dataEnvrn->Elevation, 1.0) * 100.0),
4254 142 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation)));
4255 : }
4256 : }
4257 : }
4258 763 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4259 762 : state.dataWeather->LocationTitle = state.dataEnvrn->WeatherFileLocationTitle;
4260 762 : state.dataEnvrn->Latitude = state.dataWeather->WeatherFileLatitude;
4261 762 : state.dataEnvrn->Longitude = state.dataWeather->WeatherFileLongitude;
4262 762 : state.dataEnvrn->TimeZoneNumber = state.dataWeather->WeatherFileTimeZone;
4263 762 : state.dataEnvrn->Elevation = state.dataWeather->WeatherFileElevation;
4264 : }
4265 33 : } else if (!state.dataWeather->LocationGathered) {
4266 0 : state.dataWeather->LocationTitle = "Not Entered";
4267 0 : ShowSevereError(state, "No Location given. Must have location information for simulation.");
4268 0 : ErrorsFound = true;
4269 : }
4270 :
4271 796 : if (!ErrorsFound) {
4272 796 : state.dataEnvrn->StdBaroPress = DataEnvironment::StdPressureSeaLevel * std::pow(1.0 - 2.25577e-05 * state.dataEnvrn->Elevation, 5.2559);
4273 1592 : state.dataEnvrn->StdRhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(
4274 796 : state, state.dataEnvrn->StdBaroPress, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
4275 : // Write Final Location Information to the initialization output file
4276 : static constexpr std::string_view LocHdFormat(
4277 : "! <Site:Location>, Location Name, Latitude {N+/S- Deg}, Longitude {E+/W- Deg}, Time Zone Number "
4278 : "{GMT+/-}, Elevation {m}, Standard Pressure at Elevation {Pa}, Standard RhoAir at Elevation\n");
4279 796 : print(state.files.eio, "{}", LocHdFormat);
4280 :
4281 : static constexpr std::string_view LocFormat("Site:Location,{},{:.2R},{:.2R},{:.2R},{:.2R},{:.0R},{:.4R}\n");
4282 796 : print(state.files.eio,
4283 : LocFormat,
4284 796 : state.dataWeather->LocationTitle,
4285 796 : state.dataEnvrn->Latitude,
4286 796 : state.dataEnvrn->Longitude,
4287 796 : state.dataEnvrn->TimeZoneNumber,
4288 796 : state.dataEnvrn->Elevation,
4289 796 : state.dataEnvrn->StdBaroPress,
4290 796 : state.dataEnvrn->StdRhoAir);
4291 : }
4292 796 : }
4293 :
4294 2140 : void CheckLocationValidity(EnergyPlusData &state)
4295 : {
4296 :
4297 : // SUBROUTINE INFORMATION:
4298 : // AUTHOR Rick Strand
4299 : // DATE WRITTEN June 1997
4300 :
4301 : // PURPOSE OF THIS SUBROUTINE:
4302 : // This subroutine is checks to see whether the user specified location
4303 : // or the weather file location (if one exists) is valid. The standard
4304 : // time meridian is also calculated and compared to the user supplied
4305 : // or weather file time zone number.
4306 :
4307 2140 : bool LocationError = false; // Set to true if there is a problem detected
4308 :
4309 2140 : if ((state.dataEnvrn->Latitude == -999.0) && (state.dataEnvrn->Longitude == -999.0) && (state.dataEnvrn->TimeZoneNumber != -999.0)) {
4310 0 : ShowSevereError(state, "No location specified");
4311 0 : LocationError = true;
4312 : }
4313 :
4314 2140 : if ((state.dataEnvrn->Latitude < -90.0) || (state.dataEnvrn->Latitude > 90.0)) {
4315 0 : ShowSevereError(state, format("Latitude must be between -90 and 90; Entered={:.2R}", state.dataEnvrn->Latitude));
4316 0 : LocationError = true;
4317 : }
4318 :
4319 2140 : if ((state.dataEnvrn->Longitude < -180.0) || (state.dataEnvrn->Longitude > 180.0)) {
4320 0 : ShowSevereError(state, format("Longitude must be between -180 and 180; Entered={:.2R}", state.dataEnvrn->Longitude));
4321 0 : LocationError = true;
4322 : }
4323 :
4324 2140 : if ((state.dataEnvrn->TimeZoneNumber < -12.00) || (state.dataEnvrn->TimeZoneNumber > 14.00)) {
4325 0 : ShowSevereError(state, format("Time Zone must be between -12 and +14; Entered={:.2R}", state.dataEnvrn->TimeZoneNumber));
4326 0 : LocationError = true;
4327 : }
4328 :
4329 2140 : Real64 const StdTimeMerid = GetSTM(state.dataEnvrn->Longitude); // Standard time meridian.
4330 :
4331 : // Compare the standard time meridian with the time zone number. If
4332 : // different, notify the user. If StdTimeMerid couldn't be calculated,
4333 : // produce an error message.
4334 :
4335 2140 : if (state.dataEnvrn->varyingLocationSchedIndexLat > 0 || state.dataEnvrn->varyingLocationSchedIndexLong > 0) {
4336 : // don't do any warnings, the building is moving
4337 796 : } else if (StdTimeMerid >= -12.0 && StdTimeMerid <= 12.0) {
4338 796 : if (state.dataEnvrn->TimeZoneNumber != StdTimeMerid) {
4339 : // Difference between Standard Time Meridian and TimeZone
4340 9 : Real64 const DiffCalc = std::abs(state.dataEnvrn->TimeZoneNumber - StdTimeMerid);
4341 9 : if (DiffCalc > 1.0 && DiffCalc < 24.0) {
4342 0 : if (DiffCalc < 3.0) {
4343 0 : ShowWarningError(state,
4344 0 : format("Standard Time Meridian and Time Zone differ by more than 1, Difference=\"{:.1R}\"", DiffCalc));
4345 0 : ShowContinueError(state, "Solar Positions may be incorrect");
4346 : } else {
4347 0 : ShowSevereError(state, format("Standard Time Meridian and Time Zone differ by more than 2, Difference=\"{:.1R}\"", DiffCalc));
4348 0 : ShowContinueError(state, "Solar Positions will be incorrect");
4349 : // LocationError=.TRUE.
4350 : }
4351 : }
4352 : }
4353 796 : } else {
4354 0 : ShowSevereError(state, "Unable to calculate the standard time meridian");
4355 0 : LocationError = true;
4356 : }
4357 :
4358 : // Error handling: if there are any errors in the location information
4359 : // the simulation must be terminated
4360 :
4361 2140 : if (LocationError) {
4362 0 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4363 : }
4364 :
4365 2140 : if (state.dataEnvrn->TimeZoneNumber <= 12.00) {
4366 2140 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0;
4367 : } else {
4368 0 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0 - 360.0;
4369 : }
4370 2140 : state.dataEnvrn->SinLatitude = std::sin(Constant::DegToRadians * state.dataEnvrn->Latitude);
4371 2140 : state.dataEnvrn->CosLatitude = std::cos(Constant::DegToRadians * state.dataEnvrn->Latitude);
4372 :
4373 2140 : if (state.dataEnvrn->Latitude == 0.0 && state.dataEnvrn->Longitude == 0.0 && state.dataEnvrn->TimeZoneNumber == 0.0) {
4374 0 : ShowWarningError(state,
4375 : "Did you realize that you have Latitude=0.0, Longitude=0.0 and TimeZone=0.0? Your building site is in the middle of "
4376 : "the Atlantic Ocean.");
4377 : }
4378 2140 : }
4379 :
4380 771 : void CheckWeatherFileValidity(EnergyPlusData &state)
4381 : {
4382 :
4383 : // SUBROUTINE INFORMATION:
4384 : // AUTHOR Linda Lawrie
4385 : // DATE WRITTEN February 1977
4386 : // MODIFIED June 1997 (RKS)
4387 :
4388 : // PURPOSE OF THIS SUBROUTINE:
4389 : // This subroutine contains a portion of the legacy subroutine CKBLDE.
4390 : // The main purpose of this routine is to check the validity of the
4391 : // weather dates provided by the user and the attached weather file.
4392 : // These functions may eventually be pushed to an interface. This
4393 : // routine also sends the weather file header information at the
4394 : // Environment derived type.
4395 :
4396 771 : if (!state.dataWeather->WeatherFileExists) { // No weather file exists but the user requested one--print error message
4397 :
4398 8 : if (state.dataGlobal->DoWeathSim) {
4399 0 : ShowSevereError(state, "GetNextEnvironment: Weather Environment(s) requested, but no weather file found");
4400 0 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4401 : }
4402 :
4403 : } // ... end of WeatherFileExists IF-THEN
4404 771 : }
4405 :
4406 796 : void ReportOutputFileHeaders(EnergyPlusData &state)
4407 : {
4408 :
4409 : // SUBROUTINE INFORMATION:
4410 : // AUTHOR Rick Strand
4411 : // DATE WRITTEN June 1997
4412 : // MODIFIED December 2017; Jason DeGraw
4413 :
4414 : // PURPOSE OF THIS SUBROUTINE:
4415 : // This subroutine prints out the necessary header information required
4416 : // by the EnergyPlus output file format. This subroutine can be
4417 : // replicated in any other modules which must send data to the output
4418 : // file.
4419 :
4420 : // METHODOLOGY EMPLOYED:
4421 : // For each report, the report flag integer must be saved from the
4422 : // global report number counter. Then, the report counter must be
4423 : // incremented. Finally, the header information for the report must
4424 : // be sent to the output file.
4425 :
4426 : using OutputProcessor::ReportFreq;
4427 :
4428 : static constexpr std::string_view EnvironmentString(",5,Environment Title[],Latitude[deg],Longitude[deg],Time Zone[],Elevation[m]");
4429 :
4430 : static constexpr std::array<std::string_view, (int)ReportFreq::Num> freqStrings = {
4431 : "", // No EachCall string
4432 : ",8,Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],Hour[],StartMinute[],EndMinute[],DayType",
4433 : "", // No Hour string
4434 : ",5,Cumulative Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],DayType ! When Daily ",
4435 : ",2,Cumulative Days of Simulation[],Month[] ! When Monthly ",
4436 : ",1,Cumulative Days of Simulation[] ! When Run Period ",
4437 : ",1,Calendar Year of Simulation[] ! When Annual "};
4438 :
4439 796 : auto &op = state.dataOutputProcessor;
4440 :
4441 796 : state.dataWeather->EnvironmentReportNbr = ++op->ReportNumberCounter;
4442 796 : if (state.dataWeather->EnvironmentReportNbr != 1) { // problem
4443 0 : ShowFatalError(state, "ReportOutputFileHeaders: Assigned report number for Environment title is not 1. Contact Support.");
4444 : }
4445 796 : state.dataWeather->EnvironmentReportChr = fmt::to_string(state.dataWeather->EnvironmentReportNbr);
4446 796 : strip(state.dataWeather->EnvironmentReportChr);
4447 796 : print(state.files.eso, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4448 796 : print(state.files.mtr, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4449 :
4450 : // TImeStep and Hour share a stamp
4451 796 : op->freqStampReportNums[(int)ReportFreq::Hour] = op->freqStampReportNums[(int)ReportFreq::TimeStep] = ++op->ReportNumberCounter;
4452 796 : print(state.files.eso, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4453 796 : print(state.files.mtr, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4454 :
4455 3980 : for (ReportFreq freq : {ReportFreq::Day, ReportFreq::Month, ReportFreq::Simulation, ReportFreq::Year}) {
4456 3184 : op->freqStampReportNums[(int)freq] = ++op->ReportNumberCounter;
4457 3184 : print(state.files.eso, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Report Variables Requested");
4458 3184 : print(state.files.mtr, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Meters Requested");
4459 : }
4460 796 : }
4461 :
4462 2900842 : void ReportWeatherAndTimeInformation(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
4463 : {
4464 :
4465 : // SUBROUTINE INFORMATION:
4466 : // AUTHOR Rick Strand
4467 : // DATE WRITTEN June 1997
4468 :
4469 : // PURPOSE OF THIS SUBROUTINE:
4470 : // This subroutine is the main driver of the weather reporting. This
4471 : // routine is also responsible for printing the time and environment
4472 : // stamps.
4473 :
4474 : // METHODOLOGY EMPLOYED:
4475 : // Reporting is only done for non-warmup days. The environment stamp
4476 : // is only reported at the beginning of an environment, but after the
4477 : // warmup days (to allow all modules to print the report headers to the
4478 : // output file. This is controlled by the PrintEnvrnStamp variable
4479 : // which is passed in and reset if necessary.
4480 :
4481 : // Report the time stamp and the current weather to the output file
4482 :
4483 2900842 : if (!state.dataGlobal->WarmupFlag && !state.dataWeather->RPReadAllWeatherData) { // Write the required output information
4484 :
4485 : // The first time through in a non-warmup day, the environment header
4486 : // must be printed. This must be done here and not in the generic
4487 : // DataGlobals::BeginEnvrnFlag block above because other modules in the simulation
4488 : // must also print out header information. This can be done during
4489 : // the simulation warmup if the environment stamp printing is delayed
4490 : // until the warmup is completed. The stamp should only be printed once
4491 : // per environment (set/reset of PrintEnvrnStamp). In addition, before
4492 : // the first environment, the end of the header block flag must also be
4493 : // sent to the output file.
4494 :
4495 689184 : if (printEnvrnStamp) {
4496 :
4497 208576 : if (state.dataReportFlag->PrintEndDataDictionary && state.dataGlobal->DoOutputReporting) {
4498 : static constexpr std::string_view EndOfHeaderString("End of Data Dictionary"); // End of data dictionary marker
4499 791 : print(state.files.eso, "{}\n", EndOfHeaderString);
4500 791 : print(state.files.mtr, "{}\n", EndOfHeaderString);
4501 791 : state.dataReportFlag->PrintEndDataDictionary = false;
4502 : }
4503 208576 : if (state.dataGlobal->DoOutputReporting) {
4504 1696 : std::string const &Title = state.dataWeather->Environment(state.dataWeather->Envrn).Title;
4505 : static constexpr std::string_view EnvironmentStampFormatStr(
4506 : "{},{},{:7.2F},{:7.2F},{:7.2F},{:7.2F}\n"); // Format descriptor for environ stamp
4507 1696 : print(state.files.eso,
4508 : EnvironmentStampFormatStr,
4509 1696 : state.dataWeather->EnvironmentReportChr,
4510 : Title,
4511 1696 : state.dataEnvrn->Latitude,
4512 1696 : state.dataEnvrn->Longitude,
4513 1696 : state.dataEnvrn->TimeZoneNumber,
4514 1696 : state.dataEnvrn->Elevation);
4515 1696 : print(state.files.mtr,
4516 : EnvironmentStampFormatStr,
4517 1696 : state.dataWeather->EnvironmentReportChr,
4518 : Title,
4519 1696 : state.dataEnvrn->Latitude,
4520 1696 : state.dataEnvrn->Longitude,
4521 1696 : state.dataEnvrn->TimeZoneNumber,
4522 1696 : state.dataEnvrn->Elevation);
4523 1696 : printEnvrnStamp = false;
4524 : }
4525 : }
4526 : } // ... end of .NOT.WarmupFlag IF-THEN block.
4527 2900842 : }
4528 :
4529 796 : void ReadUserWeatherInput(EnergyPlusData &state)
4530 : {
4531 :
4532 : // SUBROUTINE INFORMATION:
4533 : // AUTHOR Richard Liesen
4534 : // DATE WRITTEN September 1997
4535 :
4536 : // PURPOSE OF THIS SUBROUTINE:
4537 : // This subroutine is the main driver of the weather manager module.
4538 : // It controls the assignment of weather related global variables as
4539 : // well as the reads and writes for retrieving weather information.
4540 :
4541 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4542 796 : bool ErrorsFound(false);
4543 :
4544 : // Get the number of design days and annual runs from user inpout
4545 796 : state.dataEnvrn->TotDesDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:DesignDay");
4546 796 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
4547 796 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
4548 796 : state.dataWeather->TotRunPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriod");
4549 796 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4550 796 : state.dataGlobal->WeathSimReq = state.dataWeather->TotRunPers > 0;
4551 796 : state.dataWeather->TotReportPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Output:Table:ReportPeriod");
4552 : #ifdef GET_OUT
4553 : state.dataWeather->SPSiteScheduleNamePtr.allocate(state.dataEnvrn->TotDesDays * 5);
4554 : state.dataWeather->SPSiteScheduleUnits.allocate(state.dataEnvrn->TotDesDays * 5);
4555 :
4556 : state.dataWeather->SPSiteScheduleNamePtr = 0;
4557 : state.dataWeather->SPSiteScheduleUnits = "";
4558 : #endif //
4559 : // Allocate the Design Day and Environment array to the # of DD's or/and
4560 : // Annual runs on input file
4561 796 : state.dataWeather->DesignDay.allocate(state.dataEnvrn->TotDesDays);
4562 796 : state.dataWeather->Environment.allocate(state.dataWeather->NumOfEnvrn);
4563 :
4564 : // Set all Environments to DesignDay and then the weather environment will be set
4565 : // in the get annual run data subroutine
4566 2462 : for (int Env = 1; Env <= state.dataEnvrn->TotDesDays; ++Env) {
4567 1666 : state.dataWeather->Environment(Env).KindOfEnvrn = Constant::KindOfSim::DesignDay;
4568 : }
4569 801 : for (int Env = 1; Env <= RPD1 + RPD2; ++Env) {
4570 5 : if (!state.dataSysVars->DDOnly) {
4571 0 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
4572 : } else {
4573 5 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4574 : }
4575 : }
4576 1981 : for (int Env = 1; Env <= state.dataWeather->TotRunPers; ++Env) {
4577 1185 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + RPD1 + RPD2 + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4578 : }
4579 :
4580 796 : if (state.dataEnvrn->TotDesDays >= 1) {
4581 796 : GetDesignDayData(state, state.dataEnvrn->TotDesDays, ErrorsFound);
4582 : }
4583 :
4584 796 : if (RPD1 >= 1 || RPD2 >= 1) {
4585 3 : GetRunPeriodDesignData(state, ErrorsFound);
4586 : }
4587 :
4588 : // the last environment(s) is designated the weather environment if an annual run
4589 : // is selected. All of the design systems is done from the design day info
4590 : // which will have to be completed to run the annual run.
4591 796 : if (state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4592 771 : GetRunPeriodData(state, state.dataWeather->TotRunPers, ErrorsFound);
4593 : }
4594 :
4595 796 : if (state.dataWeather->TotReportPers > 0) {
4596 4 : GetReportPeriodData(state, state.dataWeather->TotReportPers, ErrorsFound);
4597 4 : GroupReportPeriodByType(state, state.dataWeather->TotReportPers);
4598 : }
4599 :
4600 796 : if (state.dataSysVars->FullAnnualRun) {
4601 : // GetRunPeriodData may have reset the value of TotRunPers
4602 6 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4603 : }
4604 :
4605 796 : if (RPD1 >= 1 || RPD2 >= 1 || state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4606 771 : GetSpecialDayPeriodData(state, ErrorsFound);
4607 771 : GetDSTData(state, ErrorsFound);
4608 771 : if (state.dataWeather->IDFDaylightSaving) {
4609 179 : state.dataWeather->DST = state.dataWeather->IDFDST;
4610 : }
4611 : }
4612 :
4613 796 : GetLocationInfo(state, ErrorsFound);
4614 :
4615 796 : GetGroundTemps(state);
4616 :
4617 796 : GetGroundReflectances(state, ErrorsFound);
4618 :
4619 796 : GetSnowGroundRefModifiers(state, ErrorsFound);
4620 :
4621 796 : GetWaterMainsTemperatures(state, ErrorsFound);
4622 :
4623 796 : GetWeatherStation(state, ErrorsFound);
4624 :
4625 796 : SetupEnvironmentTypes(state);
4626 :
4627 796 : GetWeatherProperties(state, ErrorsFound);
4628 : #ifdef GET_OUT
4629 : // Deallocate ones used for schedule pointers
4630 : state.dataWeather->SPSiteScheduleNamePtr.deallocate();
4631 : state.dataWeather->SPSiteScheduleUnits.deallocate();
4632 : #endif //
4633 796 : if (ErrorsFound) {
4634 0 : ShowFatalError(state, "GetWeatherInput: Above errors cause termination");
4635 : }
4636 796 : }
4637 :
4638 1033 : static int findYearForWeekday(int const month, int const day, ScheduleManager::DayType const weekday)
4639 : {
4640 : // Find a year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
4641 : // the date with the weekday specified.
4642 :
4643 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4644 : static std::array<int, 13> const defaultYear{{2013, 2014, 2015, 2010, 2011, 2017, 2007, 2013, 2014, 2015, 2010, 2011, 2017}};
4645 :
4646 1033 : int rem = calculateDayOfYear(month, day) % 7;
4647 1033 : return defaultYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4648 : }
4649 :
4650 0 : static int findLeapYearForWeekday(int const month, int const day, ScheduleManager::DayType const weekday)
4651 : {
4652 : // 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
4653 : // the date with the weekday specified.
4654 :
4655 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4656 : static std::array<int, 13> const defaultLeapYear{{2008, 1992, 2004, 2016, 2000, 2012, 1996, 2008, 1992, 2004, 2016, 2000, 2012}};
4657 :
4658 0 : int rem = calculateDayOfYear(month, day, true) % 7;
4659 0 : return defaultLeapYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4660 : }
4661 :
4662 4 : void GetReportPeriodData(EnergyPlusData &state,
4663 : int nReportPeriods, // Total number of Report Periods requested
4664 : bool &ErrorsFound)
4665 : {
4666 4 : constexpr std::string_view routineName = "GetReportPeriodData";
4667 4 : state.dataWeather->ReportPeriodInput.allocate(nReportPeriods);
4668 :
4669 4 : auto const &ipsc = state.dataIPShortCut;
4670 4 : ipsc->cCurrentModuleObject = "Output:Table:ReportPeriod";
4671 4 : int Count = 0;
4672 : int NumAlpha; // Number of alphas being input
4673 : int NumNumeric; // Number of numbers being input
4674 : int IOStat; // IO Status when calling get input subroutine
4675 13 : for (int i = 1; i <= nReportPeriods; ++i) {
4676 18 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4677 9 : ipsc->cCurrentModuleObject,
4678 : i,
4679 9 : ipsc->cAlphaArgs,
4680 : NumAlpha,
4681 9 : ipsc->rNumericArgs,
4682 : NumNumeric,
4683 : IOStat,
4684 9 : ipsc->lNumericFieldBlanks,
4685 9 : ipsc->lAlphaFieldBlanks,
4686 9 : ipsc->cAlphaFieldNames,
4687 9 : ipsc->cNumericFieldNames);
4688 :
4689 9 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
4690 9 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
4691 : // A1, \field Name
4692 9 : if (std::find_if(state.dataWeather->ReportPeriodInput.begin(),
4693 9 : state.dataWeather->ReportPeriodInput.end(),
4694 48 : [&newName](ReportPeriodData const &rpd) { return newName == rpd.title; }) !=
4695 9 : state.dataWeather->ReportPeriodInput.end()) {
4696 0 : ShowSevereDuplicateName(state, eoh);
4697 0 : ErrorsFound = true;
4698 : }
4699 :
4700 9 : ++Count;
4701 :
4702 9 : auto &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4703 : // Loop = RP + Ptr;
4704 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "ReportPeriod 1" anyways
4705 : // which is fine for our reporting below
4706 9 : reportPeriodInput.title = newName;
4707 : // A2, \field Report Name
4708 9 : reportPeriodInput.reportName = ipsc->cAlphaArgs(2);
4709 :
4710 : // set the start and end day of month from user input
4711 : // N1, \field Begin Year
4712 : // N2, \field Begin Month
4713 : // N3, \field Begin Day of Month
4714 : // N4, \field Begin Hour of Day
4715 : // N5, \field End Year
4716 : // N6, \field End Month
4717 : // N7, \field End Day of Month
4718 : // N8; \field End Hour of Day
4719 9 : reportPeriodInput.startYear = int(ipsc->rNumericArgs(1));
4720 9 : reportPeriodInput.startMonth = int(ipsc->rNumericArgs(2));
4721 9 : reportPeriodInput.startDay = int(ipsc->rNumericArgs(3));
4722 9 : reportPeriodInput.startHour = int(ipsc->rNumericArgs(4));
4723 9 : reportPeriodInput.endYear = int(ipsc->rNumericArgs(5));
4724 9 : reportPeriodInput.endMonth = int(ipsc->rNumericArgs(6));
4725 9 : reportPeriodInput.endDay = int(ipsc->rNumericArgs(7));
4726 9 : reportPeriodInput.endHour = int(ipsc->rNumericArgs(8));
4727 :
4728 : // Validate year inputs
4729 9 : if (reportPeriodInput.startYear == 0) {
4730 9 : if (reportPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
4731 0 : ShowSevereError(state,
4732 0 : format("{}: object={}, end year cannot be specified if the start year is not.",
4733 0 : ipsc->cCurrentModuleObject,
4734 0 : reportPeriodInput.title));
4735 0 : ErrorsFound = true;
4736 : }
4737 0 : } else if (reportPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
4738 0 : ShowSevereError(state,
4739 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
4740 0 : ipsc->cCurrentModuleObject,
4741 0 : reportPeriodInput.title,
4742 0 : reportPeriodInput.startYear));
4743 0 : ErrorsFound = true;
4744 : }
4745 :
4746 9 : if (reportPeriodInput.endYear != 0 && reportPeriodInput.startYear > reportPeriodInput.endYear) {
4747 0 : ShowSevereError(state,
4748 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
4749 0 : ipsc->cCurrentModuleObject,
4750 0 : reportPeriodInput.title,
4751 0 : reportPeriodInput.startYear,
4752 0 : reportPeriodInput.endYear));
4753 0 : ErrorsFound = true;
4754 : }
4755 :
4756 9 : reportPeriodInput.startJulianDate =
4757 9 : computeJulianDate(reportPeriodInput.startYear, reportPeriodInput.startMonth, reportPeriodInput.startDay);
4758 9 : reportPeriodInput.endJulianDate = computeJulianDate(reportPeriodInput.endYear, reportPeriodInput.endMonth, reportPeriodInput.endDay);
4759 9 : }
4760 4 : }
4761 :
4762 4 : void GroupReportPeriodByType(EnergyPlusData &state, const int nReportPeriods)
4763 : {
4764 : // transfer data from the reporting period object to the corresponding report period type arrays
4765 : // ThermalResilienceSummary, CO2ResilienceSummary, VisualResilienceSummary, and AllResilienceSummaries
4766 13 : for (auto const &reportPeriodInput : state.dataWeather->ReportPeriodInput) {
4767 :
4768 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4769 4 : ++state.dataWeather->TotThermalReportPers;
4770 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4771 2 : ++state.dataWeather->TotCO2ReportPers;
4772 3 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4773 2 : ++state.dataWeather->TotVisualReportPers;
4774 1 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4775 1 : ++state.dataWeather->TotThermalReportPers;
4776 1 : ++state.dataWeather->TotCO2ReportPers;
4777 1 : ++state.dataWeather->TotVisualReportPers;
4778 : }
4779 : }
4780 :
4781 4 : state.dataWeather->ThermalReportPeriodInput.allocate(state.dataWeather->TotThermalReportPers);
4782 4 : state.dataWeather->CO2ReportPeriodInput.allocate(state.dataWeather->TotCO2ReportPers);
4783 4 : state.dataWeather->VisualReportPeriodInput.allocate(state.dataWeather->TotVisualReportPers);
4784 :
4785 13 : for (int i = 1, iThermal = 1, iVisual = 1, iCO2 = 1; i <= nReportPeriods; ++i) {
4786 9 : auto const &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4787 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4788 4 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4789 4 : ++iThermal;
4790 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4791 2 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4792 2 : ++iCO2;
4793 3 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4794 2 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4795 2 : ++iVisual;
4796 1 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4797 1 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4798 1 : ++iThermal;
4799 1 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4800 1 : ++iCO2;
4801 1 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4802 1 : ++iVisual;
4803 : }
4804 : }
4805 4 : }
4806 :
4807 771 : void GetRunPeriodData(EnergyPlusData &state,
4808 : int nRunPeriods, // Total number of Run Periods requested
4809 : bool &ErrorsFound)
4810 : {
4811 :
4812 : // SUBROUTINE INFORMATION:
4813 : // AUTHOR Richard Liesen
4814 : // DATE WRITTEN October 1997
4815 : // MODIFIED February 1999, Add multiple run periods, Change name.
4816 : // March 2012, LKL, Add features to object; New "actual weather" object;
4817 :
4818 : // PURPOSE OF THIS SUBROUTINE:
4819 : // This subroutine gets the run period info from User input and the
4820 : // simulation dates
4821 :
4822 771 : constexpr std::string_view routineName = "GetRunPeriodData";
4823 : // Call Input Get routine to retrieve annual run data
4824 771 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
4825 :
4826 771 : auto const &ipsc = state.dataIPShortCut;
4827 771 : ipsc->cCurrentModuleObject = "RunPeriod";
4828 771 : int Count = 0;
4829 : int NumAlpha; // Number of alphas being input
4830 : int NumNumeric; // Number of numbers being input
4831 : int IOStat; // IO Status when calling get input subroutine
4832 1956 : for (int i = 1; i <= nRunPeriods; ++i) {
4833 2370 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4834 1185 : ipsc->cCurrentModuleObject,
4835 : i,
4836 1185 : ipsc->cAlphaArgs,
4837 : NumAlpha,
4838 1185 : ipsc->rNumericArgs,
4839 : NumNumeric,
4840 : IOStat,
4841 1185 : ipsc->lNumericFieldBlanks,
4842 1185 : ipsc->lAlphaFieldBlanks,
4843 1185 : ipsc->cAlphaFieldNames,
4844 1185 : ipsc->cNumericFieldNames);
4845 :
4846 : // A1, \field Name
4847 1185 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
4848 1185 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
4849 :
4850 1185 : if (std::find_if(state.dataWeather->RunPeriodInput.begin(),
4851 1185 : state.dataWeather->RunPeriodInput.end(),
4852 3396 : [&newName](RunPeriodData const &rpd) { return rpd.title == newName; }) != state.dataWeather->RunPeriodInput.end()) {
4853 0 : ShowSevereDuplicateName(state, eoh);
4854 0 : ErrorsFound = true;
4855 : }
4856 :
4857 1185 : ++Count;
4858 : // Loop = RP + Ptr;
4859 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "RUNPERIOD 1" anyways
4860 : // which is fine for our reporting below
4861 1185 : auto &runPeriodInput = state.dataWeather->RunPeriodInput(i);
4862 1185 : runPeriodInput.title = ipsc->cAlphaArgs(1);
4863 :
4864 : // set the start and end day of month from user input
4865 : // N1 , \field Begin Month
4866 : // N2 , \field Begin Day of Month
4867 : // N3, \field Start Year
4868 : // N4 , \field End Month
4869 : // N5 , \field End Day of Month
4870 : // N6, \field End Year
4871 1185 : runPeriodInput.startMonth = int(ipsc->rNumericArgs(1));
4872 1185 : runPeriodInput.startDay = int(ipsc->rNumericArgs(2));
4873 1185 : runPeriodInput.startYear = int(ipsc->rNumericArgs(3));
4874 1185 : runPeriodInput.endMonth = int(ipsc->rNumericArgs(4));
4875 1185 : runPeriodInput.endDay = int(ipsc->rNumericArgs(5));
4876 1185 : runPeriodInput.endYear = int(ipsc->rNumericArgs(6));
4877 1185 : runPeriodInput.TreatYearsAsConsecutive = true;
4878 :
4879 1185 : if (state.dataSysVars->FullAnnualRun && i == 1) {
4880 6 : runPeriodInput.startMonth = 1;
4881 6 : runPeriodInput.startDay = 1;
4882 6 : runPeriodInput.endMonth = 12;
4883 6 : runPeriodInput.endDay = 31;
4884 : }
4885 :
4886 : // Validate year inputs
4887 1185 : if (runPeriodInput.startYear == 0) {
4888 1178 : if (runPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
4889 0 : ShowSevereError(state,
4890 0 : format("{}: object={}, end year cannot be specified if the start year is not.",
4891 0 : ipsc->cCurrentModuleObject,
4892 0 : runPeriodInput.title));
4893 0 : ErrorsFound = true;
4894 : }
4895 7 : } else if (runPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
4896 0 : ShowSevereError(state,
4897 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
4898 0 : ipsc->cCurrentModuleObject,
4899 0 : runPeriodInput.title,
4900 0 : runPeriodInput.startYear));
4901 0 : ErrorsFound = true;
4902 : }
4903 :
4904 1185 : if (runPeriodInput.endYear != 0 && runPeriodInput.startYear > runPeriodInput.endYear) {
4905 0 : ShowSevereError(state,
4906 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
4907 0 : ipsc->cCurrentModuleObject,
4908 0 : runPeriodInput.title,
4909 0 : runPeriodInput.startYear,
4910 0 : runPeriodInput.endYear));
4911 0 : ErrorsFound = true;
4912 : }
4913 :
4914 : // A2 , \field Day of Week for Start Day
4915 1185 : bool inputWeekday = false;
4916 1185 : if (!state.dataIPShortCut->lAlphaFieldBlanks(2)) { // Have input
4917 1038 : int dayType = getEnumValue(ScheduleManager::dayTypeNamesUC, state.dataIPShortCut->cAlphaArgs(2));
4918 1038 : if (dayType < 1) {
4919 0 : ShowWarningError(state,
4920 0 : format("{}: object={}{} invalid (Day of Week) [{}] for Start is not valid, Sunday will be used.",
4921 0 : state.dataIPShortCut->cCurrentModuleObject,
4922 0 : state.dataWeather->RunPeriodInput(i).title,
4923 0 : state.dataIPShortCut->cAlphaFieldNames(2),
4924 0 : state.dataIPShortCut->cAlphaArgs(2)));
4925 0 : runPeriodInput.startWeekDay = ScheduleManager::DayType::Sunday;
4926 : } else {
4927 1038 : runPeriodInput.startWeekDay = static_cast<ScheduleManager::DayType>(dayType);
4928 1038 : inputWeekday = true;
4929 : }
4930 : } else { // No input, set the default as Sunday. This may get overriden below
4931 147 : runPeriodInput.startWeekDay = ScheduleManager::DayType::Sunday;
4932 : }
4933 :
4934 : // Validate the dates now that the weekday field has been looked at
4935 1185 : if (runPeriodInput.startMonth == 2 && runPeriodInput.startDay == 29) {
4936 : // Requested start date is a leap year
4937 0 : if (runPeriodInput.startYear == 0) { // No input starting year
4938 0 : if (inputWeekday) {
4939 0 : runPeriodInput.startYear =
4940 0 : findLeapYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
4941 : } else {
4942 : // 2012 is the default year, 1/1 is a Sunday
4943 0 : runPeriodInput.startYear = 2012;
4944 0 : runPeriodInput.startWeekDay =
4945 0 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
4946 : }
4947 : } else { // Have an input start year
4948 0 : if (!isLeapYear(runPeriodInput.startYear)) { // Start year is not a leap year
4949 0 : ShowSevereError(state,
4950 0 : format("{}: object={}, start year ({}) is not a leap year but the requested start date is 2/29.",
4951 0 : ipsc->cCurrentModuleObject,
4952 0 : runPeriodInput.title,
4953 0 : runPeriodInput.startYear));
4954 0 : ErrorsFound = true;
4955 : } else { // Start year is a leap year
4956 : ScheduleManager::DayType weekday =
4957 0 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
4958 0 : if (inputWeekday) { // Check for correctness of input
4959 0 : if (weekday != runPeriodInput.startWeekDay) {
4960 0 : ShowWarningError(state,
4961 0 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
4962 0 : ipsc->cCurrentModuleObject,
4963 0 : runPeriodInput.title,
4964 0 : ipsc->cAlphaArgs(2),
4965 0 : runPeriodInput.startYear,
4966 0 : ScheduleManager::dayTypeNamesUC[static_cast<int>(weekday)]));
4967 0 : runPeriodInput.startWeekDay = weekday;
4968 : }
4969 : } else { // Set the weekday if it was not input
4970 0 : runPeriodInput.startWeekDay = weekday;
4971 : }
4972 : }
4973 : }
4974 0 : } else {
4975 : // Non leap-day start date
4976 1185 : if (!validMonthDay(runPeriodInput.startMonth, runPeriodInput.startDay)) {
4977 0 : ShowSevereError(state,
4978 0 : format("{}: object={}, Invalid input start month/day ({}/{})",
4979 0 : ipsc->cCurrentModuleObject,
4980 0 : runPeriodInput.title,
4981 0 : runPeriodInput.startMonth,
4982 0 : runPeriodInput.startDay));
4983 0 : ErrorsFound = true;
4984 : } else { // Month/day is valid
4985 1185 : if (runPeriodInput.startYear == 0) { // No input starting year
4986 1178 : if (inputWeekday) {
4987 1033 : runPeriodInput.startYear =
4988 1033 : findYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
4989 : } else {
4990 : // 2017 is the default year, 1/1 is a Sunday
4991 145 : runPeriodInput.startYear = 2017;
4992 145 : runPeriodInput.startWeekDay =
4993 145 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
4994 : }
4995 : } else { // Have an input starting year
4996 : ScheduleManager::DayType weekday =
4997 7 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
4998 7 : if (inputWeekday) { // Check for correctness of input
4999 5 : if (weekday != runPeriodInput.startWeekDay) {
5000 2 : ShowWarningError(state,
5001 3 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
5002 1 : ipsc->cCurrentModuleObject,
5003 1 : runPeriodInput.title,
5004 1 : ipsc->cAlphaArgs(2),
5005 1 : runPeriodInput.startYear,
5006 1 : ScheduleManager::dayTypeNamesUC[static_cast<int>(weekday)]));
5007 1 : runPeriodInput.startWeekDay = weekday;
5008 : }
5009 : } else { // Set the weekday if it was not input
5010 2 : runPeriodInput.startWeekDay = weekday;
5011 : }
5012 : }
5013 : }
5014 : }
5015 :
5016 : // Compute the Julian date of the start date
5017 1185 : runPeriodInput.startJulianDate = computeJulianDate(runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5018 :
5019 : // Validate the end date
5020 1185 : if (runPeriodInput.endMonth == 2 && runPeriodInput.endDay == 29) {
5021 : // Requested end date is a leap year
5022 0 : if (runPeriodInput.endYear == 0) { // No input end year
5023 0 : if (isLeapYear(runPeriodInput.startYear) && runPeriodInput.startMonth < 3) {
5024 : // The run period is from some date on or before 2/29 through 2/29
5025 0 : runPeriodInput.endYear = runPeriodInput.startYear;
5026 : } else {
5027 : // There might be a better approach here, but for now just loop forward for the next leap year
5028 0 : for (int yr = runPeriodInput.startYear + 1; yr < runPeriodInput.startYear + 10; yr++) {
5029 0 : if (isLeapYear(yr)) {
5030 0 : runPeriodInput.endYear = yr;
5031 0 : break;
5032 : }
5033 : }
5034 : }
5035 : } else { // Have an input end year
5036 0 : if (!isLeapYear(runPeriodInput.endYear)) { // End year is not a leap year
5037 0 : ShowSevereError(state,
5038 0 : format("{}: object={}, end year ({}) is not a leap year but the requested end date is 2/29.",
5039 0 : ipsc->cCurrentModuleObject,
5040 0 : runPeriodInput.title,
5041 0 : runPeriodInput.startYear));
5042 0 : ErrorsFound = true;
5043 : } else {
5044 0 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5045 0 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5046 0 : ShowSevereError(state,
5047 0 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5048 0 : ipsc->cCurrentModuleObject,
5049 0 : runPeriodInput.title,
5050 0 : runPeriodInput.startJulianDate,
5051 0 : runPeriodInput.endJulianDate));
5052 0 : ErrorsFound = true;
5053 : }
5054 : }
5055 : }
5056 0 : } else {
5057 : // Non leap-day end date
5058 1185 : if (!validMonthDay(runPeriodInput.endMonth, runPeriodInput.endDay)) {
5059 0 : ShowSevereError(state,
5060 0 : format("{}: object={}, Invalid input end month/day ({}/{})",
5061 0 : ipsc->cCurrentModuleObject,
5062 0 : runPeriodInput.title,
5063 0 : runPeriodInput.startMonth,
5064 0 : runPeriodInput.startDay));
5065 0 : ErrorsFound = true;
5066 : } else { // Month/day is valid
5067 1185 : if (runPeriodInput.endYear == 0) { // No input end year
5068 : // Assume same year as start year
5069 1178 : runPeriodInput.endYear = runPeriodInput.startYear;
5070 1178 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5071 1178 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5072 5 : runPeriodInput.endJulianDate = 0; // Force recalculation later
5073 5 : runPeriodInput.endYear += 1;
5074 : }
5075 : } else { // Have an input end year
5076 7 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5077 7 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5078 0 : ShowSevereError(state,
5079 0 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5080 0 : ipsc->cCurrentModuleObject,
5081 0 : runPeriodInput.title,
5082 0 : runPeriodInput.startJulianDate,
5083 0 : runPeriodInput.endJulianDate));
5084 0 : ErrorsFound = true;
5085 : }
5086 : }
5087 : }
5088 : }
5089 :
5090 1185 : if (runPeriodInput.endJulianDate == 0) {
5091 5 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5092 : }
5093 :
5094 1185 : runPeriodInput.numSimYears = runPeriodInput.endYear - runPeriodInput.startYear + 1;
5095 :
5096 : // A3, \field Use Weather File Holidays and Special Days
5097 : BooleanSwitch b;
5098 1185 : if (ipsc->lAlphaFieldBlanks(3)) {
5099 9 : runPeriodInput.useHolidays = true;
5100 1176 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5101 1176 : runPeriodInput.useHolidays = static_cast<bool>(b);
5102 : } else {
5103 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5104 0 : ErrorsFound = true;
5105 : }
5106 :
5107 : // A4, \field Use Weather File Daylight Saving Period
5108 1185 : if (ipsc->lAlphaFieldBlanks(4)) {
5109 9 : runPeriodInput.useDST = true;
5110 1176 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5111 1176 : runPeriodInput.useDST = static_cast<bool>(b);
5112 : } else {
5113 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5114 0 : ErrorsFound = true;
5115 : }
5116 :
5117 : // A5, \field Apply Weekend Holiday Rule
5118 1185 : if (ipsc->lAlphaFieldBlanks(5)) {
5119 18 : runPeriodInput.applyWeekendRule = true;
5120 1167 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5121 1167 : runPeriodInput.applyWeekendRule = static_cast<bool>(b);
5122 : } else {
5123 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5124 0 : ErrorsFound = true;
5125 : }
5126 :
5127 : // A6, \field Use Weather File Rain Indicators
5128 1185 : if (ipsc->lAlphaFieldBlanks(6)) {
5129 18 : runPeriodInput.useRain = true;
5130 1167 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(6)))) != BooleanSwitch::Invalid) {
5131 1167 : runPeriodInput.useRain = static_cast<bool>(b);
5132 : } else {
5133 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6));
5134 0 : ErrorsFound = true;
5135 : }
5136 :
5137 : // A7, \field Use Weather File Snow Indicators
5138 1185 : if (ipsc->lAlphaFieldBlanks(7)) {
5139 18 : runPeriodInput.useSnow = true;
5140 1167 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
5141 1167 : runPeriodInput.useSnow = static_cast<bool>(b);
5142 : } else {
5143 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7));
5144 0 : ErrorsFound = true;
5145 : }
5146 :
5147 : // A8, \field Treat Weather as Actual
5148 1185 : if (ipsc->lAlphaFieldBlanks(8)) {
5149 1185 : runPeriodInput.actualWeather = false;
5150 0 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
5151 0 : runPeriodInput.actualWeather = static_cast<bool>(b);
5152 : } else {
5153 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8));
5154 0 : ErrorsFound = true;
5155 : }
5156 :
5157 : // A9, \field First Hour Interpolation Starting Values
5158 1185 : if (ipsc->lAlphaFieldBlanks(9) || Util::SameString(ipsc->cAlphaArgs(8), "Hour24")) {
5159 1185 : runPeriodInput.firstHrInterpUsingHr1 = false;
5160 0 : } else if (Util::SameString(ipsc->cAlphaArgs(9), "Hour1")) {
5161 0 : runPeriodInput.firstHrInterpUsingHr1 = true;
5162 : } else {
5163 : // fail-safe default
5164 0 : runPeriodInput.firstHrInterpUsingHr1 = false;
5165 : }
5166 :
5167 1185 : runPeriodInput.dayOfWeek = static_cast<int>(runPeriodInput.startWeekDay);
5168 1185 : runPeriodInput.isLeapYear = isLeapYear(runPeriodInput.startYear);
5169 :
5170 : // calculate the annual start and end days from the user inputted month and day
5171 1185 : runPeriodInput.monWeekDay = 0;
5172 1185 : if (runPeriodInput.dayOfWeek != 0 && !ErrorsFound) {
5173 1185 : SetupWeekDaysByMonth(state, runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.dayOfWeek, runPeriodInput.monWeekDay);
5174 : }
5175 1185 : }
5176 :
5177 771 : if (nRunPeriods == 0 && state.dataSysVars->FullAnnualRun) {
5178 0 : ShowWarningError(state, "No Run Periods input but Full Annual Simulation selected. Adding Run Period to 1/1 through 12/31.");
5179 0 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
5180 0 : state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
5181 0 : nRunPeriods = 1;
5182 0 : state.dataGlobal->WeathSimReq = true;
5183 0 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
5184 0 : auto &runPerInput1 = state.dataWeather->RunPeriodInput(1);
5185 0 : runPerInput1.startJulianDate = General::OrdinalDay(runPerInput1.startMonth, runPerInput1.startDay, state.dataWeather->LeapYearAdd);
5186 0 : runPerInput1.endJulianDate = General::OrdinalDay(runPerInput1.endMonth, runPerInput1.endDay, state.dataWeather->LeapYearAdd);
5187 0 : runPerInput1.monWeekDay = 0;
5188 0 : if (runPerInput1.dayOfWeek != 0 && !ErrorsFound) {
5189 0 : SetupWeekDaysByMonth(state, runPerInput1.startMonth, runPerInput1.startDay, runPerInput1.dayOfWeek, runPerInput1.monWeekDay);
5190 : }
5191 771 : } else if (nRunPeriods > 1 && state.dataSysVars->FullAnnualRun) {
5192 0 : nRunPeriods = 1;
5193 : }
5194 771 : }
5195 :
5196 3 : void GetRunPeriodDesignData(EnergyPlusData &state, bool &ErrorsFound)
5197 : {
5198 :
5199 : // SUBROUTINE INFORMATION:
5200 : // AUTHOR Linda Lawrie
5201 : // DATE WRITTEN March 2008
5202 :
5203 : // PURPOSE OF THIS SUBROUTINE:
5204 : // This subroutine gets the run period design info from User input and the
5205 : // simulation dates
5206 :
5207 3 : constexpr std::string_view routineName = "GetRunPeriodDesignData";
5208 : // Call Input Get routine to retrieve annual run data
5209 3 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
5210 3 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
5211 3 : state.dataWeather->TotRunDesPers = RPD1 + RPD2;
5212 :
5213 3 : state.dataWeather->RunPeriodDesignInput.allocate(RPD1 + RPD2);
5214 :
5215 3 : int Count = 0;
5216 3 : auto const &ipsc = state.dataIPShortCut;
5217 3 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileDays";
5218 6 : for (int i = 1; i <= RPD1; ++i) {
5219 : int NumAlphas; // Number of alphas being input
5220 : int NumNumerics; // Number of Numerics being input
5221 : int IOStat; // IO Status when calling get input subroutine
5222 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5223 3 : ipsc->cCurrentModuleObject,
5224 : i,
5225 3 : ipsc->cAlphaArgs,
5226 : NumAlphas,
5227 3 : ipsc->rNumericArgs,
5228 : NumNumerics,
5229 : IOStat,
5230 3 : ipsc->lNumericFieldBlanks,
5231 3 : ipsc->lAlphaFieldBlanks,
5232 3 : ipsc->cAlphaFieldNames,
5233 3 : ipsc->cNumericFieldNames);
5234 :
5235 3 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5236 3 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5237 6 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5238 6 : state.dataWeather->RunPeriodDesignInput.end(),
5239 8 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5240 6 : state.dataWeather->RunPeriodDesignInput.end()) {
5241 0 : ShowSevereDuplicateName(state, eoh);
5242 0 : ErrorsFound = true;
5243 : }
5244 :
5245 3 : ++Count;
5246 :
5247 3 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5248 3 : runPerDesInput.title = newName;
5249 3 : runPerDesInput.periodType = "User Selected WeatherFile RunPeriod (Design)";
5250 :
5251 : // set the start and end day of month from user input
5252 3 : runPerDesInput.startMonth = int(ipsc->rNumericArgs(1));
5253 3 : runPerDesInput.startDay = int(ipsc->rNumericArgs(2));
5254 3 : runPerDesInput.endMonth = int(ipsc->rNumericArgs(3));
5255 3 : runPerDesInput.endDay = int(ipsc->rNumericArgs(4));
5256 :
5257 3 : switch (runPerDesInput.startMonth) {
5258 3 : case 1:
5259 : case 3:
5260 : case 5:
5261 : case 7:
5262 : case 8:
5263 : case 10:
5264 : case 12: {
5265 3 : if (runPerDesInput.startDay > 31) {
5266 0 : ShowSevereError(state,
5267 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5268 0 : ipsc->cCurrentModuleObject,
5269 0 : runPerDesInput.title,
5270 0 : ipsc->cNumericFieldNames(2),
5271 0 : runPerDesInput.startDay));
5272 0 : ErrorsFound = true;
5273 : }
5274 3 : } break;
5275 0 : case 4:
5276 : case 6:
5277 : case 9:
5278 : case 11: {
5279 0 : if (runPerDesInput.startDay > 30) {
5280 0 : ShowSevereError(state,
5281 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5282 0 : ipsc->cCurrentModuleObject,
5283 0 : runPerDesInput.title,
5284 0 : ipsc->cNumericFieldNames(2),
5285 0 : runPerDesInput.startDay));
5286 0 : ErrorsFound = true;
5287 : }
5288 0 : } break;
5289 0 : case 2: {
5290 0 : if (runPerDesInput.startDay > 28 + state.dataWeather->LeapYearAdd) {
5291 0 : ShowSevereError(state,
5292 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5293 0 : ipsc->cCurrentModuleObject,
5294 0 : runPerDesInput.title,
5295 0 : ipsc->cNumericFieldNames(2),
5296 0 : runPerDesInput.startDay));
5297 0 : ErrorsFound = true;
5298 : }
5299 0 : } break;
5300 0 : default: {
5301 0 : ShowSevereError(state,
5302 0 : format("{}: object={} {} invalid (Month) [{}]",
5303 0 : ipsc->cCurrentModuleObject,
5304 0 : runPerDesInput.title,
5305 0 : ipsc->cNumericFieldNames(1),
5306 0 : runPerDesInput.startMonth));
5307 0 : ErrorsFound = true;
5308 0 : } break;
5309 : } // switch
5310 :
5311 3 : if (ipsc->lAlphaFieldBlanks(2)) {
5312 0 : runPerDesInput.dayOfWeek = (int)ScheduleManager::DayType::Monday; // Defaults to Monday
5313 : } else {
5314 3 : runPerDesInput.dayOfWeek = getEnumValue(ScheduleManager::dayTypeNamesUC, ipsc->cAlphaArgs(2));
5315 3 : if (runPerDesInput.dayOfWeek < 1 || runPerDesInput.dayOfWeek == 8) {
5316 0 : ShowWarningError(state,
5317 0 : format("{}: object={} {} invalid (Day of Week) [{} for Start is not Valid, Monday will be Used.",
5318 0 : ipsc->cCurrentModuleObject,
5319 0 : runPerDesInput.title,
5320 0 : ipsc->cAlphaFieldNames(1),
5321 0 : ipsc->cAlphaArgs(1)));
5322 0 : runPerDesInput.dayOfWeek = (int)ScheduleManager::DayType::Monday; // Defaults to Monday
5323 : }
5324 : }
5325 :
5326 : BooleanSwitch b;
5327 3 : if (ipsc->lAlphaFieldBlanks(3)) {
5328 0 : runPerDesInput.useDST = true;
5329 3 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5330 3 : runPerDesInput.useDST = static_cast<bool>(b);
5331 : } else {
5332 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5333 0 : ErrorsFound = true;
5334 : }
5335 :
5336 3 : if (ipsc->lAlphaFieldBlanks(4)) {
5337 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5338 3 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5339 3 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5340 : } else {
5341 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5342 0 : ErrorsFound = true;
5343 : }
5344 :
5345 : // calculate the annual start and end days from the user inputted month and day
5346 3 : runPerDesInput.startJulianDate = General::OrdinalDay(runPerDesInput.startMonth, runPerDesInput.startDay, state.dataWeather->LeapYearAdd);
5347 3 : runPerDesInput.endJulianDate = General::OrdinalDay(runPerDesInput.endMonth, runPerDesInput.endDay, state.dataWeather->LeapYearAdd);
5348 3 : if (runPerDesInput.startJulianDate <= runPerDesInput.endJulianDate) {
5349 3 : runPerDesInput.totalDays = (runPerDesInput.endJulianDate - runPerDesInput.startJulianDate + 1) * runPerDesInput.numSimYears;
5350 : } else {
5351 0 : runPerDesInput.totalDays = (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - runPerDesInput.startJulianDate + 1 +
5352 0 : runPerDesInput.endJulianDate) *
5353 0 : runPerDesInput.numSimYears;
5354 : }
5355 3 : runPerDesInput.monWeekDay = 0;
5356 3 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5357 3 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5358 3 : SetupWeekDaysByMonth(state,
5359 : runPeriodDesignInput1.startMonth,
5360 : runPeriodDesignInput1.startDay,
5361 : runPeriodDesignInput1.dayOfWeek,
5362 3 : runPeriodDesignInput1.monWeekDay);
5363 : }
5364 3 : }
5365 :
5366 3 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileConditionType";
5367 5 : for (int i = 1; i <= RPD2; ++i) {
5368 : int NumAlphas; // Number of alphas being input
5369 : int NumNumerics; // Number of Numerics being input
5370 : int IOStat; // IO Status when calling get input subroutine
5371 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5372 2 : ipsc->cCurrentModuleObject,
5373 : i,
5374 2 : ipsc->cAlphaArgs,
5375 : NumAlphas,
5376 2 : ipsc->rNumericArgs,
5377 : NumNumerics,
5378 : IOStat,
5379 2 : ipsc->lNumericFieldBlanks,
5380 2 : ipsc->lAlphaFieldBlanks,
5381 2 : ipsc->cAlphaFieldNames,
5382 2 : ipsc->cNumericFieldNames);
5383 2 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5384 :
5385 2 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5386 4 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5387 4 : state.dataWeather->RunPeriodDesignInput.end(),
5388 6 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5389 4 : state.dataWeather->RunPeriodDesignInput.end()) {
5390 0 : ShowSevereDuplicateName(state, eoh);
5391 0 : ErrorsFound = true;
5392 : }
5393 :
5394 2 : ++Count;
5395 2 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5396 2 : runPerDesInput.title = ipsc->cAlphaArgs(1);
5397 2 : runPerDesInput.periodType = "User Selected WeatherFile Typical/Extreme Period (Design)=" + ipsc->cAlphaArgs(2);
5398 :
5399 : // Period Selection
5400 2 : if (ipsc->lAlphaFieldBlanks(2)) {
5401 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
5402 0 : ErrorsFound = true;
5403 : } else {
5404 2 : int WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue);
5405 2 : if (WhichPeriod == 0) {
5406 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue1);
5407 : if (WhichPeriod != 0) {
5408 : }
5409 : }
5410 2 : if (WhichPeriod == 0) {
5411 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue2);
5412 0 : if (WhichPeriod != 0) {
5413 0 : ShowWarningError(state,
5414 0 : format("{}: object={} {}={} matched to {}",
5415 0 : ipsc->cCurrentModuleObject,
5416 0 : runPerDesInput.title,
5417 0 : ipsc->cAlphaFieldNames(2),
5418 0 : ipsc->cAlphaArgs(2),
5419 0 : state.dataWeather->TypicalExtremePeriods(WhichPeriod).MatchValue2));
5420 : }
5421 : }
5422 2 : if (WhichPeriod == 0) {
5423 0 : ShowSevereError(state,
5424 0 : format("{}: object={} {} invalid (not on Weather File)={}",
5425 0 : ipsc->cCurrentModuleObject,
5426 0 : runPerDesInput.title,
5427 0 : ipsc->cAlphaFieldNames(2),
5428 0 : ipsc->cAlphaArgs(2)));
5429 0 : ErrorsFound = true;
5430 : } else {
5431 2 : auto const &typicalExtPer = state.dataWeather->TypicalExtremePeriods(WhichPeriod);
5432 2 : runPerDesInput.startDay = typicalExtPer.StartDay;
5433 2 : runPerDesInput.startMonth = typicalExtPer.StartMonth;
5434 2 : runPerDesInput.startJulianDate = typicalExtPer.StartJDay;
5435 2 : runPerDesInput.endDay = typicalExtPer.EndDay;
5436 2 : runPerDesInput.endMonth = typicalExtPer.EndMonth;
5437 2 : runPerDesInput.endJulianDate = typicalExtPer.EndJDay;
5438 2 : runPerDesInput.totalDays = typicalExtPer.TotalDays;
5439 : }
5440 : }
5441 :
5442 2 : if (ipsc->lAlphaFieldBlanks(3)) {
5443 0 : runPerDesInput.dayOfWeek = (int)ScheduleManager::DayType::Monday; // Defaults to Monday
5444 : } else {
5445 2 : runPerDesInput.dayOfWeek = getEnumValue(ScheduleManager::dayTypeNamesUC, ipsc->cAlphaArgs(3));
5446 2 : if (runPerDesInput.dayOfWeek < (int)ScheduleManager::DayType::Sunday ||
5447 2 : runPerDesInput.dayOfWeek == (int)ScheduleManager::DayType::Holiday) {
5448 : // Sunday-Saturday, SummerDesignDay, WinterDesignDay, CustomDay1, and CustomDay2 are all valid. Holiday is not valid.
5449 : // The input processor should trap invalid key choices, so this should never trip.
5450 0 : assert(false);
5451 : }
5452 : }
5453 :
5454 : BooleanSwitch b;
5455 2 : if (ipsc->lAlphaFieldBlanks(4)) {
5456 0 : runPerDesInput.useDST = true;
5457 2 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5458 2 : runPerDesInput.useDST = static_cast<bool>(b);
5459 : } else {
5460 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5461 0 : ErrorsFound = true;
5462 : }
5463 :
5464 2 : if (ipsc->lAlphaFieldBlanks(5)) {
5465 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5466 2 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5467 2 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5468 : } else {
5469 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5470 0 : ErrorsFound = true;
5471 : }
5472 2 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5473 2 : runPeriodDesignInput1.monWeekDay = 0;
5474 2 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5475 2 : SetupWeekDaysByMonth(state,
5476 : runPeriodDesignInput1.startMonth,
5477 : runPeriodDesignInput1.startDay,
5478 : runPeriodDesignInput1.dayOfWeek,
5479 2 : runPeriodDesignInput1.monWeekDay);
5480 : }
5481 2 : }
5482 3 : }
5483 :
5484 771 : void GetSpecialDayPeriodData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5485 : {
5486 :
5487 : // SUBROUTINE INFORMATION:
5488 : // AUTHOR Linda Lawrie
5489 : // DATE WRITTEN June 2000
5490 :
5491 : // PURPOSE OF THIS SUBROUTINE:
5492 : // This subroutine reads any special day period data from the IDF and
5493 : // processes it into the data structure that will drive the values
5494 : // in the SpecialDayTypes array.
5495 :
5496 : // METHODOLOGY EMPLOYED:
5497 : // Processes the following IDD definition:
5498 : // SpecialDayPeriod,
5499 : // \memo This object sets up holidays/special days to be used during weather file
5500 : // \memo run periods. (These are not used with DesignDay objects.)
5501 : // \memo Depending on the value in the run period, days on the weather file may also
5502 : // \memo be used. However, the weather file specification will take precedence over
5503 : // \memo any specification shown here. (No error message on duplicate days or overlapping
5504 : // \memo days).
5505 : // A1, \field Holiday Name
5506 : // A2, \field StartDate
5507 : // \memo Dates can be several formats:
5508 : // \memo <number>/<number> (month/day)
5509 : // \memo <number> Month
5510 : // \memo Month <number>
5511 : // \memo Months are January, February, March, April, May, June, July, August, September, October, November, December
5512 : // \memo Months can be the first 3 letters of the month
5513 : // \note will eventually allow: 3 Monday April (meaning 3rd Monday in April)
5514 : // N1, \field duration (number of days)
5515 : // A3; \field SpecialDayType
5516 : // \note SpecialDayType selects the schedules appropriate for each day so labeled
5517 : // \type choice
5518 : // \key Holiday
5519 : // \key SummerDesignDay
5520 : // \key WinterDesignDay
5521 : // \key CustomDay1
5522 : // \key CustomDay2
5523 :
5524 771 : constexpr std::string_view routineName = "GetSpecialDayPeriodData";
5525 :
5526 771 : auto const &ipsc = state.dataIPShortCut;
5527 771 : ipsc->cCurrentModuleObject = "RunPeriodControl:SpecialDays";
5528 771 : int NumSpecDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5529 : int Count;
5530 771 : if (allocated(state.dataWeather->SpecialDays)) { // EPW already allocated the array
5531 763 : Count = state.dataWeather->NumSpecialDays - NumSpecDays + 1;
5532 : } else {
5533 8 : state.dataWeather->SpecialDays.allocate(NumSpecDays);
5534 8 : state.dataWeather->NumSpecialDays = NumSpecDays;
5535 8 : Count = 1;
5536 : }
5537 :
5538 771 : Array1D_string AlphArray(3);
5539 : int NumAlphas;
5540 771 : Array1D<Real64> Duration(1);
5541 : int NumNumbers;
5542 : int IOStat;
5543 :
5544 2060 : for (int i = 1; i <= NumSpecDays; ++i, ++Count) {
5545 :
5546 2578 : state.dataInputProcessing->inputProcessor->getObjectItem(
5547 1289 : state, ipsc->cCurrentModuleObject, i, AlphArray, NumAlphas, Duration, NumNumbers, IOStat);
5548 :
5549 1289 : auto &specialDay = state.dataWeather->SpecialDays(Count);
5550 :
5551 1289 : specialDay.Name = AlphArray(1);
5552 1289 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, specialDay.Name};
5553 :
5554 : int PMonth;
5555 : int PDay;
5556 : int PWeekDay;
5557 : DateType dateType;
5558 1289 : General::ProcessDateString(state, AlphArray(2), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
5559 1289 : if (dateType == DateType::MonthDay) {
5560 522 : specialDay.dateType = dateType;
5561 522 : specialDay.Month = PMonth;
5562 522 : specialDay.Day = PDay;
5563 522 : specialDay.WeekDay = 0;
5564 522 : specialDay.CompDate = PMonth * 32 + PDay;
5565 522 : specialDay.WthrFile = false;
5566 767 : } else if (dateType != DateType::Invalid) {
5567 767 : specialDay.dateType = dateType;
5568 767 : specialDay.Month = PMonth;
5569 767 : specialDay.Day = PDay;
5570 767 : specialDay.WeekDay = PWeekDay;
5571 767 : specialDay.CompDate = 0;
5572 767 : specialDay.WthrFile = false;
5573 0 : } else if (dateType == DateType::Invalid) {
5574 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
5575 0 : ErrorsFound = true;
5576 : }
5577 :
5578 1289 : if (Duration(1) > 0) {
5579 1289 : specialDay.Duration = int(Duration(1));
5580 : } else {
5581 0 : ShowSevereError(
5582 0 : state, format("{}: {} Invalid {}={:.0T}", ipsc->cCurrentModuleObject, AlphArray(1), ipsc->cNumericFieldNames(1), Duration(1)));
5583 0 : ErrorsFound = true;
5584 : }
5585 :
5586 1289 : int DayType = getEnumValue(ScheduleManager::dayTypeNamesUC, AlphArray(3));
5587 1289 : if (DayType == 0) {
5588 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), AlphArray(3));
5589 0 : ErrorsFound = true;
5590 : } else {
5591 1289 : specialDay.DayType = DayType;
5592 : }
5593 : }
5594 771 : }
5595 :
5596 7 : void CalcSpecialDayTypes(EnergyPlusData &state)
5597 : {
5598 :
5599 : // SUBROUTINE INFORMATION:
5600 : // AUTHOR Linda Lawrie
5601 : // DATE WRITTEN June 2000
5602 :
5603 : // PURPOSE OF THIS SUBROUTINE:
5604 : // This subroutine creates the array of Special Day types used during
5605 : // the simulation.
5606 :
5607 : // METHODOLOGY EMPLOYED:
5608 : // Sets up the SpecialDayTypes array that then is used during simulation.
5609 :
5610 7 : state.dataWeather->SpecialDayTypes = 0; // Initialize/Reset Special Day Types array
5611 :
5612 37 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
5613 30 : auto const &specialDay = state.dataWeather->SpecialDays(i);
5614 30 : if (specialDay.WthrFile) continue;
5615 :
5616 30 : int Warn = 0;
5617 :
5618 30 : int JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd) - 1;
5619 :
5620 60 : for (int j = 1; j <= specialDay.Duration; ++j) {
5621 30 : ++JDay;
5622 30 : if (JDay > 366) {
5623 0 : ShowWarningError(state, format("SpecialDay={} causes index of more than 366, ignoring those beyond 366", specialDay.Name));
5624 : } else {
5625 30 : if (state.dataWeather->SpecialDayTypes(JDay) != 0 && Warn == 0) {
5626 0 : ShowWarningError(state, format("SpecialDay={} attempted overwrite of previous set special day", specialDay.Name));
5627 0 : Warn = 1;
5628 30 : } else if (state.dataWeather->SpecialDayTypes(JDay) == 0) {
5629 30 : state.dataWeather->SpecialDayTypes(JDay) = specialDay.DayType;
5630 : }
5631 : }
5632 : }
5633 : }
5634 7 : }
5635 :
5636 771 : void GetDSTData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5637 : {
5638 :
5639 : // SUBROUTINE INFORMATION:
5640 : // AUTHOR Linda Lawrie
5641 : // DATE WRITTEN August 2000
5642 :
5643 : // PURPOSE OF THIS SUBROUTINE:
5644 : // This subroutine gets a possible "Daylight Saving Period" from the IDF. Using this
5645 : // will overwrite any prior DST data.
5646 :
5647 : // METHODOLOGY EMPLOYED:
5648 : // Processes the following IDD definition:
5649 : // DaylightSavingPeriod,
5650 : // \memo This object sets up the Daylight Saving period for any RunPeriod.
5651 : // \memo Ignores any DaylightSavingperiod values on the weather file and uses this definition.
5652 : // \memo (These are not used with DesignDay objects.)
5653 : // A1, \field StartDate
5654 : // A2, \field EndDate
5655 : // \memo Dates can be several formats:
5656 : // \memo <number>/<number> (month/day)
5657 : // \memo <number> <Month>
5658 : // \memo <Month> <number>
5659 : // \memo <Nth> <Weekday> in <Month)
5660 : // \memo Last <WeekDay> in <Month>
5661 : // \memo <Month> can be January, February, March, April, May, June, July, August, September,
5662 : // October, November, December
5663 : // \memo Months can be the first 3 letters of the month
5664 : // \memo <Weekday> can be Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
5665 : // \memo <Nth> can be 1 or 1st, 2 or 2nd, etc. up to 5(?)
5666 :
5667 771 : constexpr std::string_view routineName = "GetDSTData";
5668 :
5669 771 : auto const &ipsc = state.dataIPShortCut;
5670 771 : ipsc->cCurrentModuleObject = "RunPeriodControl:DaylightSavingTime";
5671 771 : int NumFound = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5672 :
5673 771 : if (NumFound == 1) {
5674 : int NumAlphas;
5675 : int IOStat;
5676 : int NumNumbers;
5677 358 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5678 179 : ipsc->cCurrentModuleObject,
5679 : 1,
5680 179 : ipsc->cAlphaArgs,
5681 : NumAlphas,
5682 179 : ipsc->rNumericArgs,
5683 : NumNumbers,
5684 : IOStat,
5685 179 : ipsc->lNumericFieldBlanks,
5686 179 : ipsc->lAlphaFieldBlanks,
5687 179 : ipsc->cAlphaFieldNames,
5688 179 : ipsc->cNumericFieldNames);
5689 :
5690 179 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
5691 :
5692 179 : if (NumAlphas != 2) {
5693 0 : ShowSevereError(state, format("{}: Insufficient fields, must have Start AND End Dates", ipsc->cCurrentModuleObject));
5694 0 : ErrorsFound = true;
5695 : } else { // Correct number of arguments
5696 358 : General::ProcessDateString(state,
5697 179 : ipsc->cAlphaArgs(1),
5698 179 : state.dataWeather->IDFDST.StMon,
5699 179 : state.dataWeather->IDFDST.StDay,
5700 179 : state.dataWeather->IDFDST.StWeekDay,
5701 179 : state.dataWeather->IDFDST.StDateType,
5702 : ErrorsFound);
5703 179 : if (state.dataWeather->IDFDST.StDateType == DateType::Invalid) {
5704 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
5705 0 : ErrorsFound = true;
5706 : }
5707 358 : General::ProcessDateString(state,
5708 179 : ipsc->cAlphaArgs(2),
5709 179 : state.dataWeather->IDFDST.EnMon,
5710 179 : state.dataWeather->IDFDST.EnDay,
5711 179 : state.dataWeather->IDFDST.EnWeekDay,
5712 179 : state.dataWeather->IDFDST.EnDateType,
5713 : ErrorsFound);
5714 179 : if (state.dataWeather->IDFDST.EnDateType == DateType::Invalid) {
5715 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
5716 0 : ErrorsFound = true;
5717 : }
5718 179 : state.dataWeather->IDFDaylightSaving = true;
5719 : }
5720 592 : } else if (NumFound > 1) {
5721 0 : ShowSevereError(state, format("{}: Too many objects in Input File, only one allowed.", ipsc->cCurrentModuleObject));
5722 0 : ErrorsFound = true;
5723 : }
5724 771 : }
5725 :
5726 796 : void GetDesignDayData(EnergyPlusData &state,
5727 : int TotDesDays, // Total number of Design days to Setup
5728 : bool &ErrorsFound)
5729 : {
5730 :
5731 : // SUBROUTINE INFORMATION:
5732 : // AUTHOR Richard Liesen
5733 : // DATE WRITTEN September 1997
5734 :
5735 : // PURPOSE OF THIS SUBROUTINE:
5736 : // This subroutine retrieves the design day info from user input file
5737 : // which is later to be used in the Setup Design Day Routine.
5738 :
5739 : // REFERENCES:
5740 : // SizingPeriod:DesignDay,
5741 : // A1, \field Name
5742 : // N1, \field Month
5743 : // N2, \field Day of Month
5744 : // A2, \field Day Type
5745 : // N3, \field Maximum Dry-Bulb Temperature
5746 : // N4, \field Daily Dry-Bulb Temperature Range
5747 : // A3, \field Dry-Bulb Temperature Range Modifier Type
5748 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
5749 : // A5, \field Humidity Condition Type
5750 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
5751 : // A6, \field Humidity Condition Day Schedule Name
5752 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
5753 : // N7, \field Enthalpy at Maximum Dry-Bulb !will require units transition.
5754 : // N8, \field Daily Wet-Bulb Temperature Range
5755 : // N9, \field Barometric Pressure
5756 : // N10, \field Wind Speed
5757 : // N11, \field Wind Direction
5758 : // A7, \field Rain Indicator
5759 : // A8, \field Snow Indicator
5760 : // A9, \field Daylight Saving Time Indicator
5761 : // A10, \field Solar Model Indicator
5762 : // A11, \field Beam Solar Day Schedule Name
5763 : // A12, \field Diffuse Solar Day Schedule Name
5764 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
5765 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
5766 : // N14; \field Sky Clearness
5767 :
5768 : static constexpr std::array<std::string_view, static_cast<int>(DesDayHumIndType::Num)> DesDayHumIndTypeStringRep = {
5769 : "Wetbulb [C]",
5770 : "Dewpoint [C]",
5771 : "Enthalpy [J/kg]",
5772 : "Humidity Ratio []",
5773 : "Schedule []",
5774 : "WetBulbProfileDefaultMultipliers []",
5775 : "WetBulbProfileDifferenceSchedule []",
5776 : "WetBulbProfileMultiplierSchedule []"};
5777 :
5778 : // Below are the 2009 fractions, HOF, Chap 14, Table 6
5779 : 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,
5780 : 0.05, 0.00, 0.00, 0.06, 0.14, 0.24, 0.39, 0.50, 0.59, 0.68, 0.75, 0.82};
5781 :
5782 : static constexpr std::string_view routineName = "GetDesignDayData";
5783 :
5784 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5785 796 : std::string units;
5786 : Constant::Units unitType;
5787 :
5788 796 : state.dataWeather->DesDayInput.allocate(TotDesDays); // Allocate the array to the # of DD's
5789 796 : state.dataWeather->desDayMods.allocate(TotDesDays);
5790 2462 : for (int iDD = 1; iDD <= TotDesDays; ++iDD)
5791 1666 : state.dataWeather->desDayMods(iDD).allocate(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
5792 :
5793 796 : state.dataWeather->spSiteSchedules.dimension(TotDesDays, Weather::SPSiteSchedules());
5794 :
5795 796 : if (state.dataSysVars->ReverseDD && TotDesDays <= 1) {
5796 0 : ShowSevereError(state, "GetDesignDayData: Reverse Design Day requested but # Design Days <=1");
5797 : }
5798 :
5799 796 : auto const &ipsc = state.dataIPShortCut;
5800 796 : ipsc->cCurrentModuleObject = "SizingPeriod:DesignDay";
5801 2462 : for (int iDesDay = 1; iDesDay <= TotDesDays; ++iDesDay) {
5802 :
5803 : int EnvrnNum;
5804 1666 : if (!state.dataSysVars->ReverseDD) {
5805 1666 : EnvrnNum = iDesDay;
5806 0 : } else if (iDesDay == 1 && TotDesDays > 1) {
5807 0 : EnvrnNum = 2;
5808 0 : } else if (iDesDay == 2) {
5809 0 : EnvrnNum = 1;
5810 : } else {
5811 0 : EnvrnNum = iDesDay;
5812 : }
5813 :
5814 : // Call Input Get routine to retrieve design day data
5815 1666 : bool MaxDryBulbEntered = false;
5816 1666 : bool PressureEntered = false;
5817 : int NumAlpha; // Number of material alpha names being passed
5818 : int NumNumerics; // Number of material properties being passed
5819 : int IOStat; // IO Status when calling get input subroutine
5820 3332 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5821 1666 : ipsc->cCurrentModuleObject,
5822 : iDesDay,
5823 1666 : ipsc->cAlphaArgs,
5824 : NumAlpha,
5825 1666 : ipsc->rNumericArgs,
5826 : NumNumerics,
5827 : IOStat,
5828 1666 : ipsc->lNumericFieldBlanks,
5829 1666 : ipsc->lAlphaFieldBlanks,
5830 1666 : ipsc->cAlphaFieldNames,
5831 1666 : ipsc->cNumericFieldNames);
5832 :
5833 1666 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
5834 1666 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
5835 1666 : desDayInput.Title = ipsc->cAlphaArgs(1); // Environment name
5836 1666 : envCurr.Title = desDayInput.Title;
5837 :
5838 1666 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, desDayInput.Title};
5839 :
5840 : // N3, \field Maximum Dry-Bulb Temperature
5841 : // N4, \field Daily Dry-Bulb Temperature Range
5842 : // N9, \field Barometric Pressure
5843 : // N10, \field Wind Speed
5844 : // N11, \field Wind Direction
5845 1666 : desDayInput.MaxDryBulb = ipsc->rNumericArgs(3); // Maximum Dry-Bulb Temperature (C)
5846 1666 : MaxDryBulbEntered = !ipsc->lNumericFieldBlanks(3);
5847 1666 : desDayInput.DailyDBRange = ipsc->rNumericArgs(4); // Daily dry-bulb temperature range (deltaC)
5848 1666 : desDayInput.PressBarom = ipsc->rNumericArgs(9); // Atmospheric/Barometric Pressure (Pascals)
5849 1666 : PressureEntered = !ipsc->lNumericFieldBlanks(9);
5850 1666 : desDayInput.PressureEntered = PressureEntered;
5851 1666 : desDayInput.WindSpeed = ipsc->rNumericArgs(10); // Wind Speed (m/s)
5852 1666 : desDayInput.WindDir = mod(ipsc->rNumericArgs(11), 360.0); // Wind Direction
5853 : // (degrees clockwise from North, N=0, E=90, S=180, W=270)
5854 : // N1, \field Month
5855 : // N2, \field Day of Month
5856 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
5857 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
5858 : // N8, \field Daily Wet-Bulb Temperature Range
5859 1666 : desDayInput.Month = int(ipsc->rNumericArgs(1)); // Month of Year ( 1 - 12 )
5860 1666 : desDayInput.DayOfMonth = int(ipsc->rNumericArgs(2)); // Day of Month ( 1 - 31 )
5861 1666 : desDayInput.TauB = ipsc->rNumericArgs(12); // beam tau >= 0
5862 1666 : desDayInput.TauD = ipsc->rNumericArgs(13); // diffuse tau >= 0
5863 1666 : desDayInput.DailyWBRange = ipsc->rNumericArgs(8); // Daily wet-bulb temperature range (deltaC)
5864 :
5865 : // N14; \field Sky Clearness
5866 1666 : desDayInput.SkyClear = ipsc->rNumericArgs(14); // Sky Clearness (0 to 1)
5867 :
5868 : // N15, \field Maximum Warmup Days Between Sizing Periods
5869 1666 : if (ipsc->lNumericFieldBlanks(15)) {
5870 : // Default to -1 if not input
5871 1666 : desDayInput.maxWarmupDays = -1;
5872 : } else {
5873 0 : desDayInput.maxWarmupDays = int(ipsc->rNumericArgs(15));
5874 : }
5875 : // A13, \field Begin Environment Reset Mode
5876 1666 : if (ipsc->lAlphaFieldBlanks(13)) {
5877 1666 : desDayInput.suppressBegEnvReset = false;
5878 : } else {
5879 0 : if (Util::SameString(ipsc->cAlphaArgs(13), "FullResetAtBeginEnvironment")) {
5880 0 : desDayInput.suppressBegEnvReset = false;
5881 0 : } else if (Util::SameString(ipsc->cAlphaArgs(13), "SuppressThermalResetAtBeginEnvironment")) {
5882 0 : desDayInput.suppressBegEnvReset = true;
5883 : }
5884 : }
5885 : // for PerformancePrecisionTradeoffs
5886 1666 : if (state.dataEnvrn->forceBeginEnvResetSuppress) {
5887 2 : desDayInput.suppressBegEnvReset = true;
5888 : }
5889 : // A7, \field Rain Indicator
5890 : BooleanSwitch b;
5891 :
5892 1666 : if (ipsc->lAlphaFieldBlanks(7)) {
5893 6 : desDayInput.RainInd = 0;
5894 1660 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
5895 1660 : desDayInput.RainInd = (int)b;
5896 : } else {
5897 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7), "No");
5898 0 : desDayInput.RainInd = 0;
5899 : }
5900 :
5901 : // A8, \field Snow Indicator
5902 1666 : if (ipsc->lAlphaFieldBlanks(8)) {
5903 6 : desDayInput.SnowInd = 0;
5904 1660 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
5905 1660 : desDayInput.SnowInd = (int)b;
5906 : } else {
5907 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8), "No");
5908 0 : desDayInput.SnowInd = 0;
5909 : }
5910 :
5911 : // A3, \field Dry-Bulb Temperature Range Modifier Type
5912 : // check DB profile input
5913 1666 : if (ipsc->lAlphaFieldBlanks(3)) {
5914 1532 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
5915 268 : } else if ((desDayInput.dryBulbRangeType = static_cast<DesDayDryBulbRangeType>(
5916 134 : getEnumValue(DesDayDryBulbRangeTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))))) != DesDayDryBulbRangeType::Invalid) {
5917 : } else {
5918 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5919 0 : ErrorsFound = true;
5920 0 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
5921 : }
5922 :
5923 1666 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
5924 3 : units = "[]";
5925 3 : unitType = Constant::Units::None;
5926 1663 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
5927 2 : units = "[deltaC]";
5928 2 : unitType = Constant::Units::deltaC;
5929 1661 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
5930 1 : units = "[C]";
5931 1 : unitType = Constant::Units::C;
5932 : }
5933 :
5934 1666 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile && !MaxDryBulbEntered && ipsc->cAlphaArgs(3) != "invalid field") {
5935 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(3), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5936 0 : ErrorsFound = true;
5937 : }
5938 :
5939 : // Assume either "multiplier" option will make full use of range...
5940 1666 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Difference &&
5941 1664 : desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
5942 1663 : Real64 testval = desDayInput.MaxDryBulb - desDayInput.DailyDBRange;
5943 1663 : if (testval < -90.0 || testval > 70.0) {
5944 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
5945 0 : ShowContinueError(state, format("{} ({.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(3), testval));
5946 0 : ErrorsFound = true;
5947 : }
5948 : }
5949 :
5950 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
5951 1666 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Default) {
5952 : // Default dry-bulb temperature Range
5953 1660 : Real64 LastHrValue = DefaultTempRangeMult[23];
5954 41500 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
5955 267984 : for (int ts = 1; ts <= state.dataGlobal->NumOfTimeStepInHour; ++ts) {
5956 228144 : Real64 WNow = state.dataWeather->Interpolation(ts);
5957 228144 : Real64 WPrev = 1.0 - WNow;
5958 228144 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutDryBulbTemp =
5959 228144 : LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
5960 : }
5961 39840 : LastHrValue = DefaultTempRangeMult[hour - 1];
5962 : }
5963 :
5964 6 : } else if (ipsc->lAlphaFieldBlanks(4)) {
5965 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaFieldNames(3), "SCHEDULE");
5966 0 : ErrorsFound = true;
5967 :
5968 6 : } else if ((desDayInput.TempRangeSchPtr = ScheduleManager::GetDayScheduleIndex(state, ipsc->cAlphaArgs(4))) == 0) {
5969 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5970 0 : ErrorsFound = true;
5971 :
5972 : } else {
5973 12 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
5974 6 : ScheduleManager::GetSingleDayScheduleValues(state, desDayInput.TempRangeSchPtr, tmp);
5975 6 : auto &desDayModEnvrn = state.dataWeather->desDayMods(EnvrnNum);
5976 150 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr) {
5977 720 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS) {
5978 576 : desDayModEnvrn(iTS, iHr).OutDryBulbTemp = tmp(iTS, iHr);
5979 : }
5980 : }
5981 :
5982 6 : if (std::find(state.dataWeather->spSiteSchedNums.begin(), state.dataWeather->spSiteSchedNums.end(), desDayInput.TempRangeSchPtr) ==
5983 12 : state.dataWeather->spSiteSchedNums.end()) {
5984 4 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.TempRangeSchPtr);
5985 8 : SetupOutputVariable(state,
5986 : "Sizing Period Site Drybulb Temperature Range Modifier Schedule Value",
5987 : unitType,
5988 4 : state.dataWeather->spSiteSchedules(EnvrnNum).OutDryBulbTemp,
5989 : OutputProcessor::TimeStepType::Zone,
5990 : OutputProcessor::StoreType::Average,
5991 4 : ipsc->cAlphaArgs(4));
5992 : }
5993 :
5994 6 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
5995 3 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.TempRangeSchPtr, 0.0, false, 1.0, false)) {
5996 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
5997 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4)));
5998 0 : ShowContinueError(state, "..Specified [Schedule] Dry-bulb Range Multiplier Values are not within [0.0, 1.0]");
5999 0 : ErrorsFound = true;
6000 : }
6001 3 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) { // delta, must be > 0.0
6002 2 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.TempRangeSchPtr, 0.0, false)) {
6003 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6004 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4)));
6005 0 : ShowContinueError(state, "Some [Schedule] Dry-bulb Range Difference Values are < 0.0 [would make max larger].");
6006 0 : ErrorsFound = true;
6007 : }
6008 : }
6009 :
6010 6 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6011 6 : Real64 testval = std::numeric_limits<Real64>::min();
6012 150 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr) {
6013 720 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS) {
6014 576 : if (desDayModsEnvrn(iTS, iHr).OutDryBulbTemp > testval) testval = desDayModsEnvrn(iTS, iHr).OutDryBulbTemp;
6015 : }
6016 : }
6017 :
6018 6 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
6019 1 : if (MaxDryBulbEntered) {
6020 0 : ShowWarningError(state, format("{}=\"{}\", data override.", ipsc->cCurrentModuleObject, desDayInput.Title));
6021 0 : ShowContinueError(state, format("..{}=[{:.2R}] will be overwritten.", ipsc->cNumericFieldNames(3), desDayInput.MaxDryBulb));
6022 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3)));
6023 0 : ShowContinueError(state, format("..with max value=[{:.2R}].", testval));
6024 : }
6025 1 : desDayInput.MaxDryBulb = testval;
6026 : }
6027 :
6028 6 : testval = desDayInput.MaxDryBulb - testval;
6029 6 : if (testval < -90.0 || testval > 70.0) {
6030 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6031 : // should this be cNumericFieldNames?
6032 0 : ShowContinueError(state, format("{} = ({.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(4), testval));
6033 0 : ErrorsFound = true;
6034 : }
6035 6 : }
6036 :
6037 : // A5, \field Humidity Condition Type
6038 1666 : desDayInput.HumIndType = static_cast<DesDayHumIndType>(getEnumValue(DesDayHumIndTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
6039 :
6040 1666 : switch (desDayInput.HumIndType) {
6041 1640 : case DesDayHumIndType::WetBulb: {
6042 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
6043 1640 : if (ipsc->lNumericFieldBlanks(5)) {
6044 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6045 0 : ErrorsFound = true;
6046 : } else {
6047 1640 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6048 : }
6049 :
6050 1640 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6051 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6052 0 : ShowContinueError(
6053 0 : state, format("{} = {.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - WetBulb", desDayInput.HumIndValue));
6054 0 : ErrorsFound = true;
6055 : }
6056 1640 : } break;
6057 :
6058 5 : case DesDayHumIndType::DewPoint: {
6059 5 : if (ipsc->lNumericFieldBlanks(5)) {
6060 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6061 0 : ErrorsFound = true;
6062 : } else {
6063 5 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6064 : }
6065 :
6066 5 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6067 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6068 0 : ShowContinueError(
6069 : state,
6070 0 : format("{} = {.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - DewPoint", desDayInput.HumIndValue));
6071 0 : ErrorsFound = true;
6072 : }
6073 5 : } break;
6074 :
6075 0 : case DesDayHumIndType::HumRatio: {
6076 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
6077 0 : if (ipsc->lNumericFieldBlanks(6)) {
6078 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(6), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6079 0 : ErrorsFound = true;
6080 : } else {
6081 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(6); // Humidity Indicating Conditions at Max Dry-Bulb
6082 : }
6083 :
6084 0 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 0.03) {
6085 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6086 0 : ShowContinueError(
6087 : state,
6088 0 : format("{} = {.2R} is out of range [0.0, 0.03]", ipsc->cAlphaFieldNames(5) + " - Humidity-Ratio", desDayInput.HumIndValue));
6089 0 : ErrorsFound = true;
6090 : }
6091 0 : } break;
6092 :
6093 3 : case DesDayHumIndType::Enthalpy: {
6094 : // N7, \field Enthalpy at Maximum Dry-Bulb {J/kg}.
6095 3 : if (ipsc->lNumericFieldBlanks(7)) {
6096 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(7), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6097 0 : ErrorsFound = true;
6098 : } else {
6099 3 : desDayInput.HumIndValue = ipsc->rNumericArgs(7); // Humidity Indicating Conditions at Max Dry-Bulb
6100 : }
6101 :
6102 3 : desDayInput.HumIndType = DesDayHumIndType::Enthalpy;
6103 3 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 130000.0) {
6104 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6105 0 : ShowContinueError(
6106 : state,
6107 0 : format("{} = {.0R} is out of range [0.0, 130000.0]", ipsc->cAlphaFieldNames(5) + " - Enthalpy", desDayInput.HumIndValue));
6108 0 : ErrorsFound = true;
6109 : }
6110 3 : } break;
6111 :
6112 3 : case DesDayHumIndType::RelHumSch: {
6113 3 : units = "[%]";
6114 3 : unitType = Constant::Units::Perc;
6115 3 : } break;
6116 :
6117 2 : case DesDayHumIndType::WBProfMul: {
6118 2 : units = "[]";
6119 2 : unitType = Constant::Units::None;
6120 2 : if (ipsc->lNumericFieldBlanks(5)) {
6121 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6122 0 : ErrorsFound = true;
6123 : } else {
6124 2 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6125 : }
6126 2 : } break;
6127 :
6128 1 : case DesDayHumIndType::WBProfDif: {
6129 1 : units = "[]";
6130 1 : unitType = Constant::Units::None;
6131 1 : if (ipsc->lNumericFieldBlanks(5)) {
6132 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6133 0 : ErrorsFound = true;
6134 : } else {
6135 1 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6136 : }
6137 1 : } break;
6138 :
6139 12 : case DesDayHumIndType::WBProfDef: {
6140 12 : if (ipsc->lNumericFieldBlanks(5)) {
6141 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6142 0 : ErrorsFound = true;
6143 : } else {
6144 12 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6145 : }
6146 12 : } break;
6147 :
6148 0 : default: {
6149 0 : ShowWarningError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6150 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6151 0 : ShowContinueError(state, "WetBulb will be used. Maximum Dry Bulb will be used as WetBulb at Maximum Dry Bulb.");
6152 0 : desDayInput.HumIndType = DesDayHumIndType::WetBulb;
6153 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(3);
6154 0 : } break;
6155 : } // switch (desDayInput.HumIndType)
6156 :
6157 : // resolve humidity schedule if needed
6158 : // A6, \field Humidity Condition Day Schedule Name
6159 1666 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch || desDayInput.HumIndType == DesDayHumIndType::WBProfMul ||
6160 1661 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6161 6 : if (ipsc->lAlphaFieldBlanks(6)) {
6162 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6163 0 : ErrorsFound = true;
6164 6 : } else if ((desDayInput.HumIndSchPtr = ScheduleManager::GetDayScheduleIndex(state, ipsc->cAlphaArgs(6))) == 0) {
6165 0 : ShowWarningItemNotFound(state,
6166 : eoh,
6167 0 : ipsc->cAlphaFieldNames(6),
6168 0 : ipsc->cAlphaArgs(6),
6169 : "Default Humidity (constant for day using Humidity Indicator Temp).");
6170 : // reset HumIndType ?
6171 : } else {
6172 12 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
6173 6 : ScheduleManager::GetSingleDayScheduleValues(state, desDayInput.HumIndSchPtr, tmp);
6174 :
6175 6 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6176 150 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr)
6177 720 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS)
6178 576 : desDayModsEnvrn(iTS, iHr).OutRelHum = tmp(iTS, iHr);
6179 :
6180 6 : if (std::find(state.dataWeather->spSiteSchedNums.begin(), state.dataWeather->spSiteSchedNums.end(), desDayInput.HumIndSchPtr) ==
6181 12 : state.dataWeather->spSiteSchedNums.end()) {
6182 5 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.HumIndSchPtr);
6183 10 : SetupOutputVariable(state,
6184 : "Sizing Period Site Humidity Condition Schedule Value",
6185 : unitType,
6186 5 : state.dataWeather->spSiteSchedules(EnvrnNum).OutRelHum,
6187 : OutputProcessor::TimeStepType::Zone,
6188 : OutputProcessor::StoreType::Average,
6189 5 : ipsc->cAlphaArgs(6));
6190 : }
6191 :
6192 6 : switch (desDayInput.HumIndType) {
6193 3 : case DesDayHumIndType::RelHumSch: {
6194 3 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.HumIndSchPtr, 0.0, false, 100.0, false)) {
6195 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6196 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6)));
6197 0 : ShowContinueError(state, "Specified [Scheduled] Relative Humidity Values are not within [0.0, 100.0]");
6198 0 : ErrorsFound = true;
6199 : }
6200 3 : } break;
6201 2 : case DesDayHumIndType::WBProfMul: {
6202 : // multiplier: use schedule value, check 0 <= v <= 1
6203 2 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.HumIndSchPtr, 0.0, false, 1.0, false)) {
6204 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6205 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6)));
6206 0 : ShowContinueError(state, "..Specified [Schedule] Wet-bulb Profile Range Multiplier Values are not within [0.0, 1.0]");
6207 0 : ErrorsFound = true;
6208 : }
6209 2 : } break;
6210 1 : case DesDayHumIndType::WBProfDif: {
6211 1 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.HumIndSchPtr, 0.0, false)) {
6212 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6213 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6)));
6214 0 : ShowContinueError(state, "Some [Schedule] Wet-bulb Profile Difference Values are < 0.0 [would make max larger].");
6215 0 : ErrorsFound = true;
6216 : }
6217 1 : } break;
6218 0 : default: {
6219 0 : } break;
6220 : } // switch (desDayInput.HumIndType)
6221 6 : } // if (desDayInput.HumIndSchPtr == 0)
6222 :
6223 1666 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
6224 : // re WetBulbProfileDefaultMultipliers
6225 12 : Real64 LastHrValue = DefaultTempRangeMult[23];
6226 300 : for (int hour = 1; hour <= Constant::HoursInDay; ++hour) {
6227 1440 : for (int ts = 1; ts <= state.dataGlobal->NumOfTimeStepInHour; ++ts) {
6228 1152 : Real64 WNow = state.dataWeather->Interpolation(ts);
6229 1152 : Real64 WPrev = 1.0 - WNow;
6230 1152 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutRelHum = LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
6231 : }
6232 288 : LastHrValue = DefaultTempRangeMult[hour - 1];
6233 : }
6234 : }
6235 :
6236 : // verify that design WB or DP <= design DB
6237 1666 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint || desDayInput.HumIndType == DesDayHumIndType::WetBulb ||
6238 21 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::WBProfDef ||
6239 7 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6240 1660 : if (desDayInput.HumIndValue > desDayInput.MaxDryBulb) {
6241 0 : ShowWarningError(state, format("{}=\"{}\", range check data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6242 0 : ShowContinueError(state,
6243 0 : format("..Humidity Indicator Temperature at Max Temperature={:.1R} > Max DryBulb={:.1R}",
6244 0 : desDayInput.HumIndValue,
6245 0 : desDayInput.MaxDryBulb));
6246 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6247 0 : ShowContinueError(state, "..Conditions for day will be set to Relative Humidity = 100%");
6248 0 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint) {
6249 0 : desDayInput.DewPointNeedsSet = true;
6250 : } else {
6251 : // wet-bulb
6252 0 : desDayInput.HumIndValue = desDayInput.MaxDryBulb;
6253 : }
6254 : }
6255 : }
6256 :
6257 : // A10, \field Solar Model Indicator
6258 1666 : if (ipsc->lAlphaFieldBlanks(10)) {
6259 6 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6260 3320 : } else if ((desDayInput.solarModel = static_cast<DesDaySolarModel>(
6261 1660 : getEnumValue(DesDaySolarModelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(10))))) != DesDaySolarModel::Invalid) {
6262 : } else {
6263 0 : ShowWarningInvalidKey(state, eoh, ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "ASHRAE ClearSky");
6264 0 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6265 : }
6266 :
6267 1666 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
6268 : // A11, \field Beam Solar Day Schedule Name
6269 3 : if (ipsc->lAlphaFieldBlanks(11)) {
6270 : // should have entered beam schedule
6271 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(11), "", "");
6272 0 : ErrorsFound = true;
6273 3 : } else if ((desDayInput.BeamSolarSchPtr = ScheduleManager::GetDayScheduleIndex(state, ipsc->cAlphaArgs(11))) == 0) {
6274 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11));
6275 0 : ErrorsFound = true;
6276 : } else {
6277 6 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
6278 3 : ScheduleManager::GetSingleDayScheduleValues(state, desDayInput.BeamSolarSchPtr, tmp);
6279 3 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6280 75 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr)
6281 360 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS)
6282 288 : desDayModsEnvrn(iTS, iHr).BeamSolarRad = tmp(iTS, iHr);
6283 :
6284 3 : unitType = Constant::Units::W_m2;
6285 3 : units = "[W/m2]";
6286 6 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6287 6 : state.dataWeather->spSiteSchedNums.end(),
6288 9 : desDayInput.BeamSolarSchPtr) == state.dataWeather->spSiteSchedNums.end()) {
6289 3 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.BeamSolarSchPtr);
6290 6 : SetupOutputVariable(state,
6291 : "Sizing Period Site Beam Solar Schedule Value",
6292 : unitType,
6293 3 : state.dataWeather->spSiteSchedules(EnvrnNum).BeamSolarRad,
6294 : OutputProcessor::TimeStepType::Zone,
6295 : OutputProcessor::StoreType::Average,
6296 3 : ipsc->cAlphaArgs(11));
6297 : }
6298 :
6299 3 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.BeamSolarSchPtr, 0.0, false)) {
6300 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6301 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11)));
6302 0 : ShowContinueError(state, "..Specified [Schedule] Values are not >= 0.0");
6303 0 : ErrorsFound = true;
6304 : }
6305 3 : }
6306 :
6307 : // A12, \field Diffuse Solar Day Schedule Name
6308 3 : if (ipsc->lAlphaFieldBlanks(12)) {
6309 : // should have entered diffuse schedule
6310 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(12), "", "");
6311 0 : ErrorsFound = true;
6312 3 : } else if ((desDayInput.DiffuseSolarSchPtr = ScheduleManager::GetDayScheduleIndex(state, ipsc->cAlphaArgs(12))) == 0) {
6313 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12));
6314 0 : ErrorsFound = true;
6315 : } else {
6316 6 : Array2D<Real64> tmp = Array2D<Real64>(state.dataGlobal->NumOfTimeStepInHour, Constant::HoursInDay);
6317 3 : ScheduleManager::GetSingleDayScheduleValues(state, desDayInput.DiffuseSolarSchPtr, tmp);
6318 3 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6319 75 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr)
6320 360 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS)
6321 288 : desDayModsEnvrn(iTS, iHr).DifSolarRad = tmp(iTS, iHr);
6322 :
6323 3 : units = "[W/m2]";
6324 3 : unitType = Constant::Units::W_m2;
6325 6 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6326 6 : state.dataWeather->spSiteSchedNums.end(),
6327 9 : desDayInput.DiffuseSolarSchPtr) == state.dataWeather->spSiteSchedNums.end()) {
6328 3 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.DiffuseSolarSchPtr);
6329 6 : SetupOutputVariable(state,
6330 : "Sizing Period Site Diffuse Solar Schedule Value",
6331 : unitType,
6332 3 : state.dataWeather->spSiteSchedules(EnvrnNum).DifSolarRad,
6333 : OutputProcessor::TimeStepType::Zone,
6334 : OutputProcessor::StoreType::Average,
6335 3 : ipsc->cAlphaArgs(12));
6336 : }
6337 3 : if (!ScheduleManager::CheckDayScheduleValueMinMax(state, desDayInput.DiffuseSolarSchPtr, 0.0, false)) {
6338 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6339 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12)));
6340 0 : ShowContinueError(state, "..Specified [Schedule] Values are not >= 0.0");
6341 0 : ErrorsFound = true;
6342 : }
6343 3 : }
6344 :
6345 1663 : } else if (desDayInput.solarModel == DesDaySolarModel::ASHRAE_ClearSky) {
6346 1574 : if (ipsc->lNumericFieldBlanks(14)) {
6347 0 : ShowWarningEmptyField(
6348 0 : state, eoh, ipsc->cNumericFieldNames(14), ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "Zero clear sky (no solar)");
6349 : }
6350 : }
6351 :
6352 : // Validate Design Day Month
6353 :
6354 1666 : switch (desDayInput.Month) {
6355 1643 : case 1:
6356 : case 3:
6357 : case 5:
6358 : case 7:
6359 : case 8:
6360 : case 10:
6361 : case 12: {
6362 1643 : if (desDayInput.DayOfMonth > 31) {
6363 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6364 0 : ShowContinueError(
6365 : state,
6366 0 : format(".. invalid field: {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6367 0 : ErrorsFound = true;
6368 : }
6369 1643 : } break;
6370 21 : case 4:
6371 : case 6:
6372 : case 9:
6373 : case 11: {
6374 21 : if (desDayInput.DayOfMonth > 30) {
6375 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6376 0 : ShowContinueError(
6377 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6378 0 : ErrorsFound = true;
6379 : }
6380 21 : } break;
6381 2 : case 2: {
6382 2 : if (desDayInput.DayOfMonth > 28) {
6383 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6384 0 : ShowContinueError(
6385 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6386 0 : ErrorsFound = true;
6387 : }
6388 2 : } break;
6389 0 : default: {
6390 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6391 0 : ShowContinueError(state, format(".. invalid {} invalid (Month) [{}].", ipsc->cNumericFieldNames(1), desDayInput.Month));
6392 0 : ErrorsFound = true;
6393 0 : } break;
6394 : } // switch (desDayInput.Month)
6395 :
6396 : // A9, \field Daylight Saving Time Indicator
6397 1666 : if (ipsc->lAlphaFieldBlanks(9)) {
6398 6 : desDayInput.DSTIndicator = 0;
6399 1660 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(9)))) != BooleanSwitch::Invalid) {
6400 1660 : desDayInput.DSTIndicator = (int)b;
6401 : } else {
6402 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(9), ipsc->cAlphaArgs(9), "No");
6403 0 : desDayInput.DSTIndicator = 0;
6404 : }
6405 :
6406 : // A2, \field Day Type
6407 1666 : desDayInput.DayType = getEnumValue(ScheduleManager::dayTypeNamesUC, ipsc->cAlphaArgs(2));
6408 1666 : if (desDayInput.DayType <= 0) {
6409 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
6410 0 : ErrorsFound = true;
6411 : }
6412 :
6413 1666 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
6414 1666 : envCurr.Title = desDayInput.Title;
6415 1666 : envCurr.KindOfEnvrn = Constant::KindOfSim::DesignDay;
6416 1666 : envCurr.DesignDayNum = EnvrnNum;
6417 1666 : envCurr.RunPeriodDesignNum = 0;
6418 1666 : envCurr.TotalDays = 1;
6419 1666 : envCurr.StartMonth = desDayInput.Month;
6420 1666 : envCurr.StartDay = desDayInput.DayOfMonth;
6421 1666 : envCurr.EndMonth = envCurr.StartMonth;
6422 1666 : envCurr.EndDay = envCurr.StartDay;
6423 1666 : envCurr.DayOfWeek = 0;
6424 1666 : envCurr.UseDST = false;
6425 1666 : envCurr.UseHolidays = false;
6426 1666 : envCurr.StartJDay = designDay.DayOfYear;
6427 1666 : envCurr.EndJDay = envCurr.StartJDay;
6428 :
6429 : // create predefined report on design day
6430 1666 : std::string envTitle = desDayInput.Title;
6431 1666 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDmaxDB, envTitle, desDayInput.MaxDryBulb);
6432 1666 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDrange, envTitle, desDayInput.DailyDBRange);
6433 1666 : if (desDayInput.HumIndType != DesDayHumIndType::RelHumSch) {
6434 1663 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, desDayInput.HumIndValue);
6435 : } else {
6436 3 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, "N/A");
6437 : }
6438 3332 : OutputReportPredefined::PreDefTableEntry(
6439 3332 : state, state.dataOutRptPredefined->pdchDDhumTyp, envTitle, DesDayHumIndTypeStringRep[(int)desDayInput.HumIndType]);
6440 1666 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindSp, envTitle, desDayInput.WindSpeed);
6441 1666 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindDr, envTitle, desDayInput.WindDir);
6442 : }
6443 796 : }
6444 :
6445 796 : void GetLocationInfo(EnergyPlusData &state, bool &ErrorsFound)
6446 : {
6447 :
6448 : // SUBROUTINE INFORMATION:
6449 : // AUTHOR Richard Liesen
6450 : // DATE WRITTEN October 1997
6451 :
6452 : // PURPOSE OF THIS SUBROUTINE:
6453 : // This subroutine gets the location info from the IDF file; latitude,
6454 : // longitude and time zone number.
6455 :
6456 796 : auto const &ipsc = state.dataIPShortCut;
6457 796 : ipsc->cCurrentModuleObject = "Site:Location";
6458 796 : int const NumLocations = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6459 :
6460 796 : if (NumLocations > 1) {
6461 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6462 0 : ErrorsFound = true;
6463 : }
6464 :
6465 796 : if (NumLocations == 1) {
6466 : int LocNumAlpha; // Number of alpha names being passed
6467 : int LocNumProp; // Number of properties being passed
6468 : int IOStat; // IO Status when calling get input subroutine
6469 789 : Array1D_string LocAlphas(2); // Temporary array to transfer location info (non-numerics)
6470 789 : Array1D<Real64> LocProps(4); // Temporary array to transfer location info (numerics)
6471 : // Call Input Get routine to retrieve Location information
6472 1578 : state.dataInputProcessing->inputProcessor->getObjectItem(
6473 789 : state, ipsc->cCurrentModuleObject, 1, LocAlphas, LocNumAlpha, LocProps, LocNumProp, IOStat);
6474 :
6475 : // set latitude, longitude, and time zone number variables
6476 789 : state.dataWeather->LocationTitle = LocAlphas(1);
6477 789 : state.dataEnvrn->Latitude = LocProps(1);
6478 789 : state.dataEnvrn->Longitude = LocProps(2);
6479 789 : state.dataEnvrn->TimeZoneNumber = LocProps(3);
6480 789 : state.dataEnvrn->Elevation = LocProps(4);
6481 789 : if (Util::SameString(LocAlphas(2), "Yes")) state.dataWeather->keepUserSiteLocationDefinition = true;
6482 789 : state.dataWeather->LocationGathered = true;
6483 789 : }
6484 796 : }
6485 :
6486 796 : void GetWeatherProperties(EnergyPlusData &state, bool &ErrorsFound)
6487 : {
6488 :
6489 : // SUBROUTINE INFORMATION:
6490 : // AUTHOR Linda Lawrie
6491 : // DATE WRITTEN July 2009
6492 :
6493 : // PURPOSE OF THIS SUBROUTINE:
6494 : // Weather properties are an advanced concept for simulation. Primarily, these properties are
6495 : // used in the test suite runs that have specific requirements for certain properties (such as
6496 : // sky temperature).
6497 :
6498 : // REFERENCES:
6499 : // WeatherProperty:SkyTemperature,
6500 : // \memo This object is used to override internal sky temperature calculations.
6501 : // A1, \field Name
6502 : // \reference DesignDays
6503 : // \note leave blank for RunPeriods (until we name them)
6504 : // \note This field references the applicable design day or runperiod(s) if left blank.
6505 : // A2, \field Calculation Type
6506 : // \type choice
6507 : // \key ScheduleValue
6508 : // \key DifferenceScheduleDryBulbValue
6509 : // \key DifferenceScheduleDewPointValue
6510 : // \key AlgorithmA
6511 : // A3; \field Schedule Name
6512 : // \type object-list
6513 : // \object-list DayScheduleNames
6514 : // \object-list ScheduleNames
6515 :
6516 : static constexpr std::string_view routineName = "GetWeatherProperties";
6517 :
6518 : int Found;
6519 : int envFound;
6520 :
6521 796 : auto const &ipsc = state.dataIPShortCut;
6522 796 : ipsc->cCurrentModuleObject = "WeatherProperty:SkyTemperature";
6523 796 : state.dataWeather->NumWPSkyTemperatures = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6524 :
6525 796 : state.dataWeather->WPSkyTemperature.allocate(state.dataWeather->NumWPSkyTemperatures); // by default, not used.
6526 :
6527 798 : for (int i = 1; i <= state.dataWeather->NumWPSkyTemperatures; ++i) {
6528 : int IOStat;
6529 : int NumAlpha;
6530 : int NumNumerics;
6531 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6532 2 : ipsc->cCurrentModuleObject,
6533 : i,
6534 2 : ipsc->cAlphaArgs,
6535 : NumAlpha,
6536 2 : ipsc->rNumericArgs,
6537 : NumNumerics,
6538 : IOStat,
6539 2 : ipsc->lNumericFieldBlanks,
6540 2 : ipsc->lAlphaFieldBlanks,
6541 2 : ipsc->cAlphaFieldNames,
6542 2 : ipsc->cNumericFieldNames);
6543 :
6544 2 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
6545 2 : auto &wpSkyTemp = state.dataWeather->WPSkyTemperature(i);
6546 2 : if (ipsc->cAlphaArgs(1).empty()) {
6547 1 : Found = 0;
6548 14 : for (int j = 1; j <= state.dataWeather->NumOfEnvrn; ++j) {
6549 13 : auto &environJ = state.dataWeather->Environment(j);
6550 13 : if (environJ.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) continue;
6551 1 : if (environJ.WP_Type1 != 0) {
6552 0 : ShowSevereError(state,
6553 0 : format("{}: {}=\"{}\", indicated Environment Name already assigned.",
6554 : routineName,
6555 0 : ipsc->cCurrentModuleObject,
6556 0 : ipsc->cAlphaArgs(1)));
6557 0 : if (!environJ.Title.empty()) {
6558 0 : ShowContinueError(state,
6559 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6560 0 : environJ.Title,
6561 0 : ipsc->cCurrentModuleObject,
6562 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6563 : } else {
6564 0 : ShowContinueError(state,
6565 0 : format("... Runperiod Environment, already using {}=\"{}\".",
6566 0 : ipsc->cCurrentModuleObject,
6567 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6568 : }
6569 0 : ErrorsFound = true;
6570 : } else {
6571 1 : environJ.WP_Type1 = i;
6572 1 : Found = j;
6573 : }
6574 : }
6575 1 : if (Found == 0) {
6576 0 : ShowWarningError(state, "GetWeatherProperties: WeatherProperty:SkyTemperature=blank, no run periods found.");
6577 0 : ShowContinueError(state, "...SkyTemperature will not be applied.");
6578 0 : continue;
6579 : }
6580 : } else { // really a name
6581 1 : Found = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataWeather->Environment, &EnvironmentData::Title);
6582 1 : envFound = Found;
6583 1 : if (Found == 0) {
6584 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
6585 0 : ErrorsFound = true;
6586 0 : continue;
6587 : }
6588 :
6589 1 : auto &envrnFound = state.dataWeather->Environment(Found);
6590 1 : if (envrnFound.WP_Type1 != 0) {
6591 0 : ShowSevereError(state,
6592 0 : format("{}:{}=\"{}\", indicated Environment Name already assigned.",
6593 : routineName,
6594 0 : ipsc->cCurrentModuleObject,
6595 0 : ipsc->cAlphaArgs(1)));
6596 0 : ShowContinueError(state,
6597 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6598 0 : envrnFound.Title,
6599 0 : ipsc->cCurrentModuleObject,
6600 0 : state.dataWeather->WPSkyTemperature(envrnFound.WP_Type1).Name));
6601 0 : ErrorsFound = true;
6602 : } else {
6603 1 : state.dataWeather->Environment(Found).WP_Type1 = i;
6604 : }
6605 : }
6606 :
6607 2 : wpSkyTemp.Name = !ipsc->lAlphaFieldBlanks(1) ? ipsc->cAlphaArgs(1) : "All RunPeriods";
6608 :
6609 : // Validate Calculation Type.
6610 2 : std::string units;
6611 : Constant::Units unitType;
6612 2 : wpSkyTemp.skyTempModel = static_cast<SkyTempModel>(getEnumValue(Weather::SkyTempModelNamesUC, ipsc->cAlphaArgs(2)));
6613 :
6614 2 : switch (wpSkyTemp.skyTempModel) {
6615 2 : case SkyTempModel::ScheduleValue: {
6616 2 : wpSkyTemp.IsSchedule = true;
6617 2 : units = "[C]";
6618 2 : unitType = Constant::Units::C;
6619 2 : } break;
6620 0 : case SkyTempModel::DryBulbDelta:
6621 : case SkyTempModel::DewPointDelta: {
6622 0 : wpSkyTemp.IsSchedule = true;
6623 0 : units = "[deltaC]";
6624 0 : unitType = Constant::Units::deltaC;
6625 0 : } break;
6626 0 : case SkyTempModel::Brunt:
6627 : case SkyTempModel::Idso:
6628 : case SkyTempModel::BerdahlMartin:
6629 : case SkyTempModel::ClarkAllen: {
6630 0 : wpSkyTemp.IsSchedule = false;
6631 0 : } break;
6632 0 : default: {
6633 : // Bad inputs are trapped by input processor
6634 0 : assert(false);
6635 : }
6636 : }
6637 :
6638 2 : if (wpSkyTemp.IsSchedule) {
6639 2 : wpSkyTemp.ScheduleName = ipsc->cAlphaArgs(3);
6640 3 : if (state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather ||
6641 1 : state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
6642 1 : wpSkyTemp.ScheduleName = ipsc->cAlphaArgs(3);
6643 : // See if it's a schedule.
6644 1 : Found = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(3));
6645 1 : if (Found == 0) {
6646 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6647 0 : ErrorsFound = true;
6648 : } else {
6649 1 : wpSkyTemp.IsSchedule = true;
6650 1 : wpSkyTemp.SchedulePtr = Found;
6651 : }
6652 : } else { // See if it's a valid schedule.
6653 1 : Found = ScheduleManager::GetDayScheduleIndex(state, ipsc->cAlphaArgs(3));
6654 1 : if (Found == 0) {
6655 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6656 0 : ErrorsFound = true;
6657 : } else {
6658 1 : if (envFound != 0) {
6659 1 : if (std::find(state.dataWeather->spSiteSchedNums.begin(), state.dataWeather->spSiteSchedNums.end(), Found) ==
6660 2 : state.dataWeather->spSiteSchedNums.end()) {
6661 1 : state.dataWeather->spSiteSchedNums.emplace_back(Found);
6662 2 : SetupOutputVariable(state,
6663 : "Sizing Period Site Sky Temperature Schedule Value",
6664 : unitType,
6665 1 : state.dataWeather->spSiteSchedules(envFound).SkyTemp,
6666 : OutputProcessor::TimeStepType::Zone,
6667 : OutputProcessor::StoreType::Average,
6668 1 : ipsc->cAlphaArgs(3));
6669 : }
6670 1 : wpSkyTemp.IsSchedule = true;
6671 1 : wpSkyTemp.SchedulePtr = Found;
6672 : }
6673 : }
6674 : }
6675 : }
6676 :
6677 : BooleanSwitch b;
6678 2 : if (!wpSkyTemp.IsSchedule && !ipsc->lAlphaFieldBlanks(4)) {
6679 0 : if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
6680 0 : wpSkyTemp.UseWeatherFileHorizontalIR = static_cast<bool>(b);
6681 : } else {
6682 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
6683 0 : ErrorsFound = true;
6684 : }
6685 : } else {
6686 2 : wpSkyTemp.UseWeatherFileHorizontalIR = true;
6687 : }
6688 2 : }
6689 3652 : for (auto &envCurr : state.dataWeather->Environment) {
6690 2856 : if (envCurr.WP_Type1 != 0 && state.dataWeather->NumWPSkyTemperatures >= envCurr.WP_Type1) {
6691 2 : envCurr.skyTempModel = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel;
6692 2 : envCurr.UseWeatherFileHorizontalIR = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).UseWeatherFileHorizontalIR;
6693 : }
6694 : }
6695 796 : }
6696 :
6697 796 : void GetGroundTemps(EnergyPlusData &state)
6698 : {
6699 :
6700 : // SUBROUTINE INFORMATION:
6701 : // AUTHOR Richard Liesen
6702 : // DATE WRITTEN October 1997
6703 :
6704 : // PURPOSE OF THIS SUBROUTINE:
6705 : // This file reads the Ground Temps from the input file and puts them
6706 : // in a new variable.
6707 :
6708 : // Initialize Site:GroundTemperature:BuildingSurface object
6709 2388 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr = GroundTemperatureManager::GetGroundTempModelAndInit(
6710 1592 : state, GroundTemperatureManager::groundTempModelNamesUC[(int)GroundTempObjType::SiteBuildingSurfaceGroundTemp], "");
6711 :
6712 : // Initialize Site:GroundTemperature:FCFactorMethod object
6713 2388 : state.dataWeather->siteFCFactorMethodGroundTempsPtr = GroundTemperatureManager::GetGroundTempModelAndInit(
6714 1592 : state, GroundTemperatureManager::groundTempModelNamesUC[static_cast<int>(GroundTempObjType::SiteFCFactorMethodGroundTemp)], "");
6715 :
6716 : // Initialize Site:GroundTemperature:Shallow object
6717 2388 : state.dataWeather->siteShallowGroundTempsPtr = GroundTemperatureManager::GetGroundTempModelAndInit(
6718 1592 : state, GroundTemperatureManager::groundTempModelNamesUC[static_cast<int>(GroundTempObjType::SiteShallowGroundTemp)], "");
6719 :
6720 : // Initialize Site:GroundTemperature:Deep object
6721 2388 : state.dataWeather->siteDeepGroundTempsPtr = GroundTemperatureManager::GetGroundTempModelAndInit(
6722 1592 : state, GroundTemperatureManager::groundTempModelNamesUC[static_cast<int>(GroundTempObjType::SiteDeepGroundTemp)], "");
6723 796 : }
6724 :
6725 796 : void GetGroundReflectances(EnergyPlusData &state, bool &ErrorsFound)
6726 : {
6727 :
6728 : // SUBROUTINE INFORMATION:
6729 : // AUTHOR Linda Lawrie
6730 : // DATE WRITTEN March 2002
6731 :
6732 : // PURPOSE OF THIS SUBROUTINE:
6733 : // This file reads the Ground Reflectances from the input file (optional) and
6734 : // places them in the monthly array.
6735 :
6736 796 : auto const &ipsc = state.dataIPShortCut;
6737 796 : ipsc->cCurrentModuleObject = "Site:GroundReflectance";
6738 796 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6739 796 : if (nObjs != 0) {
6740 3 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6741 3 : Array1D<Real64> GndProps(12); // Temporary array to transfer ground reflectances
6742 3 : if (nObjs == 1) {
6743 : int GndNumAlpha; // Number of construction alpha names being passed
6744 : int GndNumProp; // dummy variable for properties being passed
6745 : int IOStat; // IO Status when calling get input subroutine
6746 : // Get the object names for each construction from the input processor
6747 6 : state.dataInputProcessing->inputProcessor->getObjectItem(
6748 3 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6749 :
6750 3 : if (GndNumProp < 12) {
6751 0 : ShowSevereError(state, format("{}: Less than 12 values entered.", ipsc->cCurrentModuleObject));
6752 0 : ErrorsFound = true;
6753 : }
6754 :
6755 : // Assign the ground reflectances to the variable
6756 3 : state.dataWeather->GroundReflectances({1, 12}) = GndProps({1, 12});
6757 :
6758 : } else {
6759 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6760 0 : ErrorsFound = true;
6761 : }
6762 3 : }
6763 :
6764 : // Write Final Ground Reflectance Information to the initialization output file
6765 796 : print(state.files.eio,
6766 : "{}\n",
6767 : "! "
6768 : "<Site:GroundReflectance>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{dimensionless},"
6769 : "May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{dimensionless},Oct{"
6770 : "dimensionless},Nov{dimensionless},Dec{dimensionless}");
6771 :
6772 796 : print(state.files.eio, " Site:GroundReflectance");
6773 10348 : for (int i = 1; i <= 12; ++i) {
6774 9552 : print(state.files.eio, ", {:5.2F}", state.dataWeather->GroundReflectances(i));
6775 : }
6776 796 : print(state.files.eio, "\n");
6777 796 : }
6778 :
6779 796 : void GetSnowGroundRefModifiers(EnergyPlusData &state, bool &ErrorsFound)
6780 : {
6781 :
6782 : // SUBROUTINE INFORMATION:
6783 : // AUTHOR Linda Lawrie
6784 : // DATE WRITTEN March 2002
6785 :
6786 : // PURPOSE OF THIS SUBROUTINE:
6787 : // This file reads the Snow Ground Reflectance Modifiers from the input file (optional) and
6788 : // places them in the variables.
6789 :
6790 796 : auto const &ipsc = state.dataIPShortCut;
6791 796 : ipsc->cCurrentModuleObject = "Site:GroundReflectance:SnowModifier";
6792 796 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6793 796 : if (nObjs != 0) {
6794 2 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6795 2 : Array1D<Real64> GndProps(2); // Temporary array to transfer ground reflectances
6796 2 : if (nObjs == 1) {
6797 : int GndNumAlpha; // Number of construction alpha names being passed
6798 : int GndNumProp; // dummy variable for properties being passed
6799 : int IOStat; // IO Status when calling get input subroutine
6800 : // Get the object names for each construction from the input processor
6801 4 : state.dataInputProcessing->inputProcessor->getObjectItem(
6802 2 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6803 :
6804 : // Assign the ground reflectances to the variable
6805 2 : state.dataWeather->SnowGndRefModifier = GndProps(1);
6806 2 : state.dataWeather->SnowGndRefModifierForDayltg = GndProps(2);
6807 :
6808 : } else {
6809 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6810 0 : ErrorsFound = true;
6811 : }
6812 2 : }
6813 :
6814 : // Write Final Ground Reflectance Modifier Information to the initialization output file
6815 796 : print(state.files.eio, "{}\n", "! <Site:GroundReflectance:SnowModifier>, Normal, Daylighting {dimensionless}");
6816 : static constexpr std::string_view Format_720(" Site:GroundReflectance:SnowModifier, {:7.3F}, {:7.3F}\n");
6817 796 : print(state.files.eio, Format_720, state.dataWeather->SnowGndRefModifier, state.dataWeather->SnowGndRefModifierForDayltg);
6818 :
6819 796 : print(state.files.eio,
6820 : "{}\n",
6821 : "! "
6822 : "<Site:GroundReflectance:Snow>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
6823 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
6824 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
6825 796 : print(state.files.eio, "{}", " Site:GroundReflectance:Snow");
6826 10348 : for (int i = 1; i <= 12; ++i) {
6827 9552 : print(state.files.eio, ", {:5.2F}", max(min(state.dataWeather->GroundReflectances(i) * state.dataWeather->SnowGndRefModifier, 1.0), 0.0));
6828 : }
6829 796 : print(state.files.eio, "\n");
6830 796 : print(state.files.eio,
6831 : "{}\n",
6832 : "! "
6833 : "<Site:GroundReflectance:Snow:Daylighting>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
6834 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
6835 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
6836 796 : print(state.files.eio, " Site:GroundReflectance:Snow:Daylighting");
6837 10348 : for (nObjs = 1; nObjs <= 12; ++nObjs) {
6838 9552 : print(state.files.eio,
6839 : ", {:5.2F}",
6840 19104 : max(min(state.dataWeather->GroundReflectances(nObjs) * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0));
6841 : }
6842 796 : print(state.files.eio, "\n");
6843 796 : }
6844 :
6845 796 : void GetWaterMainsTemperatures(EnergyPlusData &state, bool &ErrorsFound)
6846 : {
6847 :
6848 : // SUBROUTINE INFORMATION:
6849 : // AUTHOR Peter Graham Ellis
6850 : // DATE WRITTEN January 2005
6851 :
6852 : // PURPOSE OF THIS SUBROUTINE:
6853 : // Reads the input data for the WATER MAINS TEMPERATURES object.
6854 :
6855 796 : constexpr std::string_view routineName = "GetWaterMainsTemperatures";
6856 :
6857 796 : auto const &ipsc = state.dataIPShortCut;
6858 796 : ipsc->cCurrentModuleObject = "Site:WaterMainsTemperature";
6859 796 : int NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6860 :
6861 796 : if (NumObjects == 1) {
6862 : int NumAlphas; // Number of elements in the alpha array
6863 : int NumNums; // Number of elements in the numeric array
6864 : int IOStat; // IO Status when calling get input subroutine
6865 129 : Array1D_string AlphArray(2); // Character string data
6866 129 : Array1D<Real64> NumArray(2); // Numeric data
6867 387 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6868 129 : ipsc->cCurrentModuleObject,
6869 : 1,
6870 : AlphArray,
6871 : NumAlphas,
6872 : NumArray,
6873 : NumNums,
6874 : IOStat,
6875 129 : ipsc->lNumericFieldBlanks,
6876 129 : ipsc->lAlphaFieldBlanks,
6877 129 : ipsc->cAlphaFieldNames,
6878 129 : ipsc->cNumericFieldNames);
6879 :
6880 129 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
6881 :
6882 258 : state.dataWeather->WaterMainsTempsMethod =
6883 129 : static_cast<Weather::WaterMainsTempCalcMethod>(getEnumValue(waterMainsCalcMethodNamesUC, AlphArray(1)));
6884 :
6885 129 : switch (state.dataWeather->WaterMainsTempsMethod) {
6886 3 : case WaterMainsTempCalcMethod::Schedule: {
6887 3 : state.dataWeather->WaterMainsTempsScheduleName = AlphArray(2);
6888 3 : state.dataWeather->WaterMainsTempsSchedule = ScheduleManager::GetScheduleIndex(state, AlphArray(2));
6889 3 : if (state.dataWeather->WaterMainsTempsSchedule == 0) {
6890 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
6891 0 : ErrorsFound = true;
6892 : }
6893 3 : } break;
6894 126 : case WaterMainsTempCalcMethod::Correlation: {
6895 126 : if (NumNums == 0) {
6896 0 : ShowSevereError(state, format("{}: Missing Annual Average and Maximum Difference fields.", ipsc->cCurrentModuleObject));
6897 0 : ErrorsFound = true;
6898 126 : } else if (NumNums == 1) {
6899 0 : ShowSevereError(state, format("{}: Missing Maximum Difference field.", ipsc->cCurrentModuleObject));
6900 0 : ErrorsFound = true;
6901 : } else {
6902 126 : state.dataWeather->WaterMainsTempsAnnualAvgAirTemp = NumArray(1);
6903 126 : state.dataWeather->WaterMainsTempsMaxDiffAirTemp = NumArray(2);
6904 : }
6905 126 : } break;
6906 0 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile: {
6907 : // No action
6908 0 : } break;
6909 0 : default: {
6910 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), AlphArray(1));
6911 0 : ErrorsFound = true;
6912 0 : } break;
6913 : } // switch
6914 :
6915 796 : } else if (NumObjects > 1) {
6916 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6917 0 : ErrorsFound = true;
6918 : }
6919 796 : }
6920 :
6921 2900842 : void CalcWaterMainsTemp(EnergyPlusData &state)
6922 : {
6923 :
6924 : // SUBROUTINE INFORMATION:
6925 : // AUTHOR Peter Graham Ellis
6926 : // DATE WRITTEN January 2005
6927 : // MODIFIED June 2018, B. Nigusse
6928 :
6929 : // PURPOSE OF THIS SUBROUTINE:
6930 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
6931 :
6932 : // METHODOLOGY EMPLOYED:
6933 : // Water mains temperature is either taken from a schedule or calculated by a correlation. The correlation
6934 : // is fit to Fahrenheit units, so the air temperature values are first convert to F, then mains temperature
6935 : // is calculated and converted back to C.
6936 :
6937 2900842 : switch (state.dataWeather->WaterMainsTempsMethod) {
6938 6942 : case WaterMainsTempCalcMethod::Schedule:
6939 6942 : state.dataEnvrn->WaterMainsTemp = ScheduleManager::GetCurrentScheduleValue(state, state.dataWeather->WaterMainsTempsSchedule);
6940 6942 : break;
6941 512386 : case WaterMainsTempCalcMethod::Correlation:
6942 512386 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(
6943 512386 : state, state.dataWeather->WaterMainsTempsAnnualAvgAirTemp, state.dataWeather->WaterMainsTempsMaxDiffAirTemp);
6944 512386 : break;
6945 0 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
6946 0 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
6947 0 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
6948 0 : state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp,
6949 0 : state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff);
6950 : } else {
6951 0 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
6952 : }
6953 0 : break;
6954 2381514 : default:
6955 2381514 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
6956 2381514 : break;
6957 : }
6958 2900842 : }
6959 :
6960 512386 : Real64 WaterMainsTempFromCorrelation(EnergyPlusData &state, Real64 const AnnualOAAvgDryBulbTemp, Real64 const MonthlyOAAvgDryBulbTempMaxDiff)
6961 : {
6962 :
6963 : // SUBROUTINE INFORMATION:
6964 : // AUTHOR Peter Graham Ellis
6965 : // DATE WRITTEN January 2005
6966 : // MODIFIED B Nigusse June 2018 (Refactored)
6967 :
6968 : // PURPOSE OF THIS SUBROUTINE:
6969 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
6970 :
6971 : // METHODOLOGY EMPLOYED:
6972 : // Water mains temperature calculated by a correlation. The correlation is fit to Fahrenheit units, so the
6973 : // air temperature values are first convert to F, then mains temperature is calculated and converted back to C.
6974 : // used for Calculated Method: 'Correlation' and 'CorrelationFromWeatherFile'.
6975 :
6976 : // REFERENCES:
6977 : // Correlation developed by Jay Burch and Craig Christensen at NREL, described in:
6978 : // Hendron, R., Anderson, R., Christensen, C., Eastment, M., and Reeves, P. 2004. "Development of an Energy
6979 : // Savings Benchmark for All Residential End-Uses", Proceedings of SimBuild 2004, IBPSA-USA National Conference,
6980 : // Boulder, CO, August 4 - 6, 2004.
6981 :
6982 : // Annual Average Outdoor Air Temperature (F)
6983 512386 : Real64 const Tavg = AnnualOAAvgDryBulbTemp * (9.0 / 5.0) + 32.0;
6984 : // Maximum difference in monthly average outdoor air temperatures (deltaF)
6985 512386 : Real64 const Tdiff = MonthlyOAAvgDryBulbTempMaxDiff * (9.0 / 5.0);
6986 :
6987 512386 : Real64 const Ratio = 0.4 + 0.01 * (Tavg - 44.0);
6988 512386 : Real64 const Lag = 35.0 - 1.0 * (Tavg - 44.0);
6989 512386 : Real64 constexpr Offset = 6.0;
6990 512386 : int const latitude_sign = (state.dataEnvrn->Latitude >= 0) ? 1 : -1;
6991 :
6992 : // calculated water main temp (F)
6993 : Real64 CurrentWaterMainsTemp =
6994 512386 : Tavg + Offset +
6995 512386 : Ratio * (Tdiff / 2.0) * latitude_sign * std::sin((0.986 * (state.dataEnvrn->DayOfYear - 15.0 - Lag) - 90) * Constant::DegToRadians);
6996 :
6997 512386 : if (CurrentWaterMainsTemp < 32.0) CurrentWaterMainsTemp = 32.0;
6998 :
6999 : // Convert F to C
7000 512386 : return (CurrentWaterMainsTemp - 32.0) * (5.0 / 9.0);
7001 : }
7002 796 : void GetWeatherStation(EnergyPlusData &state, bool &ErrorsFound)
7003 : {
7004 :
7005 : // SUBROUTINE INFORMATION:
7006 : // AUTHOR Peter Graham Ellis
7007 : // DATE WRITTEN January 2006
7008 :
7009 : // PURPOSE OF THIS SUBROUTINE:
7010 : // Reads the input data for the WEATHER STATION object.
7011 :
7012 796 : auto const &ipsc = state.dataIPShortCut;
7013 796 : ipsc->cCurrentModuleObject = "Site:WeatherStation";
7014 796 : int const NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
7015 :
7016 : // Default conditions for a weather station in an open field at a height of 10 m. (These should match the IDD defaults.)
7017 796 : Real64 WeatherFileWindSensorHeight = 10.0; // Height of the wind sensor at the weather station, i.e., weather file
7018 796 : Real64 WeatherFileWindExp = 0.14; // Exponent for the wind velocity profile at the weather station
7019 796 : Real64 WeatherFileWindBLHeight = 270.0; // Boundary layer height for the wind velocity profile at the weather station (m)
7020 796 : Real64 WeatherFileTempSensorHeight = 1.5; // Height of the air temperature sensor at the weather station (m)
7021 :
7022 796 : if (NumObjects == 1) {
7023 : int NumAlphas; // Number of elements in the alpha array
7024 : int NumNums; // Number of elements in the numeric array
7025 : int IOStat; // IO Status when calling get input subroutine
7026 1 : Array1D_string AlphArray(1); // Character string data
7027 1 : Array1D<Real64> NumArray(4); // Numeric data
7028 2 : state.dataInputProcessing->inputProcessor->getObjectItem(
7029 1 : state, ipsc->cCurrentModuleObject, 1, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
7030 :
7031 1 : if (NumNums > 0) WeatherFileWindSensorHeight = NumArray(1);
7032 1 : if (NumNums > 1) WeatherFileWindExp = NumArray(2);
7033 1 : if (NumNums > 2) WeatherFileWindBLHeight = NumArray(3);
7034 1 : if (NumNums > 3) WeatherFileTempSensorHeight = NumArray(4);
7035 :
7036 796 : } else if (NumObjects > 1) {
7037 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
7038 0 : ErrorsFound = true;
7039 : }
7040 :
7041 796 : state.dataEnvrn->WeatherFileWindModCoeff = std::pow(WeatherFileWindBLHeight / WeatherFileWindSensorHeight, WeatherFileWindExp);
7042 1592 : state.dataEnvrn->WeatherFileTempModCoeff = DataEnvironment::AtmosphericTempGradient * DataEnvironment::EarthRadius *
7043 796 : WeatherFileTempSensorHeight / (DataEnvironment::EarthRadius + WeatherFileTempSensorHeight);
7044 :
7045 : // Write to the initialization output file
7046 796 : print(state.files.eio,
7047 : "{}\n",
7048 : "! <Environment:Weather Station>,Wind Sensor Height Above Ground {m},Wind Speed Profile Exponent "
7049 : "{},Wind Speed Profile Boundary Layer Thickness {m},Air Temperature Sensor Height Above Ground {m},Wind "
7050 : "Speed Modifier Coefficient-Internal,Temperature Modifier Coefficient-Internal");
7051 :
7052 : // Formats
7053 : static constexpr std::string_view Format_720("Environment:Weather Station,{:.3R},{:.3R},{:.3R},{:.3R},{:.3R},{:.3R}\n");
7054 796 : print(state.files.eio,
7055 : Format_720,
7056 : WeatherFileWindSensorHeight,
7057 : WeatherFileWindExp,
7058 : WeatherFileWindBLHeight,
7059 : WeatherFileTempSensorHeight,
7060 796 : state.dataEnvrn->WeatherFileWindModCoeff,
7061 796 : state.dataEnvrn->WeatherFileTempModCoeff);
7062 796 : }
7063 :
7064 2900842 : void DayltgCurrentExtHorizIllum(EnergyPlusData &state)
7065 : {
7066 :
7067 : // SUBROUTINE INFORMATION:
7068 : // AUTHOR Fred Winkelmann
7069 : // DATE WRITTEN July 1997
7070 : // MODIFIED Nov98 (FW); Nov 2000 (FW)
7071 :
7072 : // PURPOSE OF THIS SUBROUTINE:
7073 : // CALCULATES EXTERIOR DAYLIGHT ILLUMINANCE AND LUMINOUS EFFICACY
7074 :
7075 : // METHODOLOGY EMPLOYED:
7076 : // CALLED by SetCurrentWeather.
7077 : // CALCULATES THE CURRENT-TIME-STEP
7078 : // ILLUMINANCE ON AN UNOBSTRUCTED HORIZONTAL SURFACE FROM THE
7079 : // THE SKY AND FROM DIRECT SUN.
7080 :
7081 : // REFERENCES:
7082 : // Based on DOE-2.1E subroutine DEXTIL.
7083 :
7084 : // SOLCOS(3), below, is the cosine of the solar zenith angle.
7085 2900842 : if (state.dataEnvrn->SunIsUp) {
7086 : // Exterior horizontal beam irradiance (W/m2)
7087 1449972 : Real64 SDIRH = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z;
7088 : // Exterior horizontal sky diffuse irradiance (W/m2)
7089 1449972 : Real64 SDIFH = state.dataEnvrn->DifSolarRad;
7090 : // Fraction of sky covered by clouds
7091 1449972 : state.dataEnvrn->CloudFraction = pow_2(SDIFH / (SDIRH + SDIFH + 0.0001));
7092 : // Luminous efficacy of sky diffuse solar and beam solar (lumens/W);
7093 : // Horizontal illuminance from sky and horizontal beam illuminance (lux)
7094 : // obtained from solar quantities on weather file and luminous efficacy.
7095 :
7096 1449972 : DayltgLuminousEfficacy(state, state.dataEnvrn->PDIFLW, state.dataEnvrn->PDIRLW);
7097 1449972 : state.dataEnvrn->HISKF = SDIFH * state.dataEnvrn->PDIFLW;
7098 1449972 : state.dataEnvrn->HISUNF = SDIRH * state.dataEnvrn->PDIRLW;
7099 1449972 : state.dataEnvrn->HISUNFnorm = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW;
7100 : } else {
7101 1450870 : state.dataEnvrn->CloudFraction = 0.0;
7102 1450870 : state.dataEnvrn->PDIFLW = 0.0;
7103 1450870 : state.dataEnvrn->PDIRLW = 0.0;
7104 1450870 : state.dataEnvrn->HISKF = 0.0;
7105 1450870 : state.dataEnvrn->HISUNF = 0.0;
7106 1450870 : state.dataEnvrn->HISUNFnorm = 0.0;
7107 1450870 : state.dataEnvrn->SkyClearness = 0.0;
7108 1450870 : state.dataEnvrn->SkyBrightness = 0.0;
7109 : }
7110 2900842 : }
7111 :
7112 1449972 : void DayltgLuminousEfficacy(EnergyPlusData &state,
7113 : Real64 &DiffLumEff, // Luminous efficacy of sky diffuse solar radiation (lum/W)
7114 : Real64 &DirLumEff // Luminous efficacy of beam solar radiation (lum/W)
7115 : )
7116 : {
7117 : // SUBROUTINE INFORMATION:
7118 : // AUTHOR Fred Winkelmann
7119 : // DATE WRITTEN July 1997
7120 : // MODIFIED August 2009, BG fixed upper bound for sky clearness bin 7
7121 :
7122 : // PURPOSE OF THIS SUBROUTINE:
7123 : // Uses diffuse horizontal solar irradiance, direct normal solar
7124 : // irradiance, atmospheric moisture and sun position
7125 : // to determine the luminous efficacy in lumens/watt
7126 : // of sky diffuse solar radiation and direct normal solar radiation.
7127 : // Based on an empirical method described in
7128 : // R. Perez, P. Ineichen, R. Seals, J. Michalsky and R. Stewart,
7129 : // "Modeling daylight availability and irradiance components from direct
7130 : // global irradiance components from direct and global irradiance,"
7131 : // Solar Energy 44 (1990) 271-289.
7132 :
7133 : // Diffuse luminous efficacy coefficients
7134 : static constexpr std::array<Real64, 8> ADiffLumEff = {97.24, 107.22, 104.97, 102.39, 100.71, 106.42, 141.88, 152.23};
7135 : static constexpr std::array<Real64, 8> BDiffLumEff = {-0.46, 1.15, 2.96, 5.59, 5.94, 3.83, 1.90, 0.35};
7136 : static constexpr std::array<Real64, 8> CDiffLumEff = {12.00, 0.59, -5.53, -13.95, -22.75, -36.15, -53.24, -45.27};
7137 : static constexpr std::array<Real64, 8> DDiffLumEff = {-8.91, -3.95, -8.77, -13.90, -23.74, -28.83, -14.03, -7.98};
7138 : // Direct luminous efficacy coefficients
7139 : static constexpr std::array<Real64, 8> ADirLumEff = {57.20, 98.99, 109.83, 110.34, 106.36, 107.19, 105.75, 101.18};
7140 : static constexpr std::array<Real64, 8> BDirLumEff = {-4.55, -3.46, -4.90, -5.84, -3.97, -1.25, 0.77, 1.58};
7141 : static constexpr std::array<Real64, 8> CDirLumEff = {-2.98, -1.21, -1.71, -1.99, -1.75, -1.51, -1.26, -1.10};
7142 : static constexpr std::array<Real64, 8> DDirLumEff = {117.12, 12.38, -8.81, -4.56, -6.16, -26.73, -34.44, -8.29};
7143 : // Monthly exterrestrial direct normal illuminance (lum/m2)
7144 : static constexpr std::array<Real64, 12> ExtraDirNormIll = {
7145 : 131153.0, 130613.0, 128992.0, 126816.0, 124731.0, 123240.0, 122652.0, 123120.0, 124576.0, 126658.0, 128814.0, 130471.0};
7146 :
7147 1449972 : Real64 const SunZenith = std::acos(state.dataEnvrn->SOLCOS.z); // Solar zenith angle (radians)
7148 1449972 : Real64 const SunAltitude = Constant::PiOvr2 - SunZenith; // Solar altitude angle (radians)
7149 1449972 : Real64 const SinSunAltitude = std::sin(SunAltitude);
7150 : // Clearness of sky. SkyClearness close to 1.0 corresponds to an overcast sky.
7151 : // SkyClearness > 6 is a clear sky.
7152 : // DifSolarRad is the diffuse horizontal irradiance.
7153 : // BeamSolarRad is the direct normal irradiance.
7154 1449972 : Real64 const Zeta = 1.041 * pow_3(SunZenith);
7155 2899944 : state.dataEnvrn->SkyClearness =
7156 1449972 : ((state.dataEnvrn->DifSolarRad + state.dataEnvrn->BeamSolarRad) / (state.dataEnvrn->DifSolarRad + 0.0001) + Zeta) / (1.0 + Zeta);
7157 : // Relative optical air mass
7158 1449972 : Real64 const AirMass = (1.0 - 0.1 * state.dataEnvrn->Elevation / 1000.0) /
7159 1449972 : (SinSunAltitude + 0.15 / std::pow(SunAltitude / Constant::DegToRadians + 3.885, 1.253));
7160 : // In the following, 93.73 is the extraterrestrial luminous efficacy
7161 1449972 : state.dataEnvrn->SkyBrightness = (state.dataEnvrn->DifSolarRad * 93.73) * AirMass / ExtraDirNormIll[state.dataEnvrn->Month - 1];
7162 : int ISkyClearness; // Sky clearness bin
7163 1449972 : if (state.dataEnvrn->SkyClearness <= 1.065) {
7164 544746 : ISkyClearness = 0;
7165 905226 : } else if (state.dataEnvrn->SkyClearness <= 1.23) {
7166 11432 : ISkyClearness = 1;
7167 893794 : } else if (state.dataEnvrn->SkyClearness <= 1.50) {
7168 15009 : ISkyClearness = 2;
7169 878785 : } else if (state.dataEnvrn->SkyClearness <= 1.95) {
7170 20545 : ISkyClearness = 3;
7171 858240 : } else if (state.dataEnvrn->SkyClearness <= 2.80) {
7172 98692 : ISkyClearness = 4;
7173 759548 : } else if (state.dataEnvrn->SkyClearness <= 4.50) {
7174 275751 : ISkyClearness = 5;
7175 483797 : } else if (state.dataEnvrn->SkyClearness <= 6.20) {
7176 175838 : ISkyClearness = 6;
7177 : } else {
7178 307959 : ISkyClearness = 7;
7179 : }
7180 :
7181 : // Atmospheric moisture (cm of precipitable water)
7182 1449972 : Real64 const AtmosMoisture = std::exp(0.07 * state.dataEnvrn->OutDewPointTemp - 0.075);
7183 : // Sky diffuse luminous efficacy
7184 1449972 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7185 502068 : DiffLumEff = 0.0;
7186 : } else {
7187 947904 : DiffLumEff = ADiffLumEff[ISkyClearness] + BDiffLumEff[ISkyClearness] * AtmosMoisture +
7188 947904 : CDiffLumEff[ISkyClearness] * state.dataEnvrn->SOLCOS.z +
7189 947904 : DDiffLumEff[ISkyClearness] * std::log(state.dataEnvrn->SkyBrightness);
7190 : }
7191 : // Direct normal luminous efficacy
7192 1449972 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7193 502068 : DirLumEff = 0.0;
7194 : } else {
7195 947904 : DirLumEff =
7196 947904 : max(0.0,
7197 947904 : ADirLumEff[ISkyClearness] + BDirLumEff[ISkyClearness] * AtmosMoisture +
7198 947904 : CDirLumEff[ISkyClearness] * std::exp(5.73 * SunZenith - 5.0) + DDirLumEff[ISkyClearness] * state.dataEnvrn->SkyBrightness);
7199 : }
7200 1449972 : }
7201 :
7202 2140 : Real64 GetSTM(Real64 const Longitude) // Longitude from user input
7203 : {
7204 : // FUNCTION INFORMATION:
7205 : // AUTHOR Linda K. Lawrie
7206 : // DATE WRITTEN August 2003
7207 :
7208 : // PURPOSE OF THIS FUNCTION:
7209 : // This function determines the "standard time meridian" from the input
7210 : // longitude. Calculates the proper Meridian from Longitude. This
7211 : // value is needed for weather calculations so that the sun comes
7212 : // up and goes down at the right times.
7213 :
7214 : Real64 GetSTM;
7215 :
7216 2140 : Array1D<Real64> longl({-12, 12}); // Lower Longitude value for a Time Zone
7217 2140 : Array1D<Real64> longh({-12, 12}); // Upper Longitude value for a Time Zone
7218 :
7219 2140 : GetSTM = 0.0;
7220 :
7221 2140 : longl(0) = -7.5;
7222 2140 : longh(0) = 7.5;
7223 27820 : for (int i = 1; i <= 12; ++i) {
7224 25680 : longl(i) = longl(i - 1) + 15.0;
7225 25680 : longh(i) = longh(i - 1) + 15.0;
7226 : }
7227 27820 : for (int i = 1; i <= 12; ++i) {
7228 25680 : longl(-i) = longl(-i + 1) - 15.0;
7229 25680 : longh(-i) = longh(-i + 1) - 15.0;
7230 : }
7231 2140 : Real64 temp = mod(Longitude, 360.0);
7232 2140 : if (temp > 180.0) temp -= 180.0;
7233 : Real64 tz; // resultant tz meridian
7234 13560 : for (int i = -12; i <= 12; ++i) {
7235 13560 : if (temp > longl(i) && temp <= longh(i)) {
7236 2140 : tz = i;
7237 2140 : tz = mod(i, 24.0);
7238 2140 : GetSTM = tz;
7239 2140 : break;
7240 : }
7241 : }
7242 :
7243 2140 : return GetSTM;
7244 2140 : }
7245 :
7246 6304 : void ProcessEPWHeader(EnergyPlusData &state, EpwHeaderType const headerType, std::string &Line, bool &ErrorsFound)
7247 : {
7248 :
7249 : // SUBROUTINE INFORMATION:
7250 : // AUTHOR Linda Lawrie
7251 : // DATE WRITTEN December 1999
7252 :
7253 : // PURPOSE OF THIS SUBROUTINE:
7254 : // This subroutine processes each header line in the EPW weather file.
7255 :
7256 : // METHODOLOGY EMPLOYED:
7257 : // File is positioned to the correct line, then backspaced. This routine
7258 : // reads in the line and processes as appropriate.
7259 :
7260 : Weather::DateType dateType;
7261 : int NumHdArgs;
7262 :
7263 : // Strip off Header value from Line
7264 6304 : std::string::size_type Pos = index(Line, ',');
7265 6304 : if ((Pos == std::string::npos) && !((headerType == EpwHeaderType::Comments1) || (headerType == EpwHeaderType::Comments2))) {
7266 0 : ShowSevereError(state, "Invalid Header line in in.epw -- no commas");
7267 0 : ShowContinueError(state, format("Line={}", Line));
7268 0 : ShowFatalError(state, "Previous conditions cause termination.");
7269 : }
7270 6304 : if (Pos != std::string::npos) Line.erase(0, Pos + 1);
7271 :
7272 6304 : switch (headerType) {
7273 788 : case Weather::EpwHeaderType::Location: {
7274 :
7275 : // LOCATION, A1 [City], A2 [State/Province/Region], A3 [Country],
7276 : // A4 [Source], N1 [WMO], N2 [Latitude],
7277 : // N3 [Longitude], N4 [Time Zone], N5 [Elevation {m}]
7278 :
7279 788 : NumHdArgs = 9;
7280 7880 : for (int i = 1; i <= NumHdArgs; ++i) {
7281 7092 : strip(Line);
7282 7092 : Pos = index(Line, ',');
7283 7092 : if (Pos == std::string::npos) {
7284 784 : if (len(Line) == 0) {
7285 0 : while (Pos == std::string::npos) {
7286 0 : Line = state.files.inputWeatherFile.readLine().data;
7287 0 : strip(Line);
7288 0 : uppercase(Line);
7289 0 : Pos = index(Line, ',');
7290 : }
7291 : } else {
7292 784 : Pos = len(Line);
7293 : }
7294 : }
7295 :
7296 : switch (i) {
7297 788 : case 1:
7298 788 : state.dataWeather->EPWHeaderTitle = stripped(Line.substr(0, Pos));
7299 788 : break;
7300 2364 : case 2:
7301 : case 3:
7302 : case 4:
7303 2364 : state.dataWeather->EPWHeaderTitle = strip(state.dataWeather->EPWHeaderTitle) + ' ' + stripped(Line.substr(0, Pos));
7304 2364 : break;
7305 788 : case 5:
7306 788 : state.dataWeather->EPWHeaderTitle += " WMO#=" + stripped(Line.substr(0, Pos));
7307 788 : break;
7308 3152 : case 6:
7309 : case 7:
7310 : case 8:
7311 : case 9: {
7312 : bool errFlag;
7313 3152 : Real64 const Number = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7314 3152 : if (!errFlag) {
7315 : switch (i) {
7316 788 : case 6:
7317 788 : state.dataWeather->WeatherFileLatitude = Number;
7318 788 : break;
7319 788 : case 7:
7320 788 : state.dataWeather->WeatherFileLongitude = Number;
7321 788 : break;
7322 788 : case 8:
7323 788 : state.dataWeather->WeatherFileTimeZone = Number;
7324 788 : break;
7325 788 : case 9:
7326 788 : state.dataWeather->WeatherFileElevation = Number;
7327 788 : break;
7328 0 : default:
7329 0 : break;
7330 : }
7331 : }
7332 3152 : } break;
7333 0 : default:
7334 0 : ShowSevereError(state, format("GetEPWHeader:LOCATION, invalid numeric={}", Line.substr(0, Pos)));
7335 0 : ErrorsFound = true;
7336 0 : break;
7337 : }
7338 7092 : Line.erase(0, Pos + 1);
7339 : }
7340 788 : state.dataEnvrn->WeatherFileLocationTitle = stripped(state.dataWeather->EPWHeaderTitle);
7341 788 : } break;
7342 788 : case Weather::EpwHeaderType::TypicalExtremePeriods: {
7343 788 : strip(Line);
7344 788 : Pos = index(Line, ',');
7345 788 : if (Pos == std::string::npos) {
7346 0 : if (len(Line) == 0) {
7347 0 : while (Pos == std::string::npos && len(Line) == 0) {
7348 0 : Line = state.files.inputWeatherFile.readLine().data;
7349 0 : strip(Line);
7350 0 : Pos = index(Line, ',');
7351 : }
7352 : } else {
7353 0 : Pos = len(Line);
7354 : }
7355 : }
7356 : bool IOStatus;
7357 788 : state.dataWeather->NumEPWTypExtSets = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7358 788 : Line.erase(0, Pos + 1);
7359 788 : state.dataWeather->TypicalExtremePeriods.allocate(state.dataWeather->NumEPWTypExtSets);
7360 788 : int TropExtremeCount = 0;
7361 5387 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7362 4599 : strip(Line);
7363 4599 : Pos = index(Line, ',');
7364 4599 : if (Pos != std::string::npos) {
7365 4599 : state.dataWeather->TypicalExtremePeriods(i).Title = Line.substr(0, Pos);
7366 4599 : Line.erase(0, Pos + 1);
7367 : } else {
7368 0 : ShowWarningError(state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={}", Line.substr(0, Pos)));
7369 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7370 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7371 0 : break;
7372 : }
7373 4599 : Pos = index(Line, ',');
7374 4599 : if (Pos != std::string::npos) {
7375 4599 : state.dataWeather->TypicalExtremePeriods(i).TEType = Line.substr(0, Pos);
7376 4599 : Line.erase(0, Pos + 1);
7377 4599 : if (Util::SameString(state.dataWeather->TypicalExtremePeriods(i).TEType, "EXTREME")) {
7378 1576 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MAX")) {
7379 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7380 1576 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MIN")) {
7381 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7382 1576 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MAX")) {
7383 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMax";
7384 1576 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MIN")) {
7385 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMin";
7386 : // to account for problems earlier in weather files:
7387 1576 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7388 0 : if (TropExtremeCount == 0) {
7389 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Max";
7390 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7391 0 : ++TropExtremeCount;
7392 0 : } else if (TropExtremeCount == 1) {
7393 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Min";
7394 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7395 0 : ++TropExtremeCount;
7396 : }
7397 : } else { // make new short titles
7398 1576 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7399 723 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7400 853 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7401 723 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7402 130 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7403 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7404 65 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7405 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7406 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7407 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7408 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7409 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7410 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7411 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7412 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7413 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7414 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7415 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7416 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7417 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7418 : }
7419 : }
7420 : } else { // not extreme
7421 3023 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7422 723 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7423 2300 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7424 723 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7425 1577 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7426 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7427 1577 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7428 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7429 1577 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7430 723 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7431 854 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7432 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7433 854 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7434 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7435 853 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7436 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7437 788 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7438 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7439 723 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7440 723 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7441 : }
7442 : }
7443 : } else {
7444 0 : ShowWarningError(state,
7445 0 : format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={} {}",
7446 0 : state.dataWeather->TypicalExtremePeriods(i).Title,
7447 0 : Line.substr(0, Pos)));
7448 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7449 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7450 0 : break;
7451 : }
7452 : int PMonth;
7453 : int PDay;
7454 : int PWeekDay;
7455 4599 : Pos = index(Line, ',');
7456 4599 : if (Pos != std::string::npos) {
7457 4599 : std::string dateStringUC = Line.substr(0, Pos);
7458 4599 : dateStringUC = uppercase(dateStringUC);
7459 4599 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7460 4599 : if (dateType != DateType::Invalid) {
7461 4599 : if (PMonth != 0 && PDay != 0) {
7462 4599 : state.dataWeather->TypicalExtremePeriods(i).StartMonth = PMonth;
7463 4599 : state.dataWeather->TypicalExtremePeriods(i).StartDay = PDay;
7464 : }
7465 : } else {
7466 0 : ShowSevereError(
7467 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7468 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7469 0 : ErrorsFound = true;
7470 : }
7471 4599 : Line.erase(0, Pos + 1);
7472 4599 : }
7473 4599 : Pos = index(Line, ',');
7474 4599 : if (Pos != std::string::npos) {
7475 3811 : std::string dateStringUC = Line.substr(0, Pos);
7476 3811 : dateStringUC = uppercase(dateStringUC);
7477 3811 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7478 3811 : if (dateType != DateType::Invalid) {
7479 3811 : if (PMonth != 0 && PDay != 0) {
7480 3811 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7481 3811 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7482 : }
7483 : } else {
7484 0 : ShowSevereError(
7485 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7486 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7487 0 : ErrorsFound = true;
7488 : }
7489 3811 : Line.erase(0, Pos + 1);
7490 3811 : } else { // Pos=0, probably last one
7491 788 : std::string const dateStringUC = uppercase(Line);
7492 788 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7493 788 : if (dateType != DateType::Invalid) {
7494 788 : if (PMonth != 0 && PDay != 0) {
7495 788 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7496 788 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7497 : }
7498 : } else {
7499 0 : ShowSevereError(
7500 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7501 0 : ErrorsFound = true;
7502 : }
7503 788 : }
7504 : }
7505 : // Process periods to set up other values.
7506 5387 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7507 4599 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7508 : // JulianDay (Month,Day,LeapYearValue)
7509 4599 : std::string const ExtremePeriodTitle = Util::makeUPPER(typicalExtPer.ShortTitle);
7510 4599 : if (ExtremePeriodTitle == "SUMMER") {
7511 1446 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7512 723 : typicalExtPer.MatchValue = "SummerExtreme";
7513 723 : typicalExtPer.MatchValue1 = "TropicalHot";
7514 723 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7515 : } else {
7516 723 : typicalExtPer.MatchValue = "SummerTypical";
7517 : }
7518 :
7519 3153 : } else if (ExtremePeriodTitle == "WINTER") {
7520 1446 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7521 723 : typicalExtPer.MatchValue = "WinterExtreme";
7522 723 : typicalExtPer.MatchValue1 = "TropicalCold";
7523 723 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7524 : } else {
7525 723 : typicalExtPer.MatchValue = "WinterTypical";
7526 : }
7527 :
7528 1707 : } else if (ExtremePeriodTitle == "AUTUMN") {
7529 723 : typicalExtPer.MatchValue = "AutumnTypical";
7530 :
7531 984 : } else if (ExtremePeriodTitle == "SPRING") {
7532 723 : typicalExtPer.MatchValue = "SpringTypical";
7533 :
7534 261 : } else if (ExtremePeriodTitle == "WETSEASON") {
7535 65 : typicalExtPer.MatchValue = "WetSeason";
7536 :
7537 196 : } else if (ExtremePeriodTitle == "DRYSEASON") {
7538 65 : typicalExtPer.MatchValue = "DrySeason";
7539 :
7540 131 : } else if (ExtremePeriodTitle == "NOWETSEASON") {
7541 1 : typicalExtPer.MatchValue = "NoWetSeason";
7542 :
7543 130 : } else if (ExtremePeriodTitle == "NODRYSEASON") {
7544 0 : typicalExtPer.MatchValue = "NoDrySeason";
7545 :
7546 130 : } else if ((ExtremePeriodTitle == "NODRYSEASONMAX") || (ExtremePeriodTitle == "NOWETSEASONMAX")) {
7547 0 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7548 0 : typicalExtPer.MatchValue1 = "TropicalHot";
7549 0 : typicalExtPer.MatchValue2 = "SummerExtreme";
7550 :
7551 130 : } else if ((ExtremePeriodTitle == "NODRYSEASONMIN") || (ExtremePeriodTitle == "NOWETSEASONMIN")) {
7552 0 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7553 0 : typicalExtPer.MatchValue1 = "TropicalCold";
7554 0 : typicalExtPer.MatchValue2 = "WinterExtreme";
7555 :
7556 130 : } else if (ExtremePeriodTitle == "TROPICALHOT") {
7557 65 : typicalExtPer.MatchValue = "TropicalHot";
7558 65 : typicalExtPer.MatchValue1 = "SummerExtreme";
7559 65 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7560 :
7561 65 : } else if (ExtremePeriodTitle == "TROPICALCOLD") {
7562 65 : typicalExtPer.MatchValue = "TropicalCold";
7563 65 : typicalExtPer.MatchValue1 = "WinterExtreme";
7564 65 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7565 :
7566 : } else {
7567 0 : typicalExtPer.MatchValue = "Invalid - no match";
7568 : }
7569 4599 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, 0);
7570 4599 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, 0);
7571 4599 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7572 4593 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7573 : } else {
7574 6 : typicalExtPer.TotalDays =
7575 6 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7576 : }
7577 4599 : }
7578 788 : } break;
7579 788 : case Weather::EpwHeaderType::GroundTemperatures: {
7580 : // Added for ground surfaces defined with F or c factor method. TH 7/2009
7581 : // Assume the 0.5 m set of ground temperatures
7582 : // or first set on a weather file, if any.
7583 788 : Pos = index(Line, ',');
7584 788 : if (Pos != std::string::npos) {
7585 : bool errFlag;
7586 788 : int NumGrndTemps = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7587 788 : if (!errFlag && NumGrndTemps >= 1) {
7588 788 : Line.erase(0, Pos + 1);
7589 : // skip depth, soil conductivity, soil density, soil specific heat
7590 3940 : for (int i = 1; i <= 4; ++i) {
7591 3152 : Pos = index(Line, ',');
7592 3152 : if (Pos == std::string::npos) {
7593 0 : Line.clear();
7594 0 : break;
7595 : }
7596 3152 : Line.erase(0, Pos + 1);
7597 : }
7598 788 : state.dataWeather->GroundTempsFCFromEPWHeader = 0.0;
7599 788 : int actcount = 0;
7600 10244 : for (int i = 1; i <= 12; ++i) { // take the first set of ground temperatures.
7601 9456 : Pos = index(Line, ',');
7602 9456 : if (Pos != std::string::npos) {
7603 9456 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7604 9456 : ++actcount;
7605 : } else {
7606 0 : if (len(Line) > 0) {
7607 0 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7608 0 : ++actcount;
7609 : }
7610 0 : break;
7611 : }
7612 9456 : Line.erase(0, Pos + 1);
7613 : }
7614 788 : if (actcount == 12) state.dataWeather->wthFCGroundTemps = true;
7615 : }
7616 : }
7617 788 : } break;
7618 788 : case Weather::EpwHeaderType::HolidaysDST: {
7619 : // A1, \field LeapYear Observed
7620 : // \type choice
7621 : // \key Yes
7622 : // \key No
7623 : // \note Yes if Leap Year will be observed for this file
7624 : // \note No if Leap Year days (29 Feb) should be ignored in this file
7625 : // A2, \field Daylight Saving Start Day
7626 : // A3, \field Daylight Saving End Day
7627 : // N1, \field Number of Holidays
7628 : // A4, \field Holiday 1 Name
7629 : // A5, \field Holiday 1 Day
7630 : // etc.
7631 : // Start with Minimum number of NumHdArgs
7632 788 : uppercase(Line);
7633 788 : NumHdArgs = 4;
7634 788 : int CurCount = 0;
7635 3962 : for (int i = 1; i <= NumHdArgs; ++i) {
7636 3174 : strip(Line);
7637 3174 : Pos = index(Line, ',');
7638 3174 : if (Pos == std::string::npos) {
7639 784 : if (len(Line) == 0) {
7640 0 : while (Pos == std::string::npos) {
7641 0 : Line = state.files.inputWeatherFile.readLine().data;
7642 0 : strip(Line);
7643 0 : uppercase(Line);
7644 0 : Pos = index(Line, ',');
7645 : }
7646 : } else {
7647 784 : Pos = len(Line);
7648 : }
7649 : }
7650 :
7651 : int PMonth;
7652 : int PDay;
7653 : int PWeekDay;
7654 : bool IOStatus;
7655 3174 : if (i == 1) {
7656 788 : state.dataWeather->WFAllowsLeapYears = (Line[0] == 'Y');
7657 2386 : } else if (i == 2) {
7658 : // In this section, we call ProcessDateString, and if that fails, we can recover from it
7659 : // by setting DST to false, so we don't affect ErrorsFound
7660 :
7661 : // call ProcessDateString with local bool (unused)
7662 : bool errflag1;
7663 788 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, errflag1);
7664 788 : if (dateType != DateType::Invalid) {
7665 : // ErrorsFound is still false after ProcessDateString
7666 788 : if (PMonth == 0 && PDay == 0) {
7667 787 : state.dataWeather->EPWDaylightSaving = false;
7668 : } else {
7669 1 : state.dataWeather->EPWDaylightSaving = true;
7670 1 : state.dataWeather->EPWDST.StDateType = dateType;
7671 1 : state.dataWeather->EPWDST.StMon = PMonth;
7672 1 : state.dataWeather->EPWDST.StDay = PDay;
7673 1 : state.dataWeather->EPWDST.StWeekDay = PWeekDay;
7674 : }
7675 : } else {
7676 : // ErrorsFound is untouched
7677 0 : ShowContinueError(
7678 0 : state, format("ProcessEPWHeader: Invalid Daylight Saving Period Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7679 0 : ShowContinueError(state, format("...invalid header={}", epwHeaders[static_cast<int>(headerType)]));
7680 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7681 0 : state.dataWeather->EPWDaylightSaving = false;
7682 : }
7683 :
7684 1598 : } else if (i == 3) {
7685 788 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7686 788 : if (state.dataWeather->EPWDaylightSaving) {
7687 1 : if (dateType != DateType::Invalid) {
7688 1 : state.dataWeather->EPWDST.EnDateType = dateType;
7689 1 : state.dataWeather->EPWDST.EnMon = PMonth;
7690 1 : state.dataWeather->EPWDST.EnDay = PDay;
7691 1 : state.dataWeather->EPWDST.EnWeekDay = PWeekDay;
7692 : } else {
7693 0 : ShowWarningError(
7694 : state,
7695 0 : format("ProcessEPWHeader: Invalid Daylight Saving Period End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7696 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7697 0 : state.dataWeather->EPWDaylightSaving = false;
7698 : }
7699 1 : state.dataWeather->DST = state.dataWeather->EPWDST;
7700 : }
7701 :
7702 810 : } else if (i == 4) {
7703 788 : int NumEPWHolidays = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7704 1576 : state.dataWeather->NumSpecialDays =
7705 788 : NumEPWHolidays + state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriodControl:SpecialDays");
7706 788 : state.dataWeather->SpecialDays.allocate(state.dataWeather->NumSpecialDays);
7707 788 : NumHdArgs = 4 + NumEPWHolidays * 2;
7708 :
7709 22 : } else if ((i >= 5)) {
7710 22 : if (mod(i, 2) != 0) {
7711 11 : ++CurCount;
7712 11 : if (CurCount > state.dataWeather->NumSpecialDays) {
7713 0 : ShowSevereError(state, "Too many SpecialDays");
7714 0 : ErrorsFound = true;
7715 : } else {
7716 11 : state.dataWeather->SpecialDays(CurCount).Name = Line.substr(0, Pos);
7717 : }
7718 : // Process name
7719 : } else {
7720 11 : if (CurCount <= state.dataWeather->NumSpecialDays) {
7721 11 : auto &specialDay = state.dataWeather->SpecialDays(CurCount);
7722 : // Process date
7723 11 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7724 11 : if (dateType == DateType::MonthDay) {
7725 6 : specialDay.dateType = dateType;
7726 6 : specialDay.Month = PMonth;
7727 6 : specialDay.Day = PDay;
7728 6 : specialDay.WeekDay = 0;
7729 6 : specialDay.CompDate = PMonth * 32 + PDay;
7730 6 : specialDay.Duration = 1;
7731 6 : specialDay.DayType = 1;
7732 6 : specialDay.WthrFile = true;
7733 5 : } else if (dateType != DateType::Invalid) {
7734 5 : specialDay.dateType = dateType;
7735 5 : specialDay.Month = PMonth;
7736 5 : specialDay.Day = PDay;
7737 5 : specialDay.WeekDay = PWeekDay;
7738 5 : specialDay.CompDate = 0;
7739 5 : specialDay.Duration = 1;
7740 5 : specialDay.DayType = 1;
7741 5 : specialDay.WthrFile = true;
7742 0 : } else if (dateType == DateType::Invalid) {
7743 0 : ShowSevereError(state, format("Invalid SpecialDay Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7744 0 : ErrorsFound = true;
7745 : }
7746 : }
7747 : }
7748 : }
7749 3174 : Line.erase(0, Pos + 1);
7750 : }
7751 5387 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7752 : // General::OrdinalDay (Month,Day,LeapYearValue)
7753 4599 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7754 4599 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, state.dataWeather->LeapYearAdd);
7755 4599 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, state.dataWeather->LeapYearAdd);
7756 4599 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7757 4593 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7758 : } else {
7759 6 : typicalExtPer.TotalDays =
7760 6 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7761 : }
7762 : }
7763 788 : } break;
7764 2364 : case Weather::EpwHeaderType::Comments1:
7765 : case Weather::EpwHeaderType::Comments2:
7766 : case Weather::EpwHeaderType::DesignConditions: {
7767 : // no action
7768 2364 : } break;
7769 788 : case Weather::EpwHeaderType::DataPeriods: {
7770 : // N1, \field Number of Data Periods
7771 : // N2, \field Number of Records per hour
7772 : // A1, \field Data Period 1 Name/Description
7773 : // A2, \field Data Period 1 Start Day of Week
7774 : // \type choice
7775 : // \key Sunday
7776 : // \key Monday
7777 : // \key Tuesday
7778 : // \key Wednesday
7779 : // \key Thursday
7780 : // \key Friday
7781 : // \key Saturday
7782 : // A3, \field Data Period 1 Start Day
7783 : // A4, \field Data Period 1 End Day
7784 788 : uppercase(Line);
7785 788 : NumHdArgs = 2;
7786 788 : int CurCount = 0;
7787 5516 : for (int i = 1; i <= NumHdArgs; ++i) {
7788 4728 : strip(Line);
7789 4728 : Pos = index(Line, ',');
7790 4728 : if (Pos == std::string::npos) {
7791 788 : if (len(Line) == 0) {
7792 0 : while (Pos == std::string::npos) {
7793 0 : Line = state.files.inputWeatherFile.readLine().data;
7794 0 : strip(Line);
7795 0 : uppercase(Line);
7796 0 : Pos = index(Line, ',');
7797 : }
7798 : } else {
7799 788 : Pos = len(Line);
7800 : }
7801 : }
7802 :
7803 : bool IOStatus;
7804 4728 : if (i == 1) {
7805 788 : state.dataWeather->NumDataPeriods = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7806 788 : state.dataWeather->DataPeriods.allocate(state.dataWeather->NumDataPeriods);
7807 788 : NumHdArgs += 4 * state.dataWeather->NumDataPeriods;
7808 788 : if (state.dataWeather->NumDataPeriods > 0) {
7809 1576 : for (auto &e : state.dataWeather->DataPeriods)
7810 1576 : e.NumDays = 0;
7811 : }
7812 :
7813 3940 : } else if (i == 2) {
7814 788 : state.dataWeather->NumIntervalsPerHour = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7815 3152 : } else if (i >= 3) {
7816 3152 : int const CurOne = mod(i - 3, 4);
7817 : int PMonth;
7818 : int PDay;
7819 : int PWeekDay;
7820 : int PYear;
7821 3152 : if (CurOne == 0) {
7822 : // Description of Data Period
7823 788 : ++CurCount;
7824 788 : if (CurCount > state.dataWeather->NumDataPeriods) {
7825 0 : ShowSevereError(state, "Too many data periods");
7826 0 : ErrorsFound = true;
7827 : } else {
7828 788 : state.dataWeather->DataPeriods(CurCount).Name = Line.substr(0, Pos);
7829 : }
7830 :
7831 2364 : } else if (CurOne == 1) {
7832 : // Start Day of Week
7833 788 : if (CurCount <= state.dataWeather->NumDataPeriods) {
7834 788 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
7835 788 : dataPeriod.DayOfWeek = Line.substr(0, Pos);
7836 788 : dataPeriod.WeekDay = getEnumValue(ScheduleManager::dayTypeNamesUC, dataPeriod.DayOfWeek);
7837 788 : if (dataPeriod.WeekDay < 1 || dataPeriod.WeekDay > 7) {
7838 0 : ShowSevereError(state,
7839 0 : fmt::format("Weather File -- Invalid Start Day of Week for Data Period #{}, Invalid day={}",
7840 : CurCount,
7841 0 : dataPeriod.DayOfWeek));
7842 0 : ErrorsFound = true;
7843 : }
7844 : }
7845 :
7846 1576 : } else if (CurOne == 2) {
7847 : // DataPeriod Start Day
7848 788 : if (CurCount <= state.dataWeather->NumDataPeriods) {
7849 788 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
7850 788 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
7851 788 : if (dateType == DateType::MonthDay) {
7852 788 : dataPeriod.StMon = PMonth;
7853 788 : dataPeriod.StDay = PDay;
7854 788 : dataPeriod.StYear = PYear;
7855 788 : if (PYear != 0) dataPeriod.HasYearData = true;
7856 : } else {
7857 0 : ShowSevereError(state,
7858 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day> (WeatherFile), found={}",
7859 0 : Line.substr(0, Pos)));
7860 0 : ErrorsFound = true;
7861 : }
7862 : }
7863 :
7864 788 : } else if (CurOne == 3) {
7865 788 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
7866 788 : if (CurCount <= state.dataWeather->NumDataPeriods) {
7867 788 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
7868 788 : if (dateType == DateType::MonthDay) {
7869 788 : dataPeriod.EnMon = PMonth;
7870 788 : dataPeriod.EnDay = PDay;
7871 788 : dataPeriod.EnYear = PYear;
7872 788 : if (PYear == 0 && dataPeriod.HasYearData) {
7873 0 : ShowWarningError(state, "Data Period (WeatherFile) - Start Date contains year. End Date does not.");
7874 0 : ShowContinueError(state, "...Assuming same year as Start Date for this data.");
7875 0 : dataPeriod.EnYear = dataPeriod.StYear;
7876 : }
7877 : } else {
7878 0 : ShowSevereError(state,
7879 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day>, (WeatherFile) found={}",
7880 0 : Line.substr(0, Pos)));
7881 0 : ErrorsFound = true;
7882 : }
7883 : }
7884 788 : if (dataPeriod.StYear == 0 || dataPeriod.EnYear == 0) {
7885 788 : dataPeriod.DataStJDay = General::OrdinalDay(dataPeriod.StMon, dataPeriod.StDay, state.dataWeather->LeapYearAdd);
7886 788 : dataPeriod.DataEnJDay = General::OrdinalDay(dataPeriod.EnMon, dataPeriod.EnDay, state.dataWeather->LeapYearAdd);
7887 788 : if (dataPeriod.DataStJDay <= dataPeriod.DataEnJDay) {
7888 788 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
7889 : } else {
7890 0 : dataPeriod.NumDays = (365 - dataPeriod.DataStJDay + 1) + (dataPeriod.DataEnJDay - 1 + 1);
7891 : }
7892 : } else { // weather file has actual year(s)
7893 0 : dataPeriod.DataStJDay = computeJulianDate(dataPeriod.StYear, dataPeriod.StMon, dataPeriod.StDay);
7894 0 : dataPeriod.DataEnJDay = computeJulianDate(dataPeriod.EnYear, dataPeriod.EnMon, dataPeriod.EnDay);
7895 0 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
7896 : }
7897 : // Have processed the last item for this, can set up Weekdays for months
7898 788 : dataPeriod.MonWeekDay = 0;
7899 788 : if (!ErrorsFound) {
7900 2364 : SetupWeekDaysByMonth(state,
7901 788 : state.dataWeather->DataPeriods(CurCount).StMon,
7902 788 : state.dataWeather->DataPeriods(CurCount).StDay,
7903 788 : state.dataWeather->DataPeriods(CurCount).WeekDay,
7904 788 : state.dataWeather->DataPeriods(CurCount).MonWeekDay);
7905 : }
7906 : }
7907 : }
7908 4728 : Line.erase(0, Pos + 1);
7909 : }
7910 788 : } break;
7911 0 : default: {
7912 : // Invalid header type
7913 0 : assert(false);
7914 : } break;
7915 : }
7916 6304 : }
7917 :
7918 4975 : void SkipEPlusWFHeader(EnergyPlusData &state)
7919 : {
7920 :
7921 : // SUBROUTINE INFORMATION:
7922 : // AUTHOR Linda K. Lawrie
7923 : // DATE WRITTEN August 2000
7924 :
7925 : // PURPOSE OF THIS SUBROUTINE:
7926 : // This subroutine skips the initial header records on the EnergyPlus Weather File (in.epw).
7927 :
7928 : static constexpr std::string_view Header("DATA PERIODS");
7929 :
7930 : // Read in Header Information
7931 9950 : InputFile::ReadResult<std::string> Line{"", true, false};
7932 :
7933 : // Headers should come in order
7934 : while (true) {
7935 39800 : Line = state.files.inputWeatherFile.readLine();
7936 39800 : if (Line.eof) {
7937 0 : ShowFatalError(state,
7938 0 : format("Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}", Header),
7939 0 : OptionalOutputFileRef{state.files.eso});
7940 : }
7941 39800 : uppercase(Line.data);
7942 39800 : if (has(Line.data, Header)) break;
7943 : }
7944 :
7945 : // Dummy process Data Periods line
7946 : // 'DATA PERIODS'
7947 : // N1, \field Number of Data Periods
7948 : // N2, \field Number of Records per hour
7949 : // A1, \field Data Period 1 Name/Description
7950 : // A2, \field Data Period 1 Start Day of Week
7951 : // \type choice
7952 : // \key Sunday
7953 : // \key Monday
7954 : // \key Tuesday
7955 : // \key Wednesday
7956 : // \key Thursday
7957 : // \key Friday
7958 : // \key Saturday
7959 : // A3, \field Data Period 1 Start Day
7960 : // A4, \field Data Period 1 End Day
7961 :
7962 4975 : int NumHdArgs = 2;
7963 4975 : int CurCount = 0;
7964 14925 : for (int i = 1; i <= NumHdArgs; ++i) {
7965 9950 : strip(Line.data);
7966 9950 : std::string::size_type Pos = index(Line.data, ',');
7967 9950 : if (Pos == std::string::npos) {
7968 0 : if (len(Line.data) == 0) {
7969 0 : while (Pos == std::string::npos) {
7970 0 : Line = state.files.inputWeatherFile.readLine();
7971 0 : strip(Line.data);
7972 0 : uppercase(Line.data);
7973 0 : Pos = index(Line.data, ',');
7974 : }
7975 : } else {
7976 0 : Pos = len(Line.data);
7977 : }
7978 : }
7979 :
7980 9950 : if (i == 1) {
7981 : bool IOStatus;
7982 4975 : int const NumPeriods = Util::ProcessNumber(Line.data.substr(0, Pos), IOStatus);
7983 4975 : NumHdArgs += 4 * NumPeriods;
7984 4975 : } else if ((i >= 3)) {
7985 0 : if (mod(i - 3, 4) == 0) ++CurCount;
7986 : }
7987 9950 : Line.data.erase(0, Pos + 1);
7988 : }
7989 4975 : }
7990 :
7991 1226 : void ReportMissing_RangeData(EnergyPlusData &state)
7992 : {
7993 :
7994 : // SUBROUTINE INFORMATION:
7995 : // AUTHOR Linda Lawrie
7996 : // DATE WRITTEN January 2002
7997 :
7998 : // PURPOSE OF THIS SUBROUTINE:
7999 : // This subroutine reports the counts of missing/out of range data
8000 : // for weather file environments.
8001 :
8002 : static constexpr std::string_view MissString("Missing Data Found on Weather Data File");
8003 : static constexpr std::string_view msFmt("Missing {}, Number of items={:5}");
8004 : static constexpr std::string_view InvString("Invalid Data Found on Weather Data File");
8005 : static constexpr std::string_view ivFmt("Invalid {}, Number of items={:5}");
8006 : static constexpr std::string_view RangeString("Out of Range Data Found on Weather Data File");
8007 : static constexpr std::string_view rgFmt("Out of Range {} [{},{}], Number of items={:5}");
8008 :
8009 1226 : if (!state.dataEnvrn->DisplayWeatherMissingDataWarnings) return;
8010 :
8011 0 : bool MissedHeader = false;
8012 0 : auto missedHeaderCheck = [&](Real64 const value, std::string const &description) {
8013 0 : if (value > 0) {
8014 0 : if (!MissedHeader) {
8015 0 : ShowWarningError(state, std::string{MissString});
8016 0 : MissedHeader = true;
8017 : }
8018 0 : ShowMessage(state, format(msFmt, "\"" + description + "\"", value));
8019 : }
8020 0 : };
8021 :
8022 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDryBulbTemp, "Dry Bulb Temperature");
8023 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutBaroPress, "Atmospheric Pressure");
8024 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutRelHum, "Relative Humidity");
8025 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDewPointTemp, "Dew Point Temperatures");
8026 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindSpeed, "Wind Speed");
8027 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindDir, "Wind Direction");
8028 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.BeamSolarRad, "Direct Radiation");
8029 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.DifSolarRad, "Diffuse Radiation");
8030 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.TotalSkyCover, "Total Sky Cover");
8031 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OpaqueSkyCover, "Opaque Sky Cover");
8032 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.SnowDepth, "Snow Depth");
8033 0 : if (state.dataWeather->wvarsMissedCounts.WeathCodes > 0) {
8034 0 : ShowWarningError(state, std::string{InvString});
8035 0 : ShowMessage(state, format(ivFmt, "\"Weather Codes\" (not equal 9 digits)", state.dataWeather->wvarsMissedCounts.WeathCodes));
8036 : }
8037 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.LiquidPrecip, "Liquid Precipitation Depth");
8038 :
8039 0 : bool OutOfRangeHeader = false;
8040 : auto outOfRangeHeaderCheck = // (AUTO_OK_LAMBDA)
8041 0 : [&](Real64 const value, std::string_view description, std::string_view rangeLow, std::string_view rangeHigh, std::string_view extraMsg) {
8042 0 : if (value > 0) {
8043 0 : if (!OutOfRangeHeader) {
8044 0 : ShowWarningError(state, std::string{RangeString});
8045 0 : OutOfRangeHeader = true;
8046 : }
8047 0 : ShowMessage(state, EnergyPlus::format(rgFmt, description, rangeLow, rangeHigh, value));
8048 0 : if (!extraMsg.empty()) ShowMessage(state, std::string{extraMsg});
8049 : }
8050 0 : };
8051 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp, "Dry Bulb Temperatures", ">=-90", "<=70", "");
8052 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress,
8053 : "Atmospheric Pressure",
8054 : ">31000",
8055 : "<=120000",
8056 : "Out of Range values set to last good value");
8057 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutRelHum, "Relative Humidity", ">=0", "<=110", "");
8058 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp, "Dew Point Temperatures", ">=-90", "<=70", "");
8059 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindSpeed, "Wind Speed", ">=0", "<=40", "");
8060 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindDir, "Wind Direction", ">=0", "<=360", "");
8061 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad, "Direct Radiation", ">=0", "NoLimit", "");
8062 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad, "Diffuse Radiation", ">=0", "NoLimit", "");
8063 : }
8064 :
8065 796 : void SetupInterpolationValues(EnergyPlusData &state)
8066 : {
8067 :
8068 : // SUBROUTINE INFORMATION:
8069 : // AUTHOR Linda Lawrie
8070 : // DATE WRITTEN November 2002
8071 :
8072 : // PURPOSE OF THIS SUBROUTINE:
8073 : // This subroutine creates the "interpolation" values / weights that are used for
8074 : // interpolating weather data from hourly down to the time step level.
8075 :
8076 : // METHODOLOGY EMPLOYED:
8077 : // Create arrays (InterpolationValues, SolarInterpolationValues) dependent on
8078 : // Number of Time Steps in Hour. This will be used in the "SetCurrentWeather" procedure.
8079 :
8080 796 : int halfpoint = 0;
8081 :
8082 796 : state.dataWeather->Interpolation.allocate(state.dataGlobal->NumOfTimeStepInHour);
8083 796 : state.dataWeather->SolarInterpolation.allocate(state.dataGlobal->NumOfTimeStepInHour);
8084 796 : state.dataWeather->Interpolation = 0.0;
8085 796 : state.dataWeather->SolarInterpolation = 0.0;
8086 :
8087 5369 : for (int tloop = 1; tloop <= state.dataGlobal->NumOfTimeStepInHour; ++tloop) {
8088 4573 : state.dataWeather->Interpolation(tloop) =
8089 4573 : (state.dataGlobal->NumOfTimeStepInHour == 1) ? 1.0 : min(1.0, (double(tloop) / double(state.dataGlobal->NumOfTimeStepInHour)));
8090 : }
8091 :
8092 796 : if (mod(state.dataGlobal->NumOfTimeStepInHour, 2) == 0) {
8093 : // even number of time steps.
8094 783 : halfpoint = state.dataGlobal->NumOfTimeStepInHour / 2;
8095 783 : state.dataWeather->SolarInterpolation(halfpoint) = 1.0;
8096 783 : Real64 tweight = 1.0 / double(state.dataGlobal->NumOfTimeStepInHour);
8097 3063 : for (int tloop = halfpoint + 1, hpoint = 1; tloop <= state.dataGlobal->NumOfTimeStepInHour; ++tloop, ++hpoint) {
8098 2280 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8099 : }
8100 2280 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8101 1497 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8102 : }
8103 : } else { // odd number of time steps
8104 13 : if (state.dataGlobal->NumOfTimeStepInHour == 1) {
8105 13 : state.dataWeather->SolarInterpolation(1) = 0.5;
8106 0 : } else if (state.dataGlobal->NumOfTimeStepInHour == 3) {
8107 0 : state.dataWeather->SolarInterpolation(1) = 5.0 / 6.0;
8108 0 : state.dataWeather->SolarInterpolation(2) = 5.0 / 6.0;
8109 0 : state.dataWeather->SolarInterpolation(3) = 0.5;
8110 : } else {
8111 0 : Real64 tweight = 1.0 / double(state.dataGlobal->NumOfTimeStepInHour);
8112 0 : halfpoint = state.dataGlobal->NumOfTimeStepInHour / 2;
8113 0 : Real64 tweight1 = 1.0 - tweight / 2.0;
8114 0 : state.dataWeather->SolarInterpolation(halfpoint) = tweight1;
8115 0 : state.dataWeather->SolarInterpolation(halfpoint + 1) = tweight1;
8116 0 : for (int tloop = halfpoint + 2, hpoint = 1; tloop <= state.dataGlobal->NumOfTimeStepInHour; ++tloop, ++hpoint) {
8117 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8118 : }
8119 0 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8120 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8121 : }
8122 : }
8123 : }
8124 796 : }
8125 :
8126 797 : void SetupEnvironmentTypes(EnergyPlusData &state)
8127 : {
8128 :
8129 : // SUBROUTINE INFORMATION:
8130 : // AUTHOR Linda Lawrie
8131 : // DATE WRITTEN October 2010
8132 :
8133 : // PURPOSE OF THIS SUBROUTINE:
8134 : // Make sure Environment derived type is set prior to getting
8135 : // Weather Properties
8136 :
8137 : // Transfer weather file information to the Environment derived type
8138 797 : state.dataWeather->Envrn = state.dataEnvrn->TotDesDays + 1;
8139 :
8140 : // Sizing Periods from Weather File
8141 802 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunDesPers; ++iRunPer, ++state.dataWeather->Envrn) {
8142 5 : auto const &runPer = state.dataWeather->RunPeriodDesignInput(iRunPer);
8143 5 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8144 :
8145 5 : envCurr.StartMonth = runPer.startMonth;
8146 5 : envCurr.StartDay = runPer.startDay;
8147 5 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, state.dataWeather->LeapYearAdd);
8148 5 : envCurr.TotalDays = runPer.totalDays;
8149 5 : envCurr.EndMonth = runPer.endMonth;
8150 5 : envCurr.EndDay = runPer.endDay;
8151 5 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, state.dataWeather->LeapYearAdd);
8152 5 : envCurr.NumSimYears = runPer.numSimYears;
8153 5 : if (envCurr.StartJDay <= envCurr.EndJDay) {
8154 5 : envCurr.TotalDays = (envCurr.EndJDay - envCurr.StartJDay + 1) * envCurr.NumSimYears;
8155 : } else {
8156 0 : envCurr.TotalDays =
8157 0 : (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - envCurr.StartJDay + 1 + envCurr.EndJDay) * envCurr.NumSimYears;
8158 : }
8159 5 : state.dataEnvrn->TotRunDesPersDays += envCurr.TotalDays;
8160 5 : envCurr.UseDST = runPer.useDST;
8161 5 : envCurr.UseHolidays = runPer.useHolidays;
8162 5 : envCurr.Title = runPer.title;
8163 5 : envCurr.cKindOfEnvrn = runPer.periodType;
8164 5 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
8165 5 : envCurr.DesignDayNum = 0;
8166 5 : envCurr.RunPeriodDesignNum = iRunPer;
8167 5 : envCurr.DayOfWeek = runPer.dayOfWeek;
8168 5 : envCurr.MonWeekDay = runPer.monWeekDay;
8169 5 : envCurr.SetWeekDays = false;
8170 5 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8171 5 : envCurr.UseRain = runPer.useRain;
8172 5 : envCurr.UseSnow = runPer.useSnow;
8173 5 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // this will just the default
8174 : }
8175 :
8176 : // RunPeriods from weather file
8177 1984 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunPers; ++iRunPer, ++state.dataWeather->Envrn) {
8178 1187 : auto const &runPer = state.dataWeather->RunPeriodInput(iRunPer);
8179 1187 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8180 :
8181 1187 : envCurr.StartMonth = runPer.startMonth;
8182 1187 : envCurr.StartDay = runPer.startDay;
8183 1187 : envCurr.StartYear = runPer.startYear;
8184 1187 : envCurr.EndMonth = runPer.endMonth;
8185 1187 : envCurr.EndDay = runPer.endDay;
8186 1187 : envCurr.EndYear = runPer.endYear;
8187 1187 : envCurr.NumSimYears = runPer.numSimYears;
8188 1187 : envCurr.CurrentYear = runPer.startYear;
8189 1187 : envCurr.IsLeapYear = runPer.isLeapYear;
8190 1187 : envCurr.TreatYearsAsConsecutive = true;
8191 1187 : if (runPer.actualWeather) {
8192 : // This will require leap years to be present, thus Julian days can be used for all the calculations
8193 0 : envCurr.StartJDay = envCurr.StartDate = runPer.startJulianDate;
8194 0 : envCurr.EndJDay = envCurr.EndDate = runPer.endJulianDate;
8195 0 : envCurr.TotalDays = envCurr.EndDate - envCurr.StartDate + 1;
8196 0 : envCurr.RawSimDays = envCurr.EndDate - envCurr.StartDate + 1;
8197 0 : envCurr.MatchYear = true;
8198 0 : envCurr.ActualWeather = true;
8199 : } else { // std RunPeriod
8200 1187 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8201 1187 : if (envCurr.StartYear == envCurr.EndYear) {
8202 : // Short-circuit all the calculations, we're in a single year
8203 :
8204 1181 : envCurr.IsLeapYear = isLeapYear(envCurr.StartYear) && state.dataWeather->WFAllowsLeapYears;
8205 1181 : int LocalLeapYearAdd = (int)envCurr.IsLeapYear;
8206 :
8207 1181 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, LocalLeapYearAdd);
8208 1181 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, LocalLeapYearAdd);
8209 1181 : envCurr.RawSimDays = (envCurr.EndJDay - envCurr.StartJDay + 1);
8210 1181 : envCurr.TotalDays = envCurr.RawSimDays;
8211 : } else {
8212 : // Environment crosses year boundaries
8213 6 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8214 6 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, (int)runPer.isLeapYear);
8215 12 : envCurr.EndJDay = General::OrdinalDay(
8216 6 : runPer.endMonth, runPer.endDay, (int)(isLeapYear(runPer.endYear) && state.dataWeather->WFAllowsLeapYears));
8217 6 : envCurr.TotalDays = 366 - envCurr.StartJDay + envCurr.EndJDay + 365 * std::max(envCurr.NumSimYears - 2, 0);
8218 6 : if (state.dataWeather->WFAllowsLeapYears) {
8219 : // First year
8220 0 : if (envCurr.StartJDay < 59) {
8221 0 : if (isLeapYear(envCurr.StartYear)) {
8222 0 : ++envCurr.TotalDays;
8223 : }
8224 : }
8225 : // Middle years
8226 0 : for (int yr = envCurr.StartYear + 1; yr < envCurr.EndYear; ++yr) {
8227 0 : if (isLeapYear(yr)) {
8228 0 : ++envCurr.TotalDays;
8229 : }
8230 : }
8231 : // Last year not needed, the end ordinal date will take this into account
8232 : }
8233 6 : envCurr.RawSimDays = envCurr.TotalDays;
8234 : }
8235 : }
8236 1187 : envCurr.UseDST = runPer.useDST;
8237 1187 : envCurr.UseHolidays = runPer.useHolidays;
8238 1187 : if (runPer.title.empty()) {
8239 1 : envCurr.Title = state.dataEnvrn->WeatherFileLocationTitle;
8240 : } else {
8241 1186 : envCurr.Title = runPer.title;
8242 : }
8243 1187 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::ReadAllWeatherData) {
8244 1 : envCurr.cKindOfEnvrn = "ReadAllWeatherDataRunPeriod";
8245 : } else {
8246 1186 : envCurr.cKindOfEnvrn = "WeatherFileRunPeriod";
8247 1186 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
8248 : }
8249 1187 : envCurr.DayOfWeek = runPer.dayOfWeek;
8250 1187 : envCurr.MonWeekDay = runPer.monWeekDay;
8251 1187 : envCurr.SetWeekDays = false;
8252 1187 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8253 1187 : envCurr.UseRain = runPer.useRain;
8254 1187 : envCurr.UseSnow = runPer.useSnow;
8255 1187 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // first hour interpolation choice
8256 : } // for (i)
8257 797 : }
8258 :
8259 2372 : bool isLeapYear(int const Year)
8260 : {
8261 : // true if it's a leap year, false if not.
8262 :
8263 2372 : if (mod(Year, 4) == 0) { // Potential Leap Year
8264 2 : if (!(mod(Year, 100) == 0 && mod(Year, 400) != 0)) {
8265 2 : return true;
8266 : }
8267 : }
8268 2370 : return false;
8269 : }
8270 :
8271 9305 : int computeJulianDate(int const gyyyy, // input/output gregorian year, should be specified as 4 digits
8272 : int const gmm, // input/output gregorian month
8273 : int const gdd // input/output gregorian day
8274 : )
8275 : {
8276 : // SUBROUTINE INFORMATION:
8277 : // AUTHOR Jason DeGraw
8278 : // DATE WRITTEN 10/25/2017
8279 :
8280 : // PURPOSE OF THIS SUBROUTINE:
8281 : // Split the former JGDate function in two. Convert a gregorian
8282 : // date to actual julian date. the advantage of storing a julian date
8283 : // in the jdate format rather than a 5 digit format is that any
8284 : // number of days can be add or subtracted to jdate and
8285 : // that result is a proper julian date.
8286 :
8287 : // REFERENCES:
8288 : // for discussion of this algorithm,
8289 : // see cacm, vol 11, no 10, oct 1968, page 657
8290 :
8291 9305 : int tyyyy = gyyyy;
8292 9305 : int tmm = gmm;
8293 9305 : int tdd = gdd;
8294 9305 : int l = (tmm - 14) / 12;
8295 9305 : return tdd - 32075 + 1461 * (tyyyy + 4800 + l) / 4 + 367 * (tmm - 2 - l * 12) / 12 - 3 * ((tyyyy + 4900 + l) / 100) / 4;
8296 : }
8297 :
8298 0 : int computeJulianDate(GregorianDate const &gdate)
8299 : {
8300 0 : return computeJulianDate(gdate.year, gdate.month, gdate.day);
8301 : }
8302 :
8303 0 : GregorianDate computeGregorianDate(int const jdate)
8304 : {
8305 0 : int tdate = jdate;
8306 0 : int l = tdate + 68569;
8307 0 : int n = 4 * l / 146097;
8308 0 : l -= (146097 * n + 3) / 4;
8309 0 : int tyyyy = 4000 * (l + 1) / 1461001;
8310 0 : l = l - 1461 * tyyyy / 4 + 31;
8311 0 : int tmm = 80 * l / 2447;
8312 0 : int tdd = l - 2447 * tmm / 80;
8313 0 : l = tmm / 11;
8314 0 : tmm += 2 - 12 * l;
8315 0 : tyyyy += 100 * (n - 49) + l;
8316 0 : return {tyyyy, tmm, tdd};
8317 : }
8318 :
8319 152 : ScheduleManager::DayType calculateDayOfWeek(EnergyPlusData &state, int const year, int const month, int const day)
8320 : {
8321 :
8322 : // FUNCTION INFORMATION:
8323 : // AUTHOR Linda Lawrie
8324 : // DATE WRITTEN March 2012
8325 : // MODIFIED October 2017, Jason DeGraw
8326 :
8327 : // PURPOSE OF THIS FUNCTION:
8328 : // Calculate the correct day of week.
8329 :
8330 : // METHODOLOGY EMPLOYED:
8331 : // Zeller's algorithm.
8332 :
8333 : // REFERENCES:
8334 : // http://en.wikipedia.org/wiki/Zeller%27s_congruence
8335 : // and other references around the web.
8336 :
8337 152 : int Gyyyy(year); // Gregorian yyyy
8338 152 : int Gmm(month); // Gregorian mm
8339 :
8340 : // Jan, Feb are 13, 14 months of previous year
8341 152 : if (Gmm < 3) {
8342 89 : Gmm += 12;
8343 89 : --Gyyyy;
8344 : }
8345 :
8346 152 : state.dataEnvrn->DayOfWeek = mod(day + (13 * (Gmm + 1) / 5) + Gyyyy + (Gyyyy / 4) + 6 * (Gyyyy / 100) + (Gyyyy / 400), 7);
8347 152 : if (state.dataEnvrn->DayOfWeek == 0) state.dataEnvrn->DayOfWeek = 7;
8348 :
8349 152 : return static_cast<ScheduleManager::DayType>(state.dataEnvrn->DayOfWeek);
8350 : }
8351 :
8352 1033 : int calculateDayOfYear(int const Month, int const Day, bool const leapYear)
8353 : {
8354 :
8355 : // FUNCTION INFORMATION:
8356 : // AUTHOR Jason DeGraw
8357 : // DATE WRITTEN October 10, 2017
8358 :
8359 : // PURPOSE OF THIS FUNCTION:
8360 : // Compute the day of the year for leap and non-leap years.
8361 :
8362 : static std::array<int, 12> const daysbefore{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}};
8363 : static std::array<int, 12> const daysbeforeleap{{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
8364 :
8365 : // Could probably do some bounds checking here, but for now assume the month is in [1, 12]
8366 1033 : if (leapYear) {
8367 0 : return daysbeforeleap[Month - 1] + Day;
8368 : } else {
8369 1033 : return daysbefore[Month - 1] + Day;
8370 : }
8371 : }
8372 :
8373 2370 : bool validMonthDay(int const month, int const day, int const leapYearAdd)
8374 : {
8375 :
8376 : // FUNCTION INFORMATION:
8377 : // AUTHOR Jason DeGraw
8378 : // DATE WRITTEN October 31, 2017
8379 :
8380 : // PURPOSE OF THIS FUNCTION:
8381 : // Determine if a month/day+leapyear combination is valid.
8382 :
8383 2370 : switch (month) {
8384 2210 : case 1:
8385 : case 3:
8386 : case 5:
8387 : case 7:
8388 : case 8:
8389 : case 10:
8390 : case 12:
8391 2210 : if (day > 31) {
8392 0 : return false;
8393 : }
8394 2210 : break;
8395 119 : case 4:
8396 : case 6:
8397 : case 9:
8398 : case 11:
8399 119 : if (day > 30) {
8400 0 : return false;
8401 : }
8402 119 : break;
8403 41 : case 2:
8404 41 : if (day > 28 + leapYearAdd) {
8405 0 : return false;
8406 : }
8407 41 : break;
8408 0 : default:
8409 0 : return false;
8410 : }
8411 2370 : return true;
8412 : }
8413 :
8414 0 : void AnnualMonthlyDryBulbWeatherData::CalcAnnualAndMonthlyDryBulbTemp(EnergyPlusData &state)
8415 : {
8416 :
8417 : // PURPOSE OF THIS SUBROUTINE:
8418 : // Calculates monthly daily average outdoor air drybulb temperature from
8419 : // either weather (*.EPW) file or reads monthly daily average outdoor air
8420 : // drybulb temperature from STAT (*.stat) for use to autosize main water
8421 : // temperature.
8422 :
8423 0 : Real64 MonthlyDailyDryBulbMin(200.0); // monthly-daily minimum outside air dry-bulb temperature
8424 0 : Real64 MonthlyDailyDryBulbMax(-200.0); // monthly-daily maximum outside air dry-bulb temperature
8425 0 : Real64 AnnualDailyAverageDryBulbTempSum(0.0); // annual sum of daily average outside air dry-bulb temperature
8426 0 : Array1D<Real64> MonthlyAverageDryBulbTemp(12, 0.0); // monthly-daily average outside air temperature
8427 :
8428 0 : if (!this->OADryBulbWeatherDataProcessed) {
8429 0 : const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
8430 0 : const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
8431 0 : if (statFileExists) {
8432 0 : auto statFile = state.files.inStatFilePath.try_open();
8433 0 : if (!statFile.good()) {
8434 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", statFile.filePath));
8435 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8436 0 : return;
8437 : }
8438 :
8439 0 : std::string lineAvg;
8440 0 : while (statFile.good()) {
8441 0 : auto lineIn = statFile.readLine();
8442 0 : if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
8443 0 : for (int i = 1; i <= 7; ++i) {
8444 0 : lineIn = statFile.readLine();
8445 : }
8446 0 : lineIn = statFile.readLine();
8447 0 : lineAvg = lineIn.data;
8448 0 : break;
8449 : }
8450 0 : }
8451 0 : if (lineAvg.empty()) {
8452 0 : ShowSevereError(
8453 : state,
8454 0 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have Monthly Statistics for Dry Bulb temperatures.",
8455 : statFile.filePath));
8456 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8457 0 : return;
8458 0 : } else if (lineAvg.find("Daily Avg") == std::string::npos) {
8459 0 : ShowSevereError(state,
8460 0 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have the 'Daily Avg' line in the Monthly "
8461 : "Statistics for Dry Bulb temperatures.",
8462 : statFile.filePath));
8463 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8464 0 : return;
8465 : } else {
8466 0 : int AnnualNumberOfDays = 0;
8467 0 : for (int i = 1; i <= 12; ++i) {
8468 0 : MonthlyAverageDryBulbTemp(i) = OutputReportTabular::StrToReal(OutputReportTabular::GetColumnUsingTabs(lineAvg, i + 2));
8469 0 : AnnualDailyAverageDryBulbTempSum += MonthlyAverageDryBulbTemp(i) * state.dataWeather->EndDayOfMonth(i);
8470 0 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyAverageDryBulbTemp(i));
8471 0 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyAverageDryBulbTemp(i));
8472 0 : AnnualNumberOfDays += state.dataWeather->EndDayOfMonth(i);
8473 : }
8474 0 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8475 0 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8476 0 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8477 0 : this->OADryBulbWeatherDataProcessed = true;
8478 : }
8479 0 : } else if (epwFileExists) {
8480 0 : auto epwFile = state.files.inputWeatherFilePath.try_open();
8481 0 : bool epwHasLeapYear(false);
8482 0 : if (!epwFile.good()) {
8483 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", epwFile.filePath));
8484 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8485 0 : return;
8486 : }
8487 0 : for (int i = 1; i <= 8; ++i) { // Headers
8488 0 : auto epwLine = epwFile.readLine();
8489 :
8490 0 : if (i == 5) {
8491 : // HOLIDAYS/DAYLIGHT SAVINGS,Yes,0,0,0
8492 0 : std::string::size_type pos = index(epwLine.data, ',');
8493 0 : epwLine.data.erase(0, pos + 1);
8494 0 : pos = index(epwLine.data, ',');
8495 0 : std::string LeapYear = Util::makeUPPER(epwLine.data.substr(0, pos));
8496 0 : if (LeapYear[0] == 'Y') {
8497 0 : epwHasLeapYear = true;
8498 : }
8499 0 : }
8500 0 : }
8501 0 : Array1D<int> EndDayOfMonthLocal;
8502 0 : EndDayOfMonthLocal = state.dataWeather->EndDayOfMonth;
8503 0 : if (epwHasLeapYear) {
8504 : // increase number of days for february by one day if weather data has leap year
8505 0 : EndDayOfMonthLocal(2) = EndDayOfMonthLocal(2) + 1;
8506 : }
8507 : int DayNum;
8508 : int DaysCountOfMonth;
8509 0 : for (int i = 1; i <= 12; ++i) {
8510 0 : Real64 MonthlyDailyDryBulbAvg = 0.0;
8511 0 : DaysCountOfMonth = EndDayOfMonthLocal(i);
8512 0 : for (DayNum = 1; DayNum <= DaysCountOfMonth; ++DayNum) {
8513 0 : Real64 DailyAverageDryBulbTemp = 0.0;
8514 : std::string::size_type pos;
8515 0 : for (int j = 1; j <= 24; ++j) {
8516 0 : auto epwLine = epwFile.readLine();
8517 0 : for (int ind = 1; ind <= 6; ++ind) {
8518 0 : pos = index(epwLine.data, ',');
8519 0 : epwLine.data.erase(0, pos + 1);
8520 : }
8521 0 : pos = index(epwLine.data, ',');
8522 0 : Real64 HourlyDryBulbTemp = OutputReportTabular::StrToReal(epwLine.data.substr(0, pos));
8523 0 : DailyAverageDryBulbTemp += (HourlyDryBulbTemp / 24.0);
8524 0 : }
8525 0 : AnnualDailyAverageDryBulbTempSum += DailyAverageDryBulbTemp;
8526 0 : MonthlyDailyDryBulbAvg += (DailyAverageDryBulbTemp / DaysCountOfMonth);
8527 : }
8528 0 : MonthlyAverageDryBulbTemp(i) = MonthlyDailyDryBulbAvg;
8529 0 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyDailyDryBulbAvg);
8530 0 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyDailyDryBulbAvg);
8531 : }
8532 : // calculate annual average outdoor air dry-bulb temperature and monthly daily average
8533 : // outdoor air temperature maximum difference
8534 0 : int AnnualNumberOfDays = 365;
8535 0 : if (epwHasLeapYear) AnnualNumberOfDays++;
8536 0 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8537 0 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8538 0 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8539 0 : this->OADryBulbWeatherDataProcessed = true;
8540 0 : } else {
8541 0 : ShowSevereError(state, "CalcAnnualAndMonthlyDryBulbTemp: weather file or stat file does not exist.");
8542 0 : ShowContinueError(state, format("Weather file: {}.", state.files.inputWeatherFilePath.filePath));
8543 0 : ShowContinueError(state, format("Stat file: {}.", state.files.inStatFilePath.filePath));
8544 0 : ShowContinueError(state, "Water Mains Monthly Temperature cannot be calculated using CorrelationFromWeatherFile method.");
8545 0 : ShowContinueError(state, "Instead a fixed default value of 10.0 C will be used.");
8546 : }
8547 : }
8548 0 : }
8549 :
8550 796 : void ReportWaterMainsTempParameters(EnergyPlusData &state)
8551 : {
8552 : // PURPOSE OF THIS SUBROUTINE:
8553 : // report site water mains temperature object user inputs and/or parameters calculated
8554 : // from weather or stat file
8555 :
8556 796 : if (!state.files.eio.good()) {
8557 0 : return;
8558 : }
8559 :
8560 796 : std::stringstream ss;
8561 796 : auto *eiostream = &ss;
8562 :
8563 : // Write annual average OA temperature and maximum difference in monthly-daily average outdoor air temperature
8564 : *eiostream << "! <Site Water Mains Temperature Information>"
8565 : ",Calculation Method{}"
8566 : ",Water Mains Temperature Schedule Name{}"
8567 : ",Annual Average Outdoor Air Temperature{C}"
8568 : ",Maximum Difference In Monthly Average Outdoor Air Temperatures{deltaC}"
8569 796 : ",Fixed Default Water Mains Temperature{C}\n";
8570 :
8571 796 : switch (state.dataWeather->WaterMainsTempsMethod) {
8572 3 : case WaterMainsTempCalcMethod::Schedule:
8573 3 : *eiostream << "Site Water Mains Temperature Information,";
8574 3 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8575 6 : << state.dataWeather->WaterMainsTempsScheduleName << ",";
8576 3 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8577 6 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8578 3 : *eiostream << "NA\n";
8579 3 : break;
8580 126 : case WaterMainsTempCalcMethod::Correlation:
8581 126 : *eiostream << "Site Water Mains Temperature Information,";
8582 126 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8583 : << "NA"
8584 252 : << ",";
8585 126 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8586 252 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8587 126 : *eiostream << "NA\n";
8588 126 : break;
8589 0 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
8590 0 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
8591 0 : *eiostream << "Site Water Mains Temperature Information,";
8592 0 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8593 : << "NA"
8594 0 : << ",";
8595 0 : *eiostream << format("{:.2R}", state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp) << ","
8596 0 : << format("{:.2R}", state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff) << ","
8597 0 : << "NA\n";
8598 : } else {
8599 0 : *eiostream << "Site Water Mains Temperature Information,";
8600 : *eiostream << "FixedDefault"
8601 : << ","
8602 : << "NA"
8603 : << ","
8604 : << "NA"
8605 : << ","
8606 : << "NA"
8607 0 : << "," << format("{:.1R}", 10.0) << '\n';
8608 : }
8609 0 : break;
8610 667 : default:
8611 667 : *eiostream << "Site Water Mains Temperature Information,";
8612 : *eiostream << "FixedDefault"
8613 : << ","
8614 : << "NA"
8615 : << ","
8616 : << "NA"
8617 : << ","
8618 : << "NA"
8619 667 : << "," << format("{:.1R}", 10.0) << '\n';
8620 667 : break;
8621 : }
8622 :
8623 796 : print(state.files.eio, "{}", ss.str());
8624 796 : }
8625 :
8626 509256 : void calcSky(EnergyPlusData &state,
8627 : Real64 &HorizIRSky,
8628 : Real64 &SkyTemp,
8629 : Real64 OpaqueSkyCover,
8630 : Real64 DryBulb,
8631 : Real64 DewPoint,
8632 : Real64 RelHum,
8633 : Real64 IRHoriz)
8634 : {
8635 509256 : if (IRHoriz <= 0.0) IRHoriz = 9999.0;
8636 :
8637 509256 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8638 509256 : if (!envCurr.UseWeatherFileHorizontalIR || IRHoriz >= 9999.0) {
8639 : // Missing or user defined to not use IRHoriz from weather, using sky cover and clear sky emissivity
8640 0 : Real64 ESky = CalcSkyEmissivity(state, envCurr.skyTempModel, OpaqueSkyCover, DryBulb, DewPoint, RelHum);
8641 0 : HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
8642 0 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8643 0 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8644 0 : SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
8645 : } else {
8646 0 : SkyTemp = 0.0; // dealt with later
8647 : }
8648 0 : } else {
8649 : // Valid IR from weather files
8650 509256 : HorizIRSky = IRHoriz;
8651 509256 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8652 509256 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8653 509256 : SkyTemp = root_4(IRHoriz / Constant::StefanBoltzmann) - Constant::Kelvin;
8654 : } else {
8655 0 : SkyTemp = 0.0; // dealt with later
8656 : }
8657 : }
8658 509256 : }
8659 :
8660 17804 : void ForAllHrTs(EnergyPlusData &state, std::function<void(int, int)> f)
8661 : {
8662 445100 : for (int iHr = 1; iHr <= Constant::HoursInDay; ++iHr)
8663 2764464 : for (int iTS = 1; iTS <= state.dataGlobal->NumOfTimeStepInHour; ++iTS)
8664 2337168 : f(iHr, iTS);
8665 17804 : }
8666 :
8667 : } // namespace Weather
8668 :
8669 : } // namespace EnergyPlus
|