Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <array>
50 : #include <cmath>
51 : #include <cstdio>
52 : #include <map>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array.functions.hh>
56 : #include <ObjexxFCL/ArrayS.functions.hh>
57 : #include <ObjexxFCL/string.functions.hh>
58 : #include <ObjexxFCL/time.hh>
59 :
60 : // EnergyPlus Headers
61 : #include <EnergyPlus/Data/EnergyPlusData.hh>
62 : #include <EnergyPlus/DataEnvironment.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataPrecisionGlobals.hh>
66 : #include <EnergyPlus/DataReportingFlags.hh>
67 : #include <EnergyPlus/DataSurfaces.hh>
68 : #include <EnergyPlus/DataSystemVariables.hh>
69 : #include <EnergyPlus/DataWater.hh>
70 : #include <EnergyPlus/DisplayRoutines.hh>
71 : #include <EnergyPlus/EMSManager.hh>
72 : #include <EnergyPlus/FileSystem.hh>
73 : #include <EnergyPlus/General.hh>
74 : #include <EnergyPlus/GlobalNames.hh>
75 : #include <EnergyPlus/GroundTemperatureModeling/BaseGroundTemperatureModel.hh>
76 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
77 : #include <EnergyPlus/OutputProcessor.hh>
78 : #include <EnergyPlus/OutputReportPredefined.hh>
79 : #include <EnergyPlus/OutputReportTabular.hh>
80 : #include <EnergyPlus/Psychrometrics.hh>
81 : #include <EnergyPlus/ScheduleManager.hh>
82 : #include <EnergyPlus/StringUtilities.hh>
83 : #include <EnergyPlus/SurfaceGeometry.hh>
84 : #include <EnergyPlus/ThermalComfort.hh>
85 : #include <EnergyPlus/UtilityRoutines.hh>
86 : #include <EnergyPlus/Vectors.hh>
87 : #include <EnergyPlus/WaterManager.hh>
88 : #include <EnergyPlus/WeatherManager.hh>
89 :
90 : namespace EnergyPlus {
91 :
92 : namespace Weather {
93 :
94 : // MODULE INFORMATION:
95 : // AUTHOR Rick Strand
96 : // DATE WRITTEN May 1997
97 : // MODIFIED December 1998, FW; December 1999, LKL.
98 :
99 : // PURPOSE OF THIS MODULE:
100 : // This module contains all of the weather handling routines for
101 : // EnergyPlus. That includes getting user input, defining design day
102 : // weather, retrieving data from weather files, and supplying the
103 : // outdoor environment for each time step.
104 :
105 : constexpr std::array<std::string_view, (int)EpwHeaderType::Num> epwHeaders = {"LOCATION",
106 : "DESIGN CONDITIONS",
107 : "TYPICAL/EXTREME PERIODS",
108 : "GROUND TEMPERATURES",
109 : "HOLIDAYS/DAYLIGHT SAVING",
110 : "COMMENTS 1",
111 : "COMMENTS 2",
112 : "DATA PERIODS"};
113 :
114 : static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNames{
115 : "Schedule", "Correlation", "CorrelationFromWeatherFile", "FixedDefault"};
116 :
117 : static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNamesUC{
118 : "SCHEDULE", "CORRELATION", "CORRELATIONFROMWEATHERFILE", "FIXEDDEFAULT"};
119 :
120 : static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNamesUC{
121 : "CLARKALLEN", "SCHEDULEVALUE", "DIFFERENCESCHEDULEDRYBULBVALUE", "DIFFERENCESCHEDULEDEWPOINTVALUE", "BRUNT", "IDSO", "BERDAHLMARTIN"};
122 :
123 : static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNames{"Clark and Allen",
124 : "Schedule Value",
125 : "DryBulb Difference Schedule Value",
126 : "Dewpoint Difference Schedule Value",
127 : "Brunt",
128 : "Idso",
129 : "Berdahl and Martin"};
130 :
131 2925340 : void ManageWeather(EnergyPlusData &state)
132 : {
133 :
134 : // SUBROUTINE INFORMATION:
135 : // AUTHOR Rick Strand
136 : // DATE WRITTEN May 1997
137 : // MODIFIED June 1997 (general clean-up)
138 :
139 : // PURPOSE OF THIS SUBROUTINE:
140 : // This subroutine is the main driver of the weather manager module.
141 : // It controls the assignment of weather related global variables as
142 : // well as the reads and writes for weather information.
143 :
144 2925340 : InitializeWeather(state, state.dataWeather->PrintEnvrnStamp);
145 :
146 : // Cannot call this during sizing, because EMS will not initialize properly until after simulation kickoff
147 2925340 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
148 1989001 : bool anyEMSRan = false;
149 1989001 : EMSManager::ManageEMS(state,
150 : EMSManager::EMSCallFrom::BeginZoneTimestepBeforeSetCurrentWeather,
151 : anyEMSRan,
152 3978002 : ObjexxFCL::Optional_int_const()); // calling point
153 : }
154 2925340 : SetCurrentWeather(state);
155 :
156 2925340 : ReportWeatherAndTimeInformation(state, state.dataWeather->PrintEnvrnStamp);
157 2925340 : }
158 :
159 2859 : void ResetEnvironmentCounter(EnergyPlusData &state)
160 : {
161 2859 : state.dataWeather->Envrn = 0;
162 2859 : }
163 :
164 799 : bool CheckIfAnyUnderwaterBoundaries(EnergyPlusData &state)
165 : {
166 799 : bool errorsFound = false;
167 799 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
168 :
169 799 : constexpr std::string_view routineName = "CheckIfAnyUnderwaterBoundaries";
170 :
171 799 : auto const &ipsc = state.dataIPShortCut;
172 799 : ipsc->cCurrentModuleObject = "SurfaceProperty:Underwater";
173 799 : int Num = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
174 800 : for (int i = 1; i <= Num; i++) {
175 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
176 1 : ipsc->cCurrentModuleObject,
177 : i,
178 1 : ipsc->cAlphaArgs,
179 : NumAlpha,
180 1 : ipsc->rNumericArgs,
181 : NumNumber,
182 : IOStat,
183 1 : ipsc->lNumericFieldBlanks,
184 1 : ipsc->lAlphaFieldBlanks,
185 1 : ipsc->cAlphaFieldNames,
186 1 : ipsc->cNumericFieldNames);
187 1 : state.dataWeather->underwaterBoundaries.emplace_back();
188 1 : auto &underwaterBoundary = state.dataWeather->underwaterBoundaries[i - 1];
189 1 : underwaterBoundary.Name = ipsc->cAlphaArgs(1);
190 :
191 1 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, underwaterBoundary.Name};
192 :
193 1 : underwaterBoundary.distanceFromLeadingEdge = ipsc->rNumericArgs(1);
194 1 : underwaterBoundary.OSCMIndex = Util::FindItemInList(underwaterBoundary.Name, state.dataSurface->OSCM);
195 1 : if (underwaterBoundary.OSCMIndex <= 0) {
196 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
197 0 : errorsFound = true;
198 : }
199 :
200 1 : if (ipsc->lAlphaFieldBlanks(2)) {
201 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
202 0 : errorsFound = true;
203 1 : } else if ((underwaterBoundary.waterTempSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
204 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
205 0 : errorsFound = true;
206 : }
207 :
208 1 : if (ipsc->lAlphaFieldBlanks(3)) {
209 : // that's OK, we can have a blank schedule, the water will just have no free stream velocity
210 1 : } else if ((underwaterBoundary.velocitySched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
211 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
212 0 : errorsFound = true;
213 : }
214 1 : if (errorsFound) {
215 0 : break;
216 : }
217 : }
218 799 : if (errorsFound) {
219 0 : ShowFatalError(state, "Previous input problems cause program termination");
220 : }
221 799 : return (Num > 0);
222 : }
223 :
224 : Real64
225 1343 : calculateWaterBoundaryConvectionCoefficient(Real64 const curWaterTemp, Real64 const freeStreamVelocity, Real64 const distanceFromLeadingEdge)
226 : {
227 1343 : Real64 constexpr waterKinematicViscosity = 1e-6; // m2/s
228 1343 : Real64 constexpr waterPrandtlNumber = 6; // -
229 1343 : Real64 constexpr waterThermalConductivity = 0.6; // W/mK
230 : // do some calculation for forced convection from the leading edge of the ship
231 1343 : Real64 const localReynoldsNumber = freeStreamVelocity * distanceFromLeadingEdge / waterKinematicViscosity;
232 1343 : Real64 const localNusseltNumber = 0.0296 * pow(localReynoldsNumber, 0.8) * pow(waterPrandtlNumber, 1.0 / 3.0);
233 1343 : Real64 const localConvectionCoeff = localNusseltNumber * waterThermalConductivity / distanceFromLeadingEdge;
234 :
235 : // do some calculations for natural convection from the bottom of the ship
236 1343 : Real64 constexpr distanceFromBottomOfHull = 12; // meters, assumed for now
237 : // this Prandtl correction is from Incropera & Dewitt, Intro to HT, eq 9.20
238 1343 : Real64 const prandtlCorrection =
239 : (0.75 * pow(waterPrandtlNumber, 0.5)) / pow(0.609 + 1.221 * pow(waterPrandtlNumber, 0.5) + 1.238 * waterPrandtlNumber, 0.25);
240 : // calculate the Grashof number
241 1343 : Real64 constexpr gravity = 9.81; // m/s2
242 1343 : Real64 constexpr beta = 0.000214; // water thermal expansion coefficient, from engineeringtoolbox.com, 1/C
243 1343 : Real64 constexpr assumedSurfaceTemp = 25; // Grashof requires a surface temp, this should suffice
244 : Real64 const localGrashofNumber =
245 1343 : (gravity * beta * std::abs(assumedSurfaceTemp - curWaterTemp) * pow(distanceFromBottomOfHull, 3)) / pow(waterKinematicViscosity, 2);
246 1343 : Real64 const localNusseltFreeConvection = pow(localGrashofNumber / 4, 0.25) * prandtlCorrection;
247 1343 : Real64 const localConvectionCoeffFreeConv = localNusseltFreeConvection * waterThermalConductivity / distanceFromBottomOfHull;
248 1343 : return max(localConvectionCoeff, localConvectionCoeffFreeConv);
249 : }
250 :
251 1343 : void UpdateUnderwaterBoundaries(EnergyPlusData &state)
252 : {
253 2686 : for (auto &thisBoundary : state.dataWeather->underwaterBoundaries) {
254 1343 : Real64 const curWaterTemp = thisBoundary.waterTempSched->getCurrentVal(); // C
255 1343 : Real64 freeStreamVelocity = 0;
256 1343 : if (thisBoundary.velocitySched != nullptr) {
257 1343 : freeStreamVelocity = thisBoundary.velocitySched->getCurrentVal(); // m/s
258 : }
259 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TConv = curWaterTemp;
260 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HConv =
261 1343 : Weather::calculateWaterBoundaryConvectionCoefficient(curWaterTemp, freeStreamVelocity, thisBoundary.distanceFromLeadingEdge);
262 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TRad = curWaterTemp;
263 1343 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HRad = 0.0;
264 1343 : }
265 1343 : }
266 :
267 800 : void ReadVariableLocationOrientation(EnergyPlusData &state)
268 : {
269 : static constexpr std::string_view routineName = "ReadVariableLocationOrientation";
270 :
271 800 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
272 800 : auto const &ipsc = state.dataIPShortCut;
273 :
274 800 : ipsc->cCurrentModuleObject = "Site:VariableLocation";
275 800 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject) == 0) {
276 799 : return;
277 : }
278 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
279 1 : ipsc->cCurrentModuleObject,
280 : 1,
281 1 : ipsc->cAlphaArgs,
282 : NumAlpha,
283 1 : ipsc->rNumericArgs,
284 : NumNumber,
285 : IOStat,
286 1 : ipsc->lNumericFieldBlanks,
287 1 : ipsc->lAlphaFieldBlanks,
288 1 : ipsc->cAlphaFieldNames,
289 1 : ipsc->cNumericFieldNames);
290 :
291 1 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
292 :
293 1 : if (ipsc->lAlphaFieldBlanks(1)) {
294 1 : } else if ((state.dataEnvrn->varyingLocationLatSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(1))) == nullptr) {
295 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
296 : }
297 :
298 1 : if (ipsc->lAlphaFieldBlanks(2)) {
299 1 : } else if ((state.dataEnvrn->varyingLocationLongSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
300 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
301 : }
302 :
303 1 : if (ipsc->lAlphaFieldBlanks(3)) {
304 1 : } else if ((state.dataEnvrn->varyingOrientationSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
305 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
306 : }
307 : }
308 :
309 1344 : void UpdateLocationAndOrientation(EnergyPlusData &state)
310 : {
311 1344 : if (state.dataEnvrn->varyingLocationLatSched != nullptr) {
312 1344 : state.dataEnvrn->Latitude = state.dataEnvrn->varyingLocationLatSched->getCurrentVal();
313 : }
314 1344 : if (state.dataEnvrn->varyingLocationLongSched != nullptr) {
315 1344 : state.dataEnvrn->Longitude = state.dataEnvrn->varyingLocationLongSched->getCurrentVal();
316 : }
317 :
318 1344 : CheckLocationValidity(state);
319 1344 : if (state.dataEnvrn->varyingOrientationSched != nullptr) {
320 1344 : state.dataHeatBal->BuildingAzimuth = mod(state.dataEnvrn->varyingOrientationSched->getCurrentVal(), 360.0);
321 2688 : state.dataSurfaceGeometry->CosBldgRelNorth =
322 1344 : std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
323 2688 : state.dataSurfaceGeometry->SinBldgRelNorth =
324 1344 : std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
325 8064 : for (size_t SurfNum = 1; SurfNum < state.dataSurface->Surface.size(); ++SurfNum) {
326 6720 : auto &surf = state.dataSurface->Surface(SurfNum);
327 33600 : for (int n = 1; n <= surf.Sides; ++n) {
328 26880 : Real64 Xb = surf.Vertex(n).x;
329 26880 : Real64 Yb = surf.Vertex(n).y;
330 26880 : surf.NewVertex(n).x = Xb * state.dataSurfaceGeometry->CosBldgRelNorth - Yb * state.dataSurfaceGeometry->SinBldgRelNorth;
331 26880 : surf.NewVertex(n).y = Xb * state.dataSurfaceGeometry->SinBldgRelNorth + Yb * state.dataSurfaceGeometry->CosBldgRelNorth;
332 26880 : surf.NewVertex(n).z = surf.Vertex(n).z;
333 : }
334 6720 : Vectors::CreateNewellSurfaceNormalVector(surf.NewVertex, surf.Sides, surf.NewellSurfaceNormalVector);
335 6720 : Real64 SurfWorldAz = 0.0;
336 6720 : Real64 SurfTilt = 0.0;
337 6720 : Vectors::DetermineAzimuthAndTilt(
338 6720 : surf.NewVertex, SurfWorldAz, SurfTilt, surf.lcsx, surf.lcsy, surf.lcsz, surf.NewellSurfaceNormalVector);
339 6720 : surf.Azimuth = SurfWorldAz;
340 6720 : surf.SinAzim = std::sin(SurfWorldAz * Constant::DegToRad);
341 6720 : surf.CosAzim = std::cos(SurfWorldAz * Constant::DegToRad);
342 6720 : surf.OutNormVec = surf.NewellSurfaceNormalVector;
343 : }
344 : }
345 1344 : }
346 :
347 11353 : bool GetNextEnvironment(EnergyPlusData &state, bool &Available, bool &ErrorsFound)
348 : {
349 :
350 : // SUBROUTINE INFORMATION:
351 : // AUTHOR Linda Lawrie
352 : // DATE WRITTEN August 2000
353 :
354 : // PURPOSE OF THIS SUBROUTINE:
355 : // This subroutine is called from the outer simulation manager and determines
356 : // if another environment is available in the "run list" or if the end has been
357 : // reached.
358 :
359 : static constexpr std::string_view RoutineName("GetNextEnvironment: ");
360 : static constexpr std::string_view EnvNameFormat("Environment,{},{},{},{},{},{},{},{},{},{},{},{},{}\n");
361 : static constexpr std::string_view EnvDSTNFormat("Environment:Daylight Saving,No,{}\n");
362 : static constexpr std::string_view EnvDSTYFormat("Environment:Daylight Saving,Yes,{},{},{}\n");
363 : static constexpr std::string_view DateFormat("{:02}/{:02}");
364 : static constexpr std::string_view DateFormatWithYear("{:02}/{:02}/{:04}");
365 :
366 11353 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->GetEnvironmentFirstCall) {
367 :
368 801 : state.dataReportFlag->PrintEndDataDictionary = true;
369 :
370 801 : ReportOutputFileHeaders(state); // Write the output file header information
371 :
372 : // Setup Output Variables, CurrentModuleObject='All Simulations'
373 :
374 3204 : SetupOutputVariable(state,
375 : "Site Outdoor Air Drybulb Temperature",
376 : Constant::Units::C,
377 801 : state.dataEnvrn->OutDryBulbTemp,
378 : OutputProcessor::TimeStepType::Zone,
379 : OutputProcessor::StoreType::Average,
380 : "Environment");
381 3204 : SetupOutputVariable(state,
382 : "Site Outdoor Air Dewpoint Temperature",
383 : Constant::Units::C,
384 801 : state.dataEnvrn->OutDewPointTemp,
385 : OutputProcessor::TimeStepType::Zone,
386 : OutputProcessor::StoreType::Average,
387 : "Environment");
388 3204 : SetupOutputVariable(state,
389 : "Site Outdoor Air Wetbulb Temperature",
390 : Constant::Units::C,
391 801 : state.dataEnvrn->OutWetBulbTemp,
392 : OutputProcessor::TimeStepType::Zone,
393 : OutputProcessor::StoreType::Average,
394 : "Environment");
395 3204 : SetupOutputVariable(state,
396 : "Site Outdoor Air Humidity Ratio",
397 : Constant::Units::kgWater_kgDryAir,
398 801 : state.dataEnvrn->OutHumRat,
399 : OutputProcessor::TimeStepType::Zone,
400 : OutputProcessor::StoreType::Average,
401 : "Environment");
402 3204 : SetupOutputVariable(state,
403 : "Site Outdoor Air Relative Humidity",
404 : Constant::Units::Perc,
405 801 : state.dataEnvrn->OutRelHum,
406 : OutputProcessor::TimeStepType::Zone,
407 : OutputProcessor::StoreType::Average,
408 : "Environment");
409 3204 : SetupOutputVariable(state,
410 : "Site Outdoor Air Barometric Pressure",
411 : Constant::Units::Pa,
412 801 : state.dataEnvrn->OutBaroPress,
413 : OutputProcessor::TimeStepType::Zone,
414 : OutputProcessor::StoreType::Average,
415 : "Environment");
416 3204 : SetupOutputVariable(state,
417 : "Site Wind Speed",
418 : Constant::Units::m_s,
419 801 : state.dataEnvrn->WindSpeed,
420 : OutputProcessor::TimeStepType::Zone,
421 : OutputProcessor::StoreType::Average,
422 : "Environment");
423 3204 : SetupOutputVariable(state,
424 : "Site Wind Direction",
425 : Constant::Units::deg,
426 801 : state.dataEnvrn->WindDir,
427 : OutputProcessor::TimeStepType::Zone,
428 : OutputProcessor::StoreType::Average,
429 : "Environment");
430 3204 : SetupOutputVariable(state,
431 : "Site Sky Temperature",
432 : Constant::Units::C,
433 801 : state.dataEnvrn->SkyTemp,
434 : OutputProcessor::TimeStepType::Zone,
435 : OutputProcessor::StoreType::Average,
436 : "Environment");
437 3204 : SetupOutputVariable(state,
438 : "Site Horizontal Infrared Radiation Rate per Area",
439 : Constant::Units::W_m2,
440 801 : state.dataWeather->HorizIRSky,
441 : OutputProcessor::TimeStepType::Zone,
442 : OutputProcessor::StoreType::Average,
443 : "Environment");
444 3204 : SetupOutputVariable(state,
445 : "Site Diffuse Solar Radiation Rate per Area",
446 : Constant::Units::W_m2,
447 801 : state.dataEnvrn->DifSolarRad,
448 : OutputProcessor::TimeStepType::Zone,
449 : OutputProcessor::StoreType::Average,
450 : "Environment");
451 3204 : SetupOutputVariable(state,
452 : "Site Direct Solar Radiation Rate per Area",
453 : Constant::Units::W_m2,
454 801 : state.dataEnvrn->BeamSolarRad,
455 : OutputProcessor::TimeStepType::Zone,
456 : OutputProcessor::StoreType::Average,
457 : "Environment");
458 3204 : SetupOutputVariable(state,
459 : "Liquid Precipitation Depth",
460 : Constant::Units::m,
461 801 : state.dataEnvrn->LiquidPrecipitation,
462 : OutputProcessor::TimeStepType::Zone,
463 : OutputProcessor::StoreType::Sum,
464 : "Environment");
465 3204 : SetupOutputVariable(state,
466 : "Site Precipitation Rate",
467 : Constant::Units::m_s,
468 801 : state.dataWaterData->RainFall.CurrentRate,
469 : OutputProcessor::TimeStepType::Zone,
470 : OutputProcessor::StoreType::Average,
471 : "Environment");
472 3204 : SetupOutputVariable(state,
473 : "Site Precipitation Depth",
474 : Constant::Units::m,
475 801 : state.dataWaterData->RainFall.CurrentAmount,
476 : OutputProcessor::TimeStepType::Zone,
477 : OutputProcessor::StoreType::Sum,
478 : "Environment");
479 3204 : SetupOutputVariable(state,
480 : "Site Ground Reflected Solar Radiation Rate per Area",
481 : Constant::Units::W_m2,
482 801 : state.dataEnvrn->GndSolarRad,
483 : OutputProcessor::TimeStepType::Zone,
484 : OutputProcessor::StoreType::Average,
485 : "Environment");
486 3204 : SetupOutputVariable(state,
487 : "Site Ground Temperature",
488 : Constant::Units::C,
489 801 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface],
490 : OutputProcessor::TimeStepType::Zone,
491 : OutputProcessor::StoreType::Average,
492 : "Environment");
493 3204 : SetupOutputVariable(state,
494 : "Site Surface Ground Temperature",
495 : Constant::Units::C,
496 801 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow],
497 : OutputProcessor::TimeStepType::Zone,
498 : OutputProcessor::StoreType::Average,
499 : "Environment");
500 3204 : SetupOutputVariable(state,
501 : "Site Deep Ground Temperature",
502 : Constant::Units::C,
503 801 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep],
504 : OutputProcessor::TimeStepType::Zone,
505 : OutputProcessor::StoreType::Average,
506 : "Environment");
507 3204 : SetupOutputVariable(state,
508 : "Site Simple Factor Model Ground Temperature",
509 : Constant::Units::C,
510 801 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod],
511 : OutputProcessor::TimeStepType::Zone,
512 : OutputProcessor::StoreType::Average,
513 : "Environment");
514 3204 : SetupOutputVariable(state,
515 : "Site Total Sky Cover",
516 : Constant::Units::None,
517 801 : state.dataEnvrn->TotalCloudCover,
518 : OutputProcessor::TimeStepType::Zone,
519 : OutputProcessor::StoreType::Average,
520 : "Environment");
521 3204 : SetupOutputVariable(state,
522 : "Site Opaque Sky Cover",
523 : Constant::Units::None,
524 801 : state.dataEnvrn->OpaqueCloudCover,
525 : OutputProcessor::TimeStepType::Zone,
526 : OutputProcessor::StoreType::Average,
527 : "Environment");
528 3204 : SetupOutputVariable(state,
529 : "Site Outdoor Air Enthalpy",
530 : Constant::Units::J_kg,
531 801 : state.dataEnvrn->OutEnthalpy,
532 : OutputProcessor::TimeStepType::Zone,
533 : OutputProcessor::StoreType::Average,
534 : "Environment");
535 3204 : SetupOutputVariable(state,
536 : "Site Outdoor Air Density",
537 : Constant::Units::kg_m3,
538 801 : state.dataEnvrn->OutAirDensity,
539 : OutputProcessor::TimeStepType::Zone,
540 : OutputProcessor::StoreType::Average,
541 : "Environment");
542 3204 : SetupOutputVariable(state,
543 : "Site Solar Azimuth Angle",
544 : Constant::Units::deg,
545 801 : state.dataWeather->SolarAzimuthAngle,
546 : OutputProcessor::TimeStepType::Zone,
547 : OutputProcessor::StoreType::Average,
548 : "Environment");
549 3204 : SetupOutputVariable(state,
550 : "Site Solar Altitude Angle",
551 : Constant::Units::deg,
552 801 : state.dataWeather->SolarAltitudeAngle,
553 : OutputProcessor::TimeStepType::Zone,
554 : OutputProcessor::StoreType::Average,
555 : "Environment");
556 3204 : SetupOutputVariable(state,
557 : "Site Solar Hour Angle",
558 : Constant::Units::deg,
559 801 : state.dataWeather->HrAngle,
560 : OutputProcessor::TimeStepType::Zone,
561 : OutputProcessor::StoreType::Average,
562 : "Environment");
563 2403 : SetupOutputVariable(state,
564 : "Site Rain Status",
565 : Constant::Units::None,
566 801 : state.dataWeather->RptIsRain,
567 : OutputProcessor::TimeStepType::Zone,
568 : OutputProcessor::StoreType::Average,
569 : "Environment");
570 1602 : SetupOutputVariable(state,
571 : "Site Snow on Ground Status",
572 : Constant::Units::None,
573 801 : state.dataWeather->RptIsSnow,
574 : OutputProcessor::TimeStepType::Zone,
575 : OutputProcessor::StoreType::Average,
576 : "Environment");
577 3204 : SetupOutputVariable(state,
578 : "Site Exterior Horizontal Sky Illuminance",
579 : Constant::Units::lux,
580 801 : state.dataEnvrn->HISKF,
581 : OutputProcessor::TimeStepType::Zone,
582 : OutputProcessor::StoreType::Average,
583 : "Environment");
584 3204 : SetupOutputVariable(state,
585 : "Site Exterior Horizontal Beam Illuminance",
586 : Constant::Units::lux,
587 801 : state.dataEnvrn->HISUNF,
588 : OutputProcessor::TimeStepType::Zone,
589 : OutputProcessor::StoreType::Average,
590 : "Environment");
591 3204 : SetupOutputVariable(state,
592 : "Site Exterior Beam Normal Illuminance",
593 : Constant::Units::lux,
594 801 : state.dataEnvrn->HISUNFnorm,
595 : OutputProcessor::TimeStepType::Zone,
596 : OutputProcessor::StoreType::Average,
597 : "Environment");
598 3204 : SetupOutputVariable(state,
599 : "Site Sky Diffuse Solar Radiation Luminous Efficacy",
600 : Constant::Units::lum_W,
601 801 : state.dataEnvrn->PDIFLW,
602 : OutputProcessor::TimeStepType::Zone,
603 : OutputProcessor::StoreType::Average,
604 : "Environment");
605 3204 : SetupOutputVariable(state,
606 : "Site Beam Solar Radiation Luminous Efficacy",
607 : Constant::Units::lum_W,
608 801 : state.dataEnvrn->PDIRLW,
609 : OutputProcessor::TimeStepType::Zone,
610 : OutputProcessor::StoreType::Average,
611 : "Environment");
612 3204 : SetupOutputVariable(state,
613 : "Site Daylighting Model Sky Clearness",
614 : Constant::Units::None,
615 801 : state.dataEnvrn->SkyClearness,
616 : OutputProcessor::TimeStepType::Zone,
617 : OutputProcessor::StoreType::Average,
618 : "Environment");
619 3204 : SetupOutputVariable(state,
620 : "Site Daylighting Model Sky Brightness",
621 : Constant::Units::None,
622 801 : state.dataEnvrn->SkyBrightness,
623 : OutputProcessor::TimeStepType::Zone,
624 : OutputProcessor::StoreType::Average,
625 : "Environment");
626 2403 : SetupOutputVariable(state,
627 : "Site Daylight Saving Time Status",
628 : Constant::Units::None,
629 801 : state.dataEnvrn->DSTIndicator,
630 : OutputProcessor::TimeStepType::Zone,
631 : OutputProcessor::StoreType::Average,
632 : "Environment");
633 1602 : SetupOutputVariable(state,
634 : "Site Day Type Index",
635 : Constant::Units::None,
636 801 : state.dataWeather->RptDayType,
637 : OutputProcessor::TimeStepType::Zone,
638 : OutputProcessor::StoreType::Average,
639 : "Environment");
640 3204 : SetupOutputVariable(state,
641 : "Site Mains Water Temperature",
642 : Constant::Units::C,
643 801 : state.dataEnvrn->WaterMainsTemp,
644 : OutputProcessor::TimeStepType::Zone,
645 : OutputProcessor::StoreType::Average,
646 : "Environment");
647 :
648 801 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
649 76 : SetupEMSActuator(state,
650 : "Weather Data",
651 : "Environment",
652 : "Outdoor Dry Bulb",
653 : "[C]",
654 76 : state.dataEnvrn->EMSOutDryBulbOverrideOn,
655 76 : state.dataEnvrn->EMSOutDryBulbOverrideValue);
656 76 : SetupEMSActuator(state,
657 : "Weather Data",
658 : "Environment",
659 : "Outdoor Dew Point",
660 : "[C]",
661 76 : state.dataEnvrn->EMSOutDewPointTempOverrideOn,
662 76 : state.dataEnvrn->EMSOutDewPointTempOverrideValue);
663 76 : SetupEMSActuator(state,
664 : "Weather Data",
665 : "Environment",
666 : "Outdoor Relative Humidity",
667 : "[%]",
668 76 : state.dataEnvrn->EMSOutRelHumOverrideOn,
669 76 : state.dataEnvrn->EMSOutRelHumOverrideValue);
670 76 : SetupEMSActuator(state,
671 : "Weather Data",
672 : "Environment",
673 : "Diffuse Solar",
674 : "[W/m2]",
675 76 : state.dataEnvrn->EMSDifSolarRadOverrideOn,
676 76 : state.dataEnvrn->EMSDifSolarRadOverrideValue);
677 76 : SetupEMSActuator(state,
678 : "Weather Data",
679 : "Environment",
680 : "Direct Solar",
681 : "[W/m2]",
682 76 : state.dataEnvrn->EMSBeamSolarRadOverrideOn,
683 76 : state.dataEnvrn->EMSBeamSolarRadOverrideValue);
684 76 : SetupEMSActuator(state,
685 : "Weather Data",
686 : "Environment",
687 : "Wind Speed",
688 : "[m/s]",
689 76 : state.dataEnvrn->EMSWindSpeedOverrideOn,
690 76 : state.dataEnvrn->EMSWindSpeedOverrideValue);
691 76 : SetupEMSActuator(state,
692 : "Weather Data",
693 : "Environment",
694 : "Wind Direction",
695 : "[deg]",
696 76 : state.dataEnvrn->EMSWindDirOverrideOn,
697 76 : state.dataEnvrn->EMSWindDirOverrideValue);
698 : }
699 801 : state.dataWeather->GetEnvironmentFirstCall = false;
700 :
701 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
702 :
703 11353 : if (state.dataWeather->GetBranchInputOneTimeFlag) {
704 :
705 801 : SetupInterpolationValues(state);
706 801 : state.dataWeather->TimeStepFraction = 1.0 / double(state.dataGlobal->TimeStepsInHour);
707 801 : state.dataEnvrn->rhoAirSTP = Psychrometrics::PsyRhoAirFnPbTdbW(
708 : state, DataEnvironment::StdPressureSeaLevel, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
709 801 : OpenWeatherFile(state, ErrorsFound); // moved here because of possibility of special days on EPW file
710 801 : CloseWeatherFile(state);
711 801 : ReadUserWeatherInput(state);
712 801 : AllocateWeatherData(state);
713 801 : if (state.dataWeather->NumIntervalsPerHour != 1) {
714 0 : if (state.dataWeather->NumIntervalsPerHour != state.dataGlobal->TimeStepsInHour) {
715 0 : ShowSevereError(
716 : state,
717 0 : format("{}Number of intervals per hour on Weather file does not match specified number of Time Steps Per Hour", RoutineName));
718 0 : ErrorsFound = true;
719 : }
720 : }
721 801 : state.dataWeather->GetBranchInputOneTimeFlag = false;
722 801 : state.dataWeather->Envrn = 0;
723 801 : if (state.dataWeather->NumOfEnvrn > 0) {
724 801 : ResolveLocationInformation(state, ErrorsFound); // Obtain weather related info from input file
725 801 : CheckLocationValidity(state);
726 1577 : if ((state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
727 776 : (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
728 776 : CheckWeatherFileValidity(state);
729 : }
730 801 : if (ErrorsFound) {
731 0 : ShowSevereError(state, format("{}No location specified, program will terminate.", RoutineName));
732 : }
733 : } else {
734 0 : ErrorsFound = true;
735 0 : ShowSevereError(state, format("{}No Design Days or Run Period(s) specified, program will terminate.", RoutineName));
736 : }
737 801 : if (state.dataSysVars->DDOnly && state.dataEnvrn->TotDesDays == 0) {
738 0 : ErrorsFound = true;
739 0 : ShowSevereError(
740 : state,
741 0 : format("{}Requested Design Days only (DataSystemVariables::DDOnly) but no Design Days specified, program will terminate.",
742 : RoutineName));
743 : }
744 801 : if (state.dataSysVars->ReverseDD && state.dataEnvrn->TotDesDays == 1) {
745 0 : ErrorsFound = true;
746 0 : ShowSevereError(
747 : state,
748 0 : format(
749 : "{}Requested Reverse Design Days (DataSystemVariables::ReverseDD) but only 1 Design Day specified, program will terminate.",
750 : RoutineName));
751 : }
752 :
753 : // Throw a Fatal now that we have said it'll terminalte
754 801 : if (ErrorsFound) {
755 0 : CloseWeatherFile(state); // will only close if opened.
756 0 : ShowFatalError(state, format("{}Errors found in Weather Data Input. Program terminates.", RoutineName));
757 : }
758 :
759 801 : state.dataEnvrn->CurrentOverallSimDay = 0;
760 801 : state.dataEnvrn->TotalOverallSimDays = 0;
761 801 : state.dataEnvrn->MaxNumberSimYears = 1;
762 3674 : for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
763 2873 : state.dataEnvrn->TotalOverallSimDays += state.dataWeather->Environment(i).TotalDays;
764 2873 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
765 1191 : state.dataEnvrn->MaxNumberSimYears = max(state.dataEnvrn->MaxNumberSimYears, state.dataWeather->Environment(i).NumSimYears);
766 : }
767 : }
768 801 : DisplaySimDaysProgress(state, state.dataEnvrn->CurrentOverallSimDay, state.dataEnvrn->TotalOverallSimDays);
769 : }
770 :
771 11353 : CloseWeatherFile(state); // will only close if opened.
772 11353 : ++state.dataWeather->Envrn;
773 11353 : state.dataWeather->DatesShouldBeReset = false;
774 11353 : if (state.dataWeather->Envrn > state.dataWeather->NumOfEnvrn) {
775 1603 : Available = false;
776 1603 : state.dataWeather->Envrn = 0;
777 1603 : state.dataEnvrn->CurEnvirNum = 0;
778 : } else {
779 9750 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
780 9750 : state.dataGlobal->KindOfSim = envCurr.KindOfEnvrn;
781 9750 : state.dataEnvrn->DayOfYear = envCurr.StartJDay;
782 9750 : state.dataEnvrn->DayOfMonth = envCurr.StartDay;
783 9750 : state.dataGlobal->CalendarYear = envCurr.StartYear;
784 9750 : state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
785 9750 : state.dataEnvrn->Month = envCurr.StartMonth;
786 9750 : state.dataGlobal->NumOfDayInEnvrn = envCurr.TotalDays; // Set day loop maximum from DataGlobals
787 :
788 12754 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
789 3004 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
790 13 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) {
791 8 : if (state.dataGlobal->DoDesDaySim) {
792 8 : ShowWarningError(state, format("{}Adaptive Comfort being reported during design day.", RoutineName));
793 8 : Real64 GrossApproxAvgDryBulb = (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb +
794 8 : (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb -
795 8 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).DailyDBRange)) /
796 8 : 2.0;
797 8 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
798 8 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, false, GrossApproxAvgDryBulb);
799 : }
800 8 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
801 2 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, false, GrossApproxAvgDryBulb);
802 : }
803 : }
804 : } else {
805 5 : if (state.dataGlobal->DoWeathSim || state.dataGlobal->DoDesDaySim) {
806 5 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
807 5 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, true, 0.0);
808 : }
809 5 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
810 1 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, true, 0.0);
811 : }
812 : }
813 : }
814 : }
815 :
816 9750 : if (state.dataWeather->Envrn > state.dataEnvrn->TotDesDays && state.dataWeather->WeatherFileExists) {
817 3776 : OpenEPlusWeatherFile(state, ErrorsFound, false);
818 : }
819 9750 : Available = true;
820 13398 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
821 3648 : (!state.dataWeather->WeatherFileExists && state.dataGlobal->DoWeathSim)) {
822 0 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
823 0 : ShowSevereError(state, "Weather Simulation requested, but no weather file attached.");
824 0 : ErrorsFound = true;
825 : }
826 0 : if (!state.dataGlobal->DoingHVACSizingSimulations) {
827 0 : state.dataWeather->Envrn = 0;
828 : }
829 0 : Available = false;
830 13398 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
831 3648 : (!state.dataWeather->WeatherFileExists && !state.dataGlobal->DoWeathSim)) {
832 20 : Available = false;
833 20 : if (!state.dataGlobal->DoingHVACSizingSimulations) {
834 20 : state.dataWeather->Envrn = 0;
835 : }
836 9730 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoingSizing) {
837 1223 : Available = false;
838 1223 : state.dataWeather->Envrn = 0;
839 : }
840 :
841 9750 : if (!ErrorsFound && Available && state.dataWeather->Envrn > 0) {
842 8507 : state.dataEnvrn->EnvironmentName = envCurr.Title;
843 8507 : state.dataEnvrn->CurEnvirNum = state.dataWeather->Envrn;
844 8507 : state.dataEnvrn->RunPeriodStartDayOfWeek = 0;
845 10962 : if ((state.dataGlobal->DoDesDaySim && (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather)) ||
846 2455 : ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoWeathSim)) {
847 6066 : if (state.dataWeather->PrntEnvHeaders && state.dataReportFlag->DoWeatherInitReporting) {
848 : static constexpr std::string_view EnvironFormat(
849 : "! <Environment>,Environment Name,Environment Type, Start Date, End Date, Start DayOfWeek, Duration {#days}, "
850 : "Source:Start DayOfWeek, Use Daylight Saving, Use Holidays, Apply Weekend Holiday Rule, Use Rain Values, Use Snow "
851 : "Values, Sky Temperature Model\n! <Environment:Special Days>, Special Day Name, Special Day Type, Source, Start Date, "
852 : "Duration {#days}\n! "
853 : "<Environment:Daylight Saving>, Daylight Saving Indicator, Source, Start Date, End Date\n! <Environment:WarmupDays>, "
854 : "NumberofWarmupDays");
855 800 : print(state.files.eio, "{}\n", EnvironFormat);
856 800 : state.dataWeather->PrntEnvHeaders = false;
857 : }
858 :
859 6066 : std::string StDate;
860 6066 : std::string EnDate;
861 12118 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) ||
862 6052 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodDesign)) {
863 36 : int DSTActStMon = 0;
864 36 : int DSTActStDay = 0;
865 36 : int DSTActEnMon = 0;
866 36 : int DSTActEnDay = 0;
867 36 : std::string kindOfRunPeriod = envCurr.cKindOfEnvrn;
868 36 : state.dataEnvrn->RunPeriodEnvironment = state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather;
869 36 : state.dataEnvrn->CurrentYearIsLeapYear = state.dataWeather->Environment(state.dataWeather->Envrn).IsLeapYear;
870 36 : if (state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears) {
871 2 : state.dataWeather->LeapYearAdd = 1;
872 : } else {
873 34 : state.dataWeather->LeapYearAdd = 0;
874 : }
875 36 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
876 2 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
877 : }
878 36 : state.dataWeather->UseDaylightSaving = envCurr.UseDST;
879 36 : state.dataWeather->UseSpecialDays = envCurr.UseHolidays;
880 36 : state.dataWeather->UseRainValues = envCurr.UseRain;
881 36 : state.dataWeather->UseSnowValues = envCurr.UseSnow;
882 :
883 36 : bool missingLeap(false); // Defer acting on anything found here until after the other range checks (see below)
884 :
885 36 : if (envCurr.ActualWeather && !state.dataWeather->WFAllowsLeapYears) {
886 0 : for (int year = envCurr.StartYear; year <= envCurr.EndYear; year++) {
887 0 : if (!isLeapYear(year)) {
888 0 : continue;
889 : }
890 :
891 0 : ShowSevereError(
892 : state,
893 0 : format("{}Weatherfile does not support leap years but runperiod includes a leap year ({})", RoutineName, year));
894 0 : missingLeap = true;
895 : }
896 : }
897 :
898 36 : bool OkRun = false;
899 :
900 36 : if (envCurr.ActualWeather) {
901 : // Actual weather
902 0 : for (auto const &dataperiod : state.dataWeather->DataPeriods) {
903 0 : int runStartJulian = dataperiod.DataStJDay;
904 0 : int runEndJulian = dataperiod.DataEnJDay;
905 0 : if (!dataperiod.HasYearData) {
906 0 : ShowSevereError(state,
907 0 : format("{}Actual weather runperiod has been entered but weatherfile DATA PERIOD does not have "
908 : "year included in start/end date.",
909 : RoutineName));
910 0 : ShowContinueError(state, "...to match the RunPeriod, the DATA PERIOD should be mm/dd/yyyy for both, or");
911 0 : ShowContinueError(state, "(...set \"Treat Weather as Actual\" to \"No\".)");
912 : }
913 0 : if (!General::BetweenDates(envCurr.StartDate, runStartJulian, runEndJulian)) {
914 0 : continue;
915 : }
916 0 : if (!General::BetweenDates(envCurr.EndDate, runStartJulian, runEndJulian)) {
917 0 : continue;
918 : }
919 0 : OkRun = true;
920 0 : break;
921 0 : }
922 : } else {
923 : // Typical (or just non-actual) weather
924 36 : for (auto &dataperiod : state.dataWeather->DataPeriods) {
925 : // Since this is not actual weather, there may be issues with this calculation
926 : // Assume the weather data starts the same year as the simulation, so LeapYearAdd is what
927 : // should be used.
928 36 : int runStartOrdinal = General::OrdinalDay(dataperiod.StMon, dataperiod.StDay, state.dataWeather->LeapYearAdd);
929 : // This one is harder, leave as is for now. What about multiple years of data?
930 36 : int runEndOrdinal = General::OrdinalDay(dataperiod.EnMon, dataperiod.EnDay, state.dataWeather->LeapYearAdd);
931 36 : if (runStartOrdinal == 1 && (runEndOrdinal == 366 || runEndOrdinal == 365)) {
932 : // Complete year(s) of weather data, will wrap around
933 36 : OkRun = true;
934 36 : break;
935 : }
936 0 : if (!General::BetweenDates(envCurr.StartJDay, runStartOrdinal, runEndOrdinal)) {
937 0 : continue;
938 : }
939 0 : if (!General::BetweenDates(envCurr.EndJDay, runStartOrdinal, runEndOrdinal)) {
940 0 : continue;
941 : }
942 0 : OkRun = true;
943 36 : }
944 : }
945 :
946 36 : if (!OkRun) {
947 0 : if (!envCurr.ActualWeather) {
948 0 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
949 0 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
950 0 : ShowSevereError(state,
951 0 : format("{}Runperiod [mm/dd] (Start={},End={}) requested not within Data Period(s) from Weather File",
952 : RoutineName,
953 : StDate,
954 : EnDate));
955 : } else {
956 0 : StDate = format(DateFormatWithYear, envCurr.StartMonth, envCurr.StartDay, envCurr.StartYear);
957 0 : EnDate = format(DateFormatWithYear, envCurr.EndMonth, envCurr.EndDay, envCurr.EndYear);
958 0 : ShowSevereError(
959 : state,
960 0 : format("{}Runperiod [mm/dd/yyyy] (Start={},End={}) requested not within Data Period(s) from Weather File",
961 : RoutineName,
962 : StDate,
963 : EnDate));
964 : }
965 :
966 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
967 0 : StDate = format(DateFormat, dataPeriod1.StMon, dataPeriod1.StDay);
968 0 : EnDate = format(DateFormat, dataPeriod1.EnMon, dataPeriod1.EnDay);
969 0 : if (dataPeriod1.StYear > 0) {
970 0 : StDate += format("/{}", dataPeriod1.StYear);
971 : } else {
972 0 : StDate += "/<noyear>";
973 : }
974 0 : if (dataPeriod1.EnYear > 0) {
975 0 : EnDate += format("/{}", dataPeriod1.EnYear);
976 : } else {
977 0 : EnDate += "/<noyear>";
978 : }
979 0 : if (state.dataWeather->NumDataPeriods == 1) {
980 0 : ShowContinueError(state, format("Weather Data Period (Start={},End={})", StDate, EnDate));
981 : } else {
982 0 : ShowContinueError(state, format("Multiple Weather Data Periods 1st (Start={},End={})", StDate, EnDate));
983 : }
984 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
985 : }
986 :
987 36 : if (missingLeap) {
988 : // Bail out now if we still need to
989 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
990 : }
991 :
992 : // Following builds Environment start/end for ASHRAE 55 warnings
993 36 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
994 36 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
995 36 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
996 14 : StDate += format("/{}", envCurr.StartYear);
997 14 : EnDate += format("/{}", envCurr.EndYear);
998 : }
999 36 : state.dataEnvrn->EnvironmentStartEnd = StDate + " - " + EnDate;
1000 36 : state.dataEnvrn->StartYear = envCurr.StartYear;
1001 36 : state.dataEnvrn->EndYear = envCurr.EndYear;
1002 :
1003 36 : int TWeekDay = (envCurr.DayOfWeek == 0) ? 1 : envCurr.DayOfWeek;
1004 36 : auto const &MonWeekDay = envCurr.MonWeekDay;
1005 :
1006 36 : if (state.dataReportFlag->DoWeatherInitReporting) {
1007 12 : std::string_view const AlpUseDST = (envCurr.UseDST) ? "Yes" : "No";
1008 12 : std::string_view const AlpUseSpec = (envCurr.UseHolidays) ? "Yes" : "No";
1009 12 : std::string_view const ApWkRule = (envCurr.ApplyWeekendRule) ? "Yes" : "No";
1010 12 : std::string_view const AlpUseRain = (envCurr.UseRain) ? "Yes" : "No";
1011 12 : std::string_view const AlpUseSnow = (envCurr.UseSnow) ? "Yes" : "No";
1012 :
1013 12 : print(state.files.eio,
1014 : EnvNameFormat,
1015 12 : envCurr.Title,
1016 : kindOfRunPeriod,
1017 : StDate,
1018 : EnDate,
1019 12 : Sched::dayTypeNames[TWeekDay],
1020 24 : fmt::to_string(envCurr.TotalDays),
1021 : "Use RunPeriod Specified Day",
1022 : AlpUseDST,
1023 : AlpUseSpec,
1024 : ApWkRule,
1025 : AlpUseRain,
1026 : AlpUseSnow,
1027 12 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1028 : }
1029 :
1030 60 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
1031 67 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather && state.dataGlobal->DoWeathSim) &&
1032 7 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
1033 0 : if (state.dataWeather->WFAllowsLeapYears) {
1034 0 : ShowSevereError(
1035 : state,
1036 0 : format("{}AdaptiveComfort Reporting does not work correctly with leap years in weather files.", RoutineName));
1037 0 : ErrorsFound = true;
1038 : }
1039 0 : if (state.dataWeather->NumDataPeriods != 1) {
1040 0 : ShowSevereError(
1041 : state,
1042 0 : format("{}AdaptiveComfort Reporting does not work correctly with multiple dataperiods in weather files.",
1043 : RoutineName));
1044 0 : ErrorsFound = true;
1045 : }
1046 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
1047 0 : if (dataPeriod1.StMon == 1 && dataPeriod1.StDay == 1) {
1048 0 : int RunStJDay = General::OrdinalDay(dataPeriod1.StMon, dataPeriod1.StDay, state.dataWeather->LeapYearAdd);
1049 0 : int RunEnJDay = General::OrdinalDay(dataPeriod1.EnMon, dataPeriod1.EnDay, state.dataWeather->LeapYearAdd);
1050 0 : if (RunEnJDay - RunStJDay + 1 != 365) {
1051 0 : ShowSevereError(state,
1052 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do "
1053 : "not contain 365 days.",
1054 : RoutineName));
1055 0 : ErrorsFound = true;
1056 : }
1057 0 : } else {
1058 0 : ShowSevereError(state,
1059 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do not "
1060 : "start on 1 January.",
1061 : RoutineName));
1062 0 : ErrorsFound = true;
1063 : }
1064 0 : if (state.dataWeather->NumIntervalsPerHour != 1) {
1065 0 : ShowSevereError(state,
1066 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that have "
1067 : "multiple interval records per hour.",
1068 : RoutineName));
1069 0 : ErrorsFound = true;
1070 : }
1071 : } // if
1072 :
1073 : // Only need to set Week days for Run Days
1074 36 : state.dataEnvrn->RunPeriodStartDayOfWeek = TWeekDay;
1075 36 : state.dataWeather->WeekDayTypes = 0;
1076 36 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1077 36 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1078 :
1079 36 : state.dataWeather->curSimDayForEndOfRunPeriod = envCurr.TotalDays;
1080 :
1081 36 : int i = JDay5Start;
1082 : while (true) {
1083 5242 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1084 5242 : TWeekDay = mod(TWeekDay, 7) + 1;
1085 5242 : ++i;
1086 5242 : if (i > 366) {
1087 0 : i = 1;
1088 : }
1089 5242 : if (i == JDay5End) {
1090 36 : break;
1091 : }
1092 : }
1093 :
1094 36 : state.dataWeather->DaylightSavingIsActive =
1095 36 : (state.dataWeather->UseDaylightSaving && state.dataWeather->EPWDaylightSaving) || state.dataWeather->IDFDaylightSaving;
1096 :
1097 36 : envCurr.SetWeekDays = false;
1098 :
1099 36 : if (state.dataWeather->DaylightSavingIsActive) {
1100 4 : SetDSTDateRanges(state, MonWeekDay, state.dataWeather->DSTIndex, DSTActStMon, DSTActStDay, DSTActEnMon, DSTActEnDay);
1101 : }
1102 :
1103 36 : SetSpecialDayDates(state, MonWeekDay);
1104 :
1105 36 : if (envCurr.StartMonth != 1 || envCurr.StartDay != 1) {
1106 22 : state.dataWeather->StartDatesCycleShouldBeReset = true;
1107 22 : state.dataWeather->Jan1DatesShouldBeReset = true;
1108 : }
1109 :
1110 36 : if (envCurr.StartMonth == 1 && envCurr.StartDay == 1) {
1111 14 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1112 14 : state.dataWeather->Jan1DatesShouldBeReset = true;
1113 : }
1114 :
1115 36 : if (envCurr.ActualWeather) {
1116 0 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1117 0 : state.dataWeather->Jan1DatesShouldBeReset = true;
1118 : }
1119 :
1120 : // Report Actual Dates for Daylight Saving and Special Days
1121 36 : if (!state.dataGlobal->KickOffSimulation) {
1122 24 : std::string Source;
1123 24 : if (state.dataWeather->UseDaylightSaving) {
1124 6 : if (state.dataWeather->EPWDaylightSaving) {
1125 1 : Source = "WeatherFile";
1126 : }
1127 : } else {
1128 18 : Source = "RunPeriod Object";
1129 : }
1130 24 : if (state.dataWeather->IDFDaylightSaving) {
1131 1 : Source = "InputFile";
1132 : }
1133 24 : if (state.dataWeather->DaylightSavingIsActive && state.dataReportFlag->DoWeatherInitReporting) {
1134 2 : StDate = format(DateFormat, DSTActStMon, DSTActStDay);
1135 2 : EnDate = format(DateFormat, DSTActEnMon, DSTActEnDay);
1136 2 : print(state.files.eio, EnvDSTYFormat, Source, StDate, EnDate);
1137 22 : } else if (state.dataGlobal->DoOutputReporting) {
1138 10 : print(state.files.eio, EnvDSTNFormat, Source);
1139 : }
1140 45 : for (int k = 1; k <= state.dataWeather->NumSpecialDays; ++k) {
1141 21 : auto &specialDay = state.dataWeather->SpecialDays(k);
1142 : static constexpr std::string_view EnvSpDyFormat("Environment:Special Days,{},{},{},{},{:3}\n");
1143 21 : if (specialDay.WthrFile && state.dataWeather->UseSpecialDays && state.dataReportFlag->DoWeatherInitReporting) {
1144 11 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1145 11 : print(state.files.eio,
1146 : EnvSpDyFormat,
1147 11 : specialDay.Name,
1148 11 : Sched::dayTypeNames[specialDay.DayType],
1149 : "WeatherFile",
1150 : StDate,
1151 11 : specialDay.Duration);
1152 : }
1153 21 : if (!specialDay.WthrFile && state.dataReportFlag->DoWeatherInitReporting) {
1154 10 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1155 10 : print(state.files.eio,
1156 : EnvSpDyFormat,
1157 10 : specialDay.Name,
1158 10 : Sched::dayTypeNames[specialDay.DayType],
1159 : "InputFile",
1160 : StDate,
1161 10 : specialDay.Duration);
1162 : }
1163 : }
1164 24 : }
1165 :
1166 6192 : } else if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay ||
1167 126 : state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay) { // Design Day
1168 6029 : auto const &desDayInput = state.dataWeather->DesDayInput(envCurr.DesignDayNum);
1169 6029 : state.dataEnvrn->RunPeriodEnvironment = false;
1170 6029 : StDate = format(DateFormat, desDayInput.Month, desDayInput.DayOfMonth);
1171 6029 : EnDate = StDate;
1172 6029 : if (state.dataReportFlag->DoWeatherInitReporting) {
1173 1855 : print(state.files.eio,
1174 : EnvNameFormat,
1175 1855 : envCurr.Title,
1176 : "SizingPeriod:DesignDay",
1177 : StDate,
1178 : EnDate,
1179 1855 : Sched::dayTypeNames[desDayInput.DayType],
1180 : "1",
1181 : "N/A",
1182 : "N/A",
1183 : "N/A",
1184 : "N/A",
1185 : "N/A",
1186 : "N/A",
1187 1855 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1188 : }
1189 6029 : if (desDayInput.DSTIndicator == 0 && state.dataReportFlag->DoWeatherInitReporting) {
1190 1855 : print(state.files.eio, EnvDSTNFormat, "SizingPeriod:DesignDay");
1191 4174 : } else if (state.dataReportFlag->DoWeatherInitReporting) {
1192 0 : print(state.files.eio, EnvDSTYFormat, "SizingPeriod:DesignDay", StDate, EnDate);
1193 : }
1194 : }
1195 6066 : }
1196 : } // ErrorsFound
1197 : }
1198 :
1199 11353 : if (ErrorsFound && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
1200 0 : ShowSevereError(state, format("{}Errors found in getting a new environment", RoutineName));
1201 0 : Available = false;
1202 11353 : } else if (ErrorsFound) {
1203 0 : Available = false;
1204 : }
1205 11353 : return Available && !ErrorsFound;
1206 : }
1207 :
1208 17 : void AddDesignSetToEnvironmentStruct(EnergyPlusData &state, int const HVACSizingIterCount)
1209 : {
1210 17 : int OrigNumOfEnvrn = state.dataWeather->NumOfEnvrn;
1211 :
1212 84 : for (int i = 1; i <= OrigNumOfEnvrn; ++i) {
1213 : // Gotcha: references may no longer be valid after a redimension! Cannot declare reference to Environment(i) here.
1214 67 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1215 35 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1216 35 : auto &envBase = state.dataWeather->Environment(i);
1217 35 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1218 35 : envNew = envBase; // copy over seed data from current array element
1219 35 : envNew.SeedEnvrnNum = i;
1220 35 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeDesignDay;
1221 35 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1222 35 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1223 32 : } else if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
1224 0 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1225 0 : auto &envBase = state.dataWeather->Environment(i);
1226 0 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1227 0 : envNew = envBase; // copy over seed data
1228 0 : envNew.SeedEnvrnNum = i;
1229 0 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeRunPeriodDesign;
1230 0 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1231 0 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1232 : }
1233 : } // for each loop over Environment data strucure
1234 17 : }
1235 :
1236 1989 : void SetupWeekDaysByMonth(EnergyPlusData &state, int const StMon, int const StDay, int const StWeekDay, Array1D_int &WeekDays)
1237 : {
1238 :
1239 : // SUBROUTINE INFORMATION:
1240 : // AUTHOR Linda Lawrie
1241 : // DATE WRITTEN August 2000
1242 :
1243 : // PURPOSE OF THIS SUBROUTINE:
1244 : // This subroutine calculates the weekday for each month based on the start date and
1245 : // weekday specified for that date.
1246 :
1247 : // Argument array dimensioning
1248 1989 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1249 :
1250 : // Set 1st day of Start Month
1251 1989 : int CurWeekDay{StWeekDay};
1252 8198 : for (int i = 1; i <= StDay - 1; ++i) {
1253 6209 : --CurWeekDay;
1254 6209 : if (CurWeekDay == 0) {
1255 941 : CurWeekDay = 7;
1256 : }
1257 : }
1258 :
1259 1989 : WeekDays(StMon) = CurWeekDay;
1260 21060 : for (int i = StMon + 1; i <= 12; ++i) {
1261 :
1262 19071 : if (i == 2) {
1263 1482 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1264 7422 : while (CurWeekDay > 7) {
1265 5940 : CurWeekDay -= 7;
1266 : }
1267 1482 : WeekDays(i) = CurWeekDay;
1268 17589 : } else if (i == 3) {
1269 1498 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + state.dataWeather->LeapYearAdd;
1270 7490 : while (CurWeekDay > 7) {
1271 5992 : CurWeekDay -= 7;
1272 : }
1273 1498 : WeekDays(i) = CurWeekDay;
1274 : } else {
1275 16091 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1276 86392 : while (CurWeekDay > 7) {
1277 70301 : CurWeekDay -= 7;
1278 : }
1279 16091 : WeekDays(i) = CurWeekDay;
1280 : }
1281 : }
1282 :
1283 1989 : if (any_eq(WeekDays, 0)) {
1284 : // need to start at StMon and go backwards.
1285 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1286 506 : CurWeekDay = StWeekDay;
1287 3133 : for (int i = 1; i <= StDay - 1; ++i) {
1288 2627 : --CurWeekDay;
1289 2627 : if (CurWeekDay == 0) {
1290 412 : CurWeekDay = 7;
1291 : }
1292 : }
1293 :
1294 3308 : for (int i = StMon - 1; i >= 1; --i) {
1295 :
1296 2802 : if (i == 1) {
1297 506 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1298 2807 : while (CurWeekDay <= 0) {
1299 2301 : CurWeekDay += 7;
1300 : }
1301 506 : WeekDays(i) = CurWeekDay;
1302 2296 : } else if (i == 2) {
1303 490 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
1304 2450 : while (CurWeekDay <= 0) {
1305 1960 : CurWeekDay += 7;
1306 : }
1307 490 : WeekDays(i) = CurWeekDay;
1308 1806 : } else if ((i >= 3) && (i <= 12)) {
1309 1806 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1310 9645 : while (CurWeekDay <= 0) {
1311 7839 : CurWeekDay += 7;
1312 : }
1313 1806 : WeekDays(i) = CurWeekDay;
1314 : }
1315 : }
1316 : }
1317 1989 : }
1318 : #pragma clang diagnostic pop
1319 :
1320 0 : void ResetWeekDaysByMonth(EnergyPlusData &state,
1321 : Array1D_int &WeekDays,
1322 : int const AddLeapYear,
1323 : int const StartMonth,
1324 : int const StartMonthDay,
1325 : int const EndMonth,
1326 : int const EndMonthDay,
1327 : bool const Rollover,
1328 : bool const MidSimReset)
1329 : {
1330 :
1331 : // SUBROUTINE INFORMATION:
1332 : // AUTHOR Linda Lawrie
1333 : // DATE WRITTEN March 2012
1334 :
1335 : // PURPOSE OF THIS SUBROUTINE:
1336 : // This subroutine resets the weekday for each month based on the current weekday
1337 : // and previous weekdays per month.
1338 :
1339 0 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1340 :
1341 0 : Array1D_int WeekDaysCopy(12);
1342 : int CurWeekDay;
1343 :
1344 0 : WeekDaysCopy = WeekDays;
1345 0 : if (!MidSimReset) {
1346 0 : if (Rollover) {
1347 0 : if (StartMonth == 1) {
1348 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1349 : } else {
1350 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1351 : }
1352 : } else { // restart at same as before
1353 0 : CurWeekDay = WeekDays(StartMonth);
1354 : }
1355 0 : while (CurWeekDay > 7) {
1356 0 : CurWeekDay -= 7;
1357 : }
1358 :
1359 0 : WeekDays = 0;
1360 0 : WeekDays(StartMonth) = CurWeekDay;
1361 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1362 0 : if (i == 2) {
1363 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1364 0 : while (CurWeekDay > 7) {
1365 0 : CurWeekDay -= 7;
1366 : }
1367 0 : WeekDays(i) = CurWeekDay;
1368 0 : } else if (i == 3) {
1369 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1370 0 : while (CurWeekDay > 7) {
1371 0 : CurWeekDay -= 7;
1372 : }
1373 0 : WeekDays(i) = CurWeekDay;
1374 : } else {
1375 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1376 0 : while (CurWeekDay > 7) {
1377 0 : CurWeekDay -= 7;
1378 : }
1379 0 : WeekDays(i) = CurWeekDay;
1380 : }
1381 : }
1382 :
1383 0 : if (any_eq(WeekDays, 0)) {
1384 : // need to start at StMon and go backwards.
1385 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1386 0 : CurWeekDay = WeekDays(StartMonth);
1387 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1388 0 : --CurWeekDay;
1389 0 : if (CurWeekDay == 0) {
1390 0 : CurWeekDay = 7;
1391 : }
1392 : }
1393 :
1394 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1395 :
1396 0 : if (i == 1) {
1397 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1398 0 : while (CurWeekDay <= 0) {
1399 0 : CurWeekDay += 7;
1400 : }
1401 0 : WeekDays(i) = CurWeekDay;
1402 0 : } else if (i == 2) {
1403 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1404 0 : while (CurWeekDay <= 0) {
1405 0 : CurWeekDay += 7;
1406 : }
1407 0 : WeekDays(i) = CurWeekDay;
1408 0 : } else if ((i >= 3) && (i <= 12)) {
1409 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1410 0 : while (CurWeekDay <= 0) {
1411 0 : CurWeekDay += 7;
1412 : }
1413 0 : WeekDays(i) = CurWeekDay;
1414 : }
1415 : }
1416 : }
1417 :
1418 : } else {
1419 0 : if (Rollover) {
1420 0 : if (StartMonth == 1) {
1421 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1422 : } else {
1423 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1424 : }
1425 : } else { // restart at same as before
1426 0 : CurWeekDay = WeekDays(StartMonth);
1427 : }
1428 0 : while (CurWeekDay > 7) {
1429 0 : CurWeekDay -= 7;
1430 : }
1431 0 : WeekDays = 0;
1432 0 : if (StartMonth != 1) {
1433 0 : CurWeekDay = WeekDaysCopy(12) + state.dataWeather->EndDayOfMonth(12);
1434 0 : while (CurWeekDay > 7) {
1435 0 : CurWeekDay -= 7;
1436 : }
1437 0 : WeekDays(1) = CurWeekDay;
1438 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1439 0 : while (CurWeekDay > 7) {
1440 0 : CurWeekDay -= 7;
1441 : }
1442 0 : WeekDays(2) = CurWeekDay;
1443 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1444 0 : while (CurWeekDay > 7) {
1445 0 : CurWeekDay -= 7;
1446 : }
1447 0 : WeekDays(3) = CurWeekDay;
1448 0 : for (int i = 4; i <= 12; ++i) {
1449 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1450 0 : while (CurWeekDay > 7) {
1451 0 : CurWeekDay -= 7;
1452 : }
1453 0 : WeekDays(i) = CurWeekDay;
1454 : }
1455 : } else {
1456 0 : WeekDays = 0;
1457 0 : WeekDays(StartMonth) = CurWeekDay;
1458 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1459 0 : if (i == 2) {
1460 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1461 0 : while (CurWeekDay > 7) {
1462 0 : CurWeekDay -= 7;
1463 : }
1464 0 : WeekDays(i) = CurWeekDay;
1465 0 : } else if (i == 3) {
1466 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1467 0 : while (CurWeekDay > 7) {
1468 0 : CurWeekDay -= 7;
1469 : }
1470 0 : WeekDays(i) = CurWeekDay;
1471 : } else {
1472 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1473 0 : while (CurWeekDay > 7) {
1474 0 : CurWeekDay -= 7;
1475 : }
1476 0 : WeekDays(i) = CurWeekDay;
1477 : }
1478 : }
1479 :
1480 0 : if (any_eq(WeekDays, 0)) {
1481 : // need to start at StMon and go backwards.
1482 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1483 0 : CurWeekDay = WeekDays(StartMonth);
1484 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1485 0 : --CurWeekDay;
1486 0 : if (CurWeekDay == 0) {
1487 0 : CurWeekDay = 7;
1488 : }
1489 : }
1490 :
1491 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1492 :
1493 0 : if (i == 1) {
1494 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1495 0 : while (CurWeekDay <= 0) {
1496 0 : CurWeekDay += 7;
1497 : }
1498 0 : WeekDays(i) = CurWeekDay;
1499 0 : } else if (i == 2) {
1500 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1501 0 : while (CurWeekDay <= 0) {
1502 0 : CurWeekDay += 7;
1503 : }
1504 0 : WeekDays(i) = CurWeekDay;
1505 0 : } else if ((i >= 3) && (i <= 12)) {
1506 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1507 0 : while (CurWeekDay <= 0) {
1508 0 : CurWeekDay += 7;
1509 : }
1510 0 : WeekDays(i) = CurWeekDay;
1511 : }
1512 : }
1513 : }
1514 : }
1515 : }
1516 0 : }
1517 :
1518 6 : void SetDSTDateRanges(EnergyPlusData &state,
1519 : Array1D_int const &MonWeekDay, // Weekday of each day 1 of month
1520 : Array1D_int &DSTIdx, // DST Index for each julian day (1:366)
1521 : ObjexxFCL::Optional_int DSTActStMon,
1522 : ObjexxFCL::Optional_int DSTActStDay,
1523 : ObjexxFCL::Optional_int DSTActEnMon,
1524 : ObjexxFCL::Optional_int DSTActEnDay)
1525 : {
1526 :
1527 : // SUBROUTINE INFORMATION:
1528 : // AUTHOR Linda Lawrie
1529 : // DATE WRITTEN March 2012
1530 :
1531 : // PURPOSE OF THIS SUBROUTINE:
1532 : // With multiple year weather files (or repeating weather files that rollover day),
1533 : // need to set DST (Daylight Saving Time) dates at start of environment or year.
1534 : // DST is only projected for one year.
1535 :
1536 : static constexpr std::string_view RoutineName("SetDSTDateRanges: ");
1537 :
1538 : int ActStartMonth; // Actual Start Month
1539 : int ActStartDay; // Actual Start Day of Month
1540 : int ActEndMonth; // Actual End Month
1541 : int ActEndDay; // Actual End Day of Month
1542 :
1543 6 : bool ErrorsFound = false;
1544 6 : if (state.dataWeather->DST.StDateType == DateType::MonthDay) {
1545 3 : ActStartMonth = state.dataWeather->DST.StMon;
1546 3 : ActStartDay = state.dataWeather->DST.StDay;
1547 3 : } else if (state.dataWeather->DST.StDateType == DateType::NthDayInMonth) {
1548 3 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1549 6 : while (ThisDay <= 0) {
1550 3 : ThisDay += 7;
1551 : }
1552 3 : ThisDay += 7 * (state.dataWeather->DST.StDay - 1);
1553 3 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1554 0 : ShowSevereError(state, format("{}Determining DST: DST Start Date, Nth Day of Month, not enough Nths", RoutineName));
1555 0 : ErrorsFound = true;
1556 : } else {
1557 3 : ActStartMonth = state.dataWeather->DST.StMon;
1558 3 : ActStartDay = ThisDay;
1559 : }
1560 : } else { // LastWeekDayInMonth
1561 0 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1562 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1563 0 : ThisDay += 7;
1564 : }
1565 0 : ActStartMonth = state.dataWeather->DST.StMon;
1566 0 : ActStartDay = ThisDay;
1567 : }
1568 :
1569 6 : if (state.dataWeather->DST.EnDateType == DateType::MonthDay) {
1570 3 : ActEndMonth = state.dataWeather->DST.EnMon;
1571 3 : ActEndDay = state.dataWeather->DST.EnDay;
1572 3 : } else if (state.dataWeather->DST.EnDateType == DateType::NthDayInMonth) {
1573 3 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1574 6 : while (ThisDay <= 0) {
1575 3 : ThisDay += 7;
1576 : }
1577 3 : ThisDay += 7 * (state.dataWeather->DST.EnDay - 1);
1578 3 : if (ThisDay >> state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1579 0 : ActEndMonth = 0; // Suppress uninitialized warning
1580 0 : ActEndDay = 0; // Suppress uninitialized warning
1581 0 : ShowSevereError(state, format("{}Determining DST: DST End Date, Nth Day of Month, not enough Nths", RoutineName));
1582 0 : ErrorsFound = true;
1583 : } else {
1584 3 : ActEndMonth = state.dataWeather->DST.EnMon;
1585 3 : ActEndDay = ThisDay;
1586 : }
1587 : } else { // LastWeekDayInMonth
1588 0 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1589 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1590 0 : ThisDay += 7;
1591 : }
1592 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1593 0 : ActEndDay = ThisDay;
1594 : }
1595 :
1596 6 : if (ErrorsFound) {
1597 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1598 : }
1599 :
1600 6 : if (present(DSTActStMon)) {
1601 4 : DSTActStMon = ActStartMonth;
1602 4 : DSTActStDay = ActStartDay;
1603 4 : DSTActEnMon = ActEndMonth;
1604 4 : DSTActEnDay = ActEndDay;
1605 : }
1606 :
1607 6 : DSTIdx = 0;
1608 6 : int JDay = General::OrdinalDay(ActStartMonth, ActStartDay, state.dataWeather->LeapYearAdd);
1609 6 : int JDay1 = General::OrdinalDay(ActEndMonth, ActEndDay, state.dataWeather->LeapYearAdd);
1610 6 : if (JDay1 >= JDay) {
1611 6 : DSTIdx({JDay, JDay1}) = 1;
1612 : } else {
1613 0 : DSTIdx({JDay, 366}) = 1;
1614 0 : DSTIdx({1, JDay1}) = 1;
1615 : }
1616 6 : }
1617 :
1618 43 : void SetSpecialDayDates(EnergyPlusData &state, Array1D_int const &MonWeekDay) // Weekday of each day 1 of month
1619 : {
1620 :
1621 : // SUBROUTINE INFORMATION:
1622 : // AUTHOR Linda Lawrie
1623 : // DATE WRITTEN March 2012
1624 :
1625 : // PURPOSE OF THIS SUBROUTINE:
1626 : // With multiple year weather files (or repeating weather files that rollover day),
1627 : // need to set Special Day dates at start of environment or year.
1628 : // Special Days are only projected for one year.
1629 :
1630 : static constexpr std::string_view RoutineName("SetSpecialDayDates: ");
1631 :
1632 : int JDay;
1633 :
1634 43 : bool ErrorsFound = false;
1635 43 : state.dataWeather->SpecialDayTypes = 0;
1636 106 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1637 63 : auto &specialDay = state.dataWeather->SpecialDays(i);
1638 63 : if (specialDay.WthrFile && !state.dataWeather->UseSpecialDays) {
1639 0 : continue;
1640 : }
1641 63 : if (specialDay.dateType <= DateType::MonthDay) {
1642 30 : JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd);
1643 30 : if (specialDay.Duration == 1 && state.dataWeather->Environment(state.dataWeather->Envrn).ApplyWeekendRule) {
1644 18 : if (state.dataWeather->WeekDayTypes(JDay) == static_cast<int>(Sched::DayType::Sunday)) {
1645 : // Sunday, must go to Monday
1646 6 : ++JDay;
1647 6 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
1648 0 : JDay = 1;
1649 : }
1650 12 : } else if (state.dataWeather->WeekDayTypes(JDay) == (int)Sched::DayType::Saturday) {
1651 0 : ++JDay;
1652 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
1653 0 : JDay = 1;
1654 : }
1655 0 : ++JDay;
1656 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
1657 0 : JDay = 1;
1658 : }
1659 : }
1660 : }
1661 30 : General::InvOrdinalDay(JDay, specialDay.ActStMon, specialDay.ActStDay, state.dataWeather->LeapYearAdd);
1662 33 : } else if (specialDay.dateType == DateType::NthDayInMonth) {
1663 27 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1664 27 : if (specialDay.WeekDay < MonWeekDay(specialDay.Month)) {
1665 18 : ThisDay += 7;
1666 : }
1667 27 : ThisDay += 7 * (specialDay.Day - 1);
1668 27 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1669 0 : ShowSevereError(state,
1670 0 : format("{}Special Day Date, Nth Day of Month, not enough Nths, for SpecialDay={}", RoutineName, specialDay.Name));
1671 0 : ErrorsFound = true;
1672 0 : continue;
1673 : }
1674 27 : specialDay.ActStMon = specialDay.Month;
1675 27 : specialDay.ActStDay = ThisDay;
1676 27 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1677 : } else { // LastWeekDayInMonth
1678 6 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1679 33 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1680 27 : ThisDay += 7;
1681 : }
1682 6 : specialDay.ActStMon = specialDay.Month;
1683 6 : specialDay.ActStDay = ThisDay;
1684 6 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1685 : }
1686 63 : if (state.dataWeather->SpecialDayTypes(JDay) != 0) {
1687 0 : ShowWarningError(
1688 : state,
1689 0 : format("{}Special Day definition ({}) is overwriting previously entered special day period", RoutineName, specialDay.Name));
1690 0 : if (state.dataWeather->UseSpecialDays) {
1691 0 : ShowContinueError(state, "...This could be caused by definitions on the Weather File.");
1692 : }
1693 0 : ShowContinueError(state, "...This could be caused by duplicate definitions in the Input File.");
1694 : }
1695 63 : int JDay1 = JDay - 1;
1696 126 : for (int j = 0; j <= specialDay.Duration - 1; ++j) {
1697 63 : ++JDay1;
1698 63 : if (JDay1 == 366 && state.dataWeather->LeapYearAdd == 0) {
1699 0 : JDay1 = 1;
1700 : }
1701 63 : if (JDay1 == 367) {
1702 0 : JDay1 = 1;
1703 : }
1704 63 : state.dataWeather->SpecialDayTypes(JDay1) = specialDay.DayType;
1705 : }
1706 : }
1707 :
1708 43 : if (ErrorsFound) {
1709 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1710 : }
1711 43 : }
1712 :
1713 2925340 : void InitializeWeather(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
1714 : {
1715 :
1716 : // SUBROUTINE INFORMATION:
1717 : // AUTHOR Rick Strand
1718 : // DATE WRITTEN June 1997
1719 :
1720 : // PURPOSE OF THIS SUBROUTINE:
1721 : // This subroutine is the main driver of the weather initializations.
1722 : // Most of the weather handling can be described as "initializations"
1723 : // so most of the work is done via this subroutine.
1724 :
1725 2925340 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->FirstCall) {
1726 :
1727 801 : state.dataWeather->FirstCall = false;
1728 801 : state.dataEnvrn->EndMonthFlag = false;
1729 :
1730 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
1731 :
1732 2925340 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
1733 2925340 : if (state.dataGlobal->BeginEnvrnFlag) {
1734 :
1735 : // Call and setup the Design Day environment
1736 7218 : if (envCurr.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
1737 6008 : if (envCurr.DesignDayNum > 0) {
1738 5985 : SetUpDesignDay(state, envCurr.DesignDayNum);
1739 5985 : state.dataEnvrn->EnvironmentName = envCurr.Title;
1740 : }
1741 : }
1742 :
1743 : // Only used in Weather file environments
1744 : // Start over missing values with each environment
1745 7218 : state.dataWeather->wvarsMissing.OutBaroPress = state.dataEnvrn->StdBaroPress; // Initial "missing" value
1746 7218 : state.dataWeather->wvarsMissing.OutDryBulbTemp = 6.0; // Initial "missing" value
1747 7218 : state.dataWeather->wvarsMissing.OutDewPointTemp = 3.0; // Initial "missing" value
1748 7218 : state.dataWeather->wvarsMissing.OutRelHum = 50.0; // Initial "missing" value
1749 7218 : state.dataWeather->wvarsMissing.WindSpeed = 2.5; // Initial "missing" value
1750 7218 : state.dataWeather->wvarsMissing.WindDir = 180; // Initial "missing" value
1751 7218 : state.dataWeather->wvarsMissing.TotalSkyCover = 5; // Initial "missing" value
1752 7218 : state.dataWeather->wvarsMissing.OpaqueSkyCover = 5; // Initial "missing" value
1753 7218 : state.dataWeather->wvarsMissing.Visibility = 777.7; // Initial "missing" value
1754 7218 : state.dataWeather->wvarsMissing.Ceiling = 77777; // Initial "missing" value
1755 7218 : state.dataWeather->wvarsMissing.AerOptDepth = 0.0; // Initial "missing" value
1756 7218 : state.dataWeather->wvarsMissing.SnowDepth = 0; // Initial "missing" value
1757 7218 : state.dataWeather->wvarsMissing.DaysLastSnow = 88; // Initial "missing" value
1758 7218 : state.dataWeather->wvarsMissing.Albedo = 0.0; // Initial "missing" value
1759 7218 : state.dataWeather->wvarsMissing.LiquidPrecip = 0.0; // Initial "missing" value
1760 : // Counts set to 0 for each environment
1761 7218 : state.dataWeather->wvarsMissedCounts = Weather::WeatherVarCounts();
1762 :
1763 : // Counts set to 0 for each environment
1764 7218 : state.dataWeather->wvarsOutOfRangeCounts = Weather::WeatherVarCounts();
1765 :
1766 7218 : state.dataWeather->IsRainThreshold = 0.8 / double(state.dataGlobal->TimeStepsInHour); // [mm]
1767 :
1768 7218 : if (!state.dataWeather->RPReadAllWeatherData) {
1769 7217 : printEnvrnStamp = true; // Set this to true so that on first non-warmup day (only) the environment header will print out
1770 : }
1771 :
1772 23626 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1773 16408 : state.dataWeather->SpecialDays(i).Used = false;
1774 : }
1775 :
1776 8531 : if ((state.dataGlobal->KindOfSim != Constant::KindOfSim::DesignDay) &&
1777 1313 : (state.dataGlobal->KindOfSim != Constant::KindOfSim::HVACSizeDesignDay)) {
1778 1233 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read first day's weather
1779 : } else {
1780 5985 : state.dataWeather->TomorrowVariables = state.dataWeather->DesignDay(envCurr.DesignDayNum);
1781 : }
1782 :
1783 : } // ... end of DataGlobals::BeginEnvrnFlag IF-THEN block.
1784 :
1785 2925340 : if (state.dataGlobal->BeginDayFlag) {
1786 :
1787 : // Check Holidays, Daylight Saving Time, Ground Temperatures, etc.
1788 :
1789 27036 : UpdateWeatherData(state); // Update daily weather info
1790 :
1791 : // Read tomorrow's weather only if necessary. This means that the
1792 : // simulation is out of warmup, is using a weather tape for this
1793 : // environment, and is not on the last day (day after last day is
1794 : // assumed to be equal to last day).
1795 :
1796 : // Following code checks whether the present day of simulation matches the start month and start day.
1797 : // In a multi year simulation with run period less than 365, we need to position the weather line
1798 : // appropriately.
1799 :
1800 33395 : if ((!state.dataGlobal->WarmupFlag) &&
1801 6359 : ((envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) && (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay))) {
1802 3011 : if (state.dataGlobal->DayOfSim < state.dataGlobal->NumOfDayInEnvrn) {
1803 2991 : if (state.dataGlobal->DayOfSim == state.dataWeather->curSimDayForEndOfRunPeriod) {
1804 0 : state.dataWeather->curSimDayForEndOfRunPeriod += envCurr.RawSimDays;
1805 0 : if (state.dataWeather->StartDatesCycleShouldBeReset) {
1806 0 : ResetWeekDaysByMonth(state,
1807 0 : envCurr.MonWeekDay,
1808 0 : state.dataWeather->LeapYearAdd,
1809 : envCurr.StartMonth,
1810 : envCurr.StartDay,
1811 : envCurr.EndMonth,
1812 : envCurr.EndDay,
1813 0 : envCurr.RollDayTypeOnRepeat);
1814 0 : if (state.dataWeather->DaylightSavingIsActive) {
1815 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1816 : }
1817 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1818 : }
1819 0 : ++state.dataWeather->YearOfSim;
1820 0 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1821 : } else {
1822 2991 : ReadWeatherForDay(state, state.dataGlobal->DayOfSim + 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1823 : }
1824 : }
1825 : }
1826 :
1827 27036 : state.dataEnvrn->EndYearFlag = false;
1828 27036 : if (state.dataEnvrn->DayOfMonth == state.dataWeather->EndDayOfMonthWithLeapDay(state.dataEnvrn->Month)) {
1829 103 : state.dataEnvrn->EndMonthFlag = true;
1830 103 : state.dataEnvrn->EndYearFlag = (state.dataEnvrn->Month == 12);
1831 : }
1832 :
1833 : // Set Tomorrow's date data
1834 27036 : state.dataEnvrn->MonthTomorrow = state.dataWeather->TomorrowVariables.Month;
1835 27036 : state.dataEnvrn->DayOfMonthTomorrow = state.dataWeather->TomorrowVariables.DayOfMonth;
1836 27036 : state.dataEnvrn->DayOfWeekTomorrow = state.dataWeather->TomorrowVariables.DayOfWeek;
1837 27036 : state.dataEnvrn->HolidayIndexTomorrow = state.dataWeather->TomorrowVariables.HolidayIndex;
1838 27036 : state.dataEnvrn->YearTomorrow = state.dataWeather->TomorrowVariables.Year;
1839 :
1840 27036 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1841 3810 : if (state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1 && envCurr.ActualWeather) {
1842 0 : if (state.dataWeather->DatesShouldBeReset) {
1843 0 : if (envCurr.TreatYearsAsConsecutive) {
1844 0 : ++envCurr.CurrentYear;
1845 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1846 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1847 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1848 :
1849 : // need to reset MonWeekDay and WeekDayTypes
1850 0 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1851 0 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1852 0 : if (!envCurr.ActualWeather) {
1853 0 : state.dataWeather->curSimDayForEndOfRunPeriod =
1854 0 : state.dataGlobal->DayOfSim + envCurr.RawSimDays + state.dataWeather->LeapYearAdd - 1;
1855 : }
1856 :
1857 : {
1858 0 : int i = JDay5Start;
1859 0 : int TWeekDay = state.dataEnvrn->DayOfWeek;
1860 : while (true) {
1861 0 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1862 0 : TWeekDay = mod(TWeekDay, 7) + 1;
1863 0 : ++i;
1864 0 : if (i > 366) {
1865 0 : i = 1;
1866 : }
1867 0 : if (i == JDay5End) {
1868 0 : break;
1869 : }
1870 : }
1871 : }
1872 0 : ResetWeekDaysByMonth(state,
1873 0 : envCurr.MonWeekDay,
1874 0 : state.dataWeather->LeapYearAdd,
1875 : envCurr.StartMonth,
1876 : envCurr.StartDay,
1877 : envCurr.EndMonth,
1878 : envCurr.EndDay,
1879 0 : envCurr.RollDayTypeOnRepeat);
1880 0 : if (state.dataWeather->DaylightSavingIsActive) {
1881 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1882 : }
1883 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1884 : }
1885 : }
1886 3810 : } else if ((state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1) && state.dataWeather->DatesShouldBeReset &&
1887 0 : (state.dataWeather->Jan1DatesShouldBeReset)) {
1888 0 : if (envCurr.TreatYearsAsConsecutive) {
1889 0 : ++envCurr.CurrentYear;
1890 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1891 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1892 0 : if (state.dataEnvrn->CurrentYearIsLeapYear && !state.dataWeather->WFAllowsLeapYears) {
1893 0 : state.dataEnvrn->CurrentYearIsLeapYear = false;
1894 : }
1895 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod && state.dataEnvrn->CurrentYearIsLeapYear) {
1896 0 : ++state.dataWeather->curSimDayForEndOfRunPeriod;
1897 : }
1898 : }
1899 :
1900 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1901 :
1902 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod) {
1903 0 : ResetWeekDaysByMonth(state,
1904 0 : envCurr.MonWeekDay,
1905 0 : state.dataWeather->LeapYearAdd,
1906 : envCurr.StartMonth,
1907 : envCurr.StartDay,
1908 : envCurr.EndMonth,
1909 : envCurr.EndDay,
1910 0 : envCurr.RollDayTypeOnRepeat,
1911 0 : envCurr.RollDayTypeOnRepeat || state.dataEnvrn->CurrentYearIsLeapYear);
1912 0 : if (state.dataWeather->DaylightSavingIsActive) {
1913 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1914 : }
1915 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1916 : }
1917 : }
1918 : }
1919 :
1920 : // at the end of each day find the min/max weather used for DOAS sizing
1921 27036 : if (state.dataGlobal->AirLoopHVACDOASUsedInSim) {
1922 35 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign || envCurr.KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1923 850 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
1924 5712 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
1925 4896 : Real64 Tdb = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDryBulbTemp;
1926 4896 : Real64 Tdp = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDewPointTemp;
1927 4896 : if (Tdb > envCurr.maxCoolingOATSizing) {
1928 42 : envCurr.maxCoolingOATSizing = Tdb;
1929 42 : envCurr.maxCoolingOADPSizing = Tdp;
1930 : }
1931 4896 : if (Tdb < envCurr.minHeatingOATSizing) {
1932 31 : envCurr.minHeatingOATSizing = Tdb;
1933 31 : envCurr.minHeatingOADPSizing = Tdp;
1934 : }
1935 : } // for (iTS)
1936 : } // for (iHr)
1937 : }
1938 : }
1939 :
1940 : } // ... end of DataGlobals::BeginDayFlag IF-THEN block.
1941 :
1942 5823644 : if (!state.dataGlobal->BeginDayFlag && !state.dataGlobal->WarmupFlag &&
1943 703561 : (state.dataEnvrn->Month != envCurr.StartMonth || state.dataEnvrn->DayOfMonth != envCurr.StartDay) &&
1944 5823644 : !state.dataWeather->DatesShouldBeReset && envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1945 7 : state.dataWeather->DatesShouldBeReset = true;
1946 : }
1947 :
1948 2926653 : if (state.dataGlobal->EndEnvrnFlag && (envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
1949 1313 : (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
1950 1233 : state.files.inputWeatherFile.rewind();
1951 1233 : SkipEPlusWFHeader(state);
1952 1233 : ReportMissing_RangeData(state);
1953 : }
1954 :
1955 : // set the EndDesignDayEnvrnsFlag (dataGlobal)
1956 : // True at the end of the last design day environment (last time step of last hour of last day of environ which is a design day)
1957 2925340 : state.dataGlobal->EndDesignDayEnvrnsFlag = false;
1958 2925340 : if (state.dataGlobal->EndEnvrnFlag) {
1959 7217 : if (state.dataWeather->Envrn < state.dataWeather->NumOfEnvrn) {
1960 6355 : if (envCurr.KindOfEnvrn != state.dataWeather->Environment(state.dataWeather->Envrn + 1).KindOfEnvrn) {
1961 2816 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1962 : }
1963 : } else {
1964 : // if the last environment set the flag to true.
1965 862 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1966 : }
1967 : }
1968 :
1969 2925340 : if (state.dataWeather->WaterMainsParameterReport) {
1970 : // this is done only once
1971 801 : if (state.dataWeather->WaterMainsTempsMethod == WaterMainsTempCalcMethod::CorrelationFromWeatherFile) {
1972 0 : if (!state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
1973 0 : state.dataWeather->OADryBulbAverage.CalcAnnualAndMonthlyDryBulbTemp(state);
1974 : }
1975 : }
1976 : // reports to eio file
1977 801 : ReportWaterMainsTempParameters(state);
1978 801 : state.dataWeather->WaterMainsParameterReport = false;
1979 : }
1980 2925340 : }
1981 :
1982 27036 : void UpdateWeatherData(EnergyPlusData &state)
1983 : {
1984 :
1985 : // SUBROUTINE INFORMATION:
1986 : // AUTHOR Rick Strand
1987 : // DATE WRITTEN June 1997
1988 :
1989 : // PURPOSE OF THIS SUBROUTINE:
1990 : // This subroutine updates all of the daily weather data in the local
1991 : // module level variables and the global variables.
1992 : // This subroutine will temporarily transfer the weather data for the
1993 : // current day to the old data structure contained in envdat.inc until
1994 : // enough reengineering has taken place to eliminate the need for this
1995 : // include.
1996 :
1997 27036 : state.dataWeather->TodayVariables = state.dataWeather->TomorrowVariables; // Transfer Tomorrow's Daily Weather Variables to Today
1998 :
1999 27036 : if (state.dataGlobal->BeginEnvrnFlag) {
2000 7218 : state.dataGlobal->PreviousHour = 24;
2001 : }
2002 :
2003 27036 : state.dataWeather->wvarsHrTsToday = state.dataWeather->wvarsHrTsTomorrow; // What a waste
2004 :
2005 : // Update Global Data
2006 :
2007 27036 : state.dataEnvrn->DayOfYear = state.dataWeather->TodayVariables.DayOfYear;
2008 27036 : state.dataEnvrn->Year = state.dataWeather->TodayVariables.Year;
2009 27036 : state.dataEnvrn->Month = state.dataWeather->TodayVariables.Month;
2010 27036 : state.dataEnvrn->DayOfMonth = state.dataWeather->TodayVariables.DayOfMonth;
2011 27036 : state.dataEnvrn->DayOfWeek = state.dataWeather->TodayVariables.DayOfWeek;
2012 27036 : state.dataEnvrn->HolidayIndex = state.dataWeather->TodayVariables.HolidayIndex;
2013 27036 : if (state.dataEnvrn->HolidayIndex > 0) {
2014 22534 : state.dataWeather->RptDayType = state.dataEnvrn->HolidayIndex;
2015 : } else {
2016 4502 : state.dataWeather->RptDayType = state.dataEnvrn->DayOfWeek;
2017 : }
2018 27036 : state.dataEnvrn->DSTIndicator = state.dataWeather->TodayVariables.DaylightSavingIndex;
2019 27036 : state.dataEnvrn->EquationOfTime = state.dataWeather->TodayVariables.EquationOfTime;
2020 27036 : state.dataEnvrn->CosSolarDeclinAngle = state.dataWeather->TodayVariables.CosSolarDeclinAngle;
2021 27036 : state.dataEnvrn->SinSolarDeclinAngle = state.dataWeather->TodayVariables.SinSolarDeclinAngle;
2022 27036 : }
2023 :
2024 2925340 : void SetCurrentWeather(EnergyPlusData &state)
2025 : {
2026 :
2027 : // SUBROUTINE INFORMATION:
2028 : // AUTHOR Russ Taylor
2029 : // DATE WRITTEN March 1990
2030 : // MODIFIED Aug94 (LKL) Fixed improper weighting
2031 : // Nov98 (FCW) Added call to get exterior illuminances
2032 : // Jan02 (FCW) Changed how ground reflectance for daylighting is set
2033 : // Mar12 (LKL) Changed settings for leap years/ current years.
2034 : // RE-ENGINEERED Apr97,May97 (RKS)
2035 :
2036 : // PURPOSE OF THIS SUBROUTINE:
2037 : // The purpose of this subroutine is to interpolate the hourly
2038 : // environment data for the sub-hourly time steps in EnergyPlus. In
2039 : // other words, this subroutine puts the current weather conditions
2040 : // into the proper variables. Rather than using the same data for
2041 : // each time step, environment data is interpolated as a continuum
2042 : // throughout the day.
2043 :
2044 : // METHODOLOGY EMPLOYED:
2045 : // The current hour (DataGlobals::HourOfDay) as well as the next hour are used
2046 : // to come up with environment data per time step interval. Method
2047 : // used is to assign a weighting for the current hour's data and
2048 : // (1-that weighting) to the next hour's data. Actual method is: if
2049 : // the current time step is 15 minutes into hour, the interpolated dry
2050 : // bulb temperature should be 3/4*dry bulb temperature of current hour
2051 : // and 1/4*dry bulb temperature of next environment hourly data. At
2052 : // day boundary (current hour = 24), the next hour is hour 1 of next
2053 : // weather data day (Tomorrow%).
2054 :
2055 : static constexpr std::string_view RoutineName("SetCurrentWeather");
2056 :
2057 2925340 : state.dataWeather->NextHour = state.dataGlobal->HourOfDay + 1;
2058 :
2059 2925340 : if (state.dataGlobal->HourOfDay == 24) { // Should investigate whether EndDayFlag is always set here and use that instead
2060 125257 : state.dataWeather->NextHour = 1;
2061 : }
2062 :
2063 2925340 : if (state.dataGlobal->HourOfDay == 1) { // Should investigate whether DataGlobals::BeginDayFlag is always set here and use that instead
2064 129107 : state.dataEnvrn->DayOfYear_Schedule = General::OrdinalDay(state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, 1);
2065 : }
2066 :
2067 2925340 : Sched::UpdateScheduleVals(state);
2068 :
2069 2925340 : state.dataEnvrn->CurMnDyHr =
2070 5850680 : format("{:02d}/{:02d} {:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, (unsigned short)(state.dataGlobal->HourOfDay - 1));
2071 2925340 : state.dataEnvrn->CurMnDy = format("{:02d}/{:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth);
2072 2925340 : state.dataEnvrn->CurMnDyYr =
2073 5850680 : format("{:02d}/{:02d}/{:04d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->CalendarYear);
2074 :
2075 2925340 : state.dataGlobal->WeightNow = state.dataWeather->Interpolation(state.dataGlobal->TimeStep);
2076 2925340 : state.dataGlobal->WeightPreviousHour = 1.0 - state.dataGlobal->WeightNow;
2077 :
2078 2925340 : state.dataGlobal->CurrentTime = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * (state.dataWeather->TimeStepFraction);
2079 2925340 : state.dataGlobal->SimTimeSteps = (state.dataGlobal->DayOfSim - 1) * 24 * state.dataGlobal->TimeStepsInHour +
2080 2925340 : (state.dataGlobal->HourOfDay - 1) * state.dataGlobal->TimeStepsInHour + state.dataGlobal->TimeStep;
2081 :
2082 2925340 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] =
2083 2925340 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2084 2925340 : state.dataEnvrn->GroundTempKelvin = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] + Constant::Kelvin;
2085 2925340 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod] =
2086 2925340 : state.dataWeather->siteFCFactorMethodGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2087 2925340 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow] =
2088 2925340 : state.dataWeather->siteShallowGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2089 2925340 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep] =
2090 2925340 : state.dataWeather->siteDeepGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2091 2925340 : state.dataEnvrn->GndReflectance = state.dataWeather->GroundReflectances(state.dataEnvrn->Month);
2092 2925340 : state.dataEnvrn->GndReflectanceForDayltg = state.dataEnvrn->GndReflectance;
2093 :
2094 2925340 : CalcWaterMainsTemp(state);
2095 :
2096 : // Determine if Sun is up or down, set Solar Cosine values for time step.
2097 2925340 : DetermineSunUpDown(state, state.dataEnvrn->SOLCOS);
2098 2925340 : if (state.dataEnvrn->SunIsUp && state.dataWeather->SolarAltitudeAngle < 0.0) {
2099 0 : ShowFatalError(state, format("SetCurrentWeather: At {} Sun is Up but Solar Altitude Angle is < 0.0", state.dataEnvrn->CurMnDyHr));
2100 : }
2101 :
2102 2925340 : auto const &today = state.dataWeather->wvarsHrTsToday(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2103 2925340 : state.dataEnvrn->OutDryBulbTemp = today.OutDryBulbTemp;
2104 2925340 : if (state.dataEnvrn->EMSOutDryBulbOverrideOn) {
2105 0 : state.dataEnvrn->OutDryBulbTemp = state.dataEnvrn->EMSOutDryBulbOverrideValue;
2106 : }
2107 2925340 : state.dataEnvrn->OutBaroPress = today.OutBaroPress;
2108 2925340 : state.dataEnvrn->OutDewPointTemp = today.OutDewPointTemp;
2109 2925340 : if (state.dataEnvrn->EMSOutDewPointTempOverrideOn) {
2110 0 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->EMSOutDewPointTempOverrideValue;
2111 : }
2112 2925340 : state.dataEnvrn->OutRelHum = today.OutRelHum;
2113 2925340 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->OutRelHum / 100.0;
2114 2925340 : if (state.dataEnvrn->EMSOutRelHumOverrideOn) {
2115 0 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->EMSOutRelHumOverrideValue / 100.0;
2116 0 : state.dataEnvrn->OutRelHum = state.dataEnvrn->EMSOutRelHumOverrideValue;
2117 : }
2118 :
2119 : // Humidity Ratio and Wet Bulb are derived
2120 2925340 : state.dataEnvrn->OutHumRat = Psychrometrics::PsyWFnTdbRhPb(
2121 2925340 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutRelHumValue, state.dataEnvrn->OutBaroPress, RoutineName);
2122 5850680 : state.dataEnvrn->OutWetBulbTemp =
2123 2925340 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat, state.dataEnvrn->OutBaroPress);
2124 2925340 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
2125 957737 : state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2126 1915474 : Real64 TempVal = Psychrometrics::PsyWFnTdbTwbPb(
2127 957737 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutBaroPress);
2128 957737 : state.dataEnvrn->OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, TempVal, state.dataEnvrn->OutBaroPress);
2129 : }
2130 :
2131 2925340 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
2132 1000823 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2133 : }
2134 :
2135 3251458 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) ||
2136 326118 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay)) {
2137 :
2138 9314019 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2139 6682070 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2140 : }
2141 :
2142 2631949 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2143 2631949 : int const envrnDayNum = envCurr.DesignDayNum;
2144 2631949 : auto const &desDayInput = state.dataWeather->DesDayInput(envrnDayNum);
2145 2631949 : auto &spSiteSchedule = state.dataWeather->spSiteSchedules(envrnDayNum);
2146 2631949 : auto const &desDayMod = state.dataWeather->desDayMods(envrnDayNum)(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2147 :
2148 2631949 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Default) {
2149 9234 : spSiteSchedule.OutDryBulbTemp = desDayMod.OutDryBulbTemp;
2150 : }
2151 :
2152 2631949 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
2153 2623174 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
2154 15606 : spSiteSchedule.OutRelHum = desDayMod.OutRelHum;
2155 : }
2156 2631949 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
2157 5289 : spSiteSchedule.BeamSolarRad = desDayMod.BeamSolarRad;
2158 5289 : spSiteSchedule.DifSolarRad = desDayMod.DifSolarRad;
2159 : }
2160 :
2161 2631949 : if (envCurr.skyTempModel == SkyTempModel::ScheduleValue || envCurr.skyTempModel == SkyTempModel::DryBulbDelta ||
2162 2630122 : envCurr.skyTempModel == SkyTempModel::DewPointDelta) {
2163 1827 : spSiteSchedule.SkyTemp = desDayMod.SkyTemp;
2164 : }
2165 293391 : } else if (state.dataEnvrn->TotDesDays > 0) {
2166 880341 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2167 586950 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2168 : }
2169 : }
2170 :
2171 2925340 : state.dataEnvrn->WindSpeed = today.WindSpeed;
2172 2925340 : if (state.dataEnvrn->EMSWindSpeedOverrideOn) {
2173 0 : state.dataEnvrn->WindSpeed = state.dataEnvrn->EMSWindSpeedOverrideValue;
2174 : }
2175 2925340 : state.dataEnvrn->WindDir = today.WindDir;
2176 2925340 : if (state.dataEnvrn->EMSWindDirOverrideOn) {
2177 0 : state.dataEnvrn->WindDir = state.dataEnvrn->EMSWindDirOverrideValue;
2178 : }
2179 2925340 : state.dataWeather->HorizIRSky = today.HorizIRSky;
2180 2925340 : state.dataEnvrn->SkyTemp = today.SkyTemp;
2181 2925340 : state.dataEnvrn->SkyTempKelvin = state.dataEnvrn->SkyTemp + Constant::Kelvin;
2182 2925340 : state.dataEnvrn->DifSolarRad = today.DifSolarRad;
2183 2925340 : if (state.dataEnvrn->EMSDifSolarRadOverrideOn) {
2184 0 : state.dataEnvrn->DifSolarRad = state.dataEnvrn->EMSDifSolarRadOverrideValue;
2185 : }
2186 2925340 : state.dataEnvrn->BeamSolarRad = today.BeamSolarRad;
2187 2925340 : if (state.dataEnvrn->EMSBeamSolarRadOverrideOn) {
2188 0 : state.dataEnvrn->BeamSolarRad = state.dataEnvrn->EMSBeamSolarRadOverrideValue;
2189 : }
2190 2925340 : state.dataEnvrn->LiquidPrecipitation = today.LiquidPrecip / 1000.0; // convert from mm to m
2191 2925340 : if ((state.dataEnvrn->RunPeriodEnvironment) && (!state.dataGlobal->WarmupFlag)) {
2192 254136 : int month = state.dataEnvrn->Month;
2193 254136 : state.dataWaterData->RainFall.MonthlyTotalPrecInWeather.at(month - 1) += state.dataEnvrn->LiquidPrecipitation * 1000.0;
2194 254136 : if ((state.dataEnvrn->LiquidPrecipitation > 0) && (state.dataGlobal->TimeStep == 1)) {
2195 549 : state.dataWaterData->RainFall.numRainyHoursInWeather.at(month - 1) += 1;
2196 : }
2197 : }
2198 :
2199 2925340 : WaterManager::UpdatePrecipitation(state);
2200 :
2201 2925340 : state.dataEnvrn->TotalCloudCover = today.TotalSkyCover;
2202 2925340 : state.dataEnvrn->OpaqueCloudCover = today.OpaqueSkyCover;
2203 :
2204 2925340 : if (state.dataWeather->UseRainValues) {
2205 : // It is set as LiquidPrecipitation >= .8 mm here: state.dataWeather->TomorrowLiquidPrecip(ts, hour) >=
2206 : // state.dataWeather->IsRainThreshold;
2207 2891293 : state.dataEnvrn->IsRain = today.IsRain;
2208 2891293 : if (state.dataWaterData->RainFall.ModeID == DataWater::RainfallMode::RainSchedDesign && state.dataEnvrn->RunPeriodEnvironment) {
2209 : // CurrentAmount unit: m
2210 0 : state.dataEnvrn->IsRain = state.dataWaterData->RainFall.CurrentAmount >= (state.dataWeather->IsRainThreshold / 1000.0);
2211 : }
2212 : } else {
2213 34047 : state.dataEnvrn->IsRain = false;
2214 : }
2215 2925340 : if (state.dataWeather->UseSnowValues) {
2216 2891293 : state.dataEnvrn->IsSnow = today.IsSnow;
2217 : } else {
2218 34047 : state.dataEnvrn->IsSnow = false;
2219 : }
2220 :
2221 2925340 : if (state.dataEnvrn->IsSnow) {
2222 0 : state.dataEnvrn->GndReflectance = max(min(state.dataEnvrn->GndReflectance * state.dataWeather->SnowGndRefModifier, 1.0), 0.0);
2223 0 : state.dataEnvrn->GndReflectanceForDayltg =
2224 0 : max(min(state.dataEnvrn->GndReflectanceForDayltg * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0);
2225 : }
2226 :
2227 2925340 : state.dataEnvrn->GndSolarRad =
2228 2925340 : max((state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z + state.dataEnvrn->DifSolarRad) * state.dataEnvrn->GndReflectance, 0.0);
2229 :
2230 2925340 : if (!state.dataEnvrn->SunIsUp) {
2231 1463062 : state.dataEnvrn->DifSolarRad = 0.0;
2232 1463062 : state.dataEnvrn->BeamSolarRad = 0.0;
2233 1463062 : state.dataEnvrn->GndSolarRad = 0.0;
2234 : }
2235 :
2236 2925340 : state.dataEnvrn->OutEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2237 5850680 : state.dataEnvrn->OutAirDensity =
2238 2925340 : Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2239 :
2240 2925340 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
2241 0 : state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2242 : }
2243 2925340 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
2244 0 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2245 : }
2246 :
2247 2925340 : DayltgCurrentExtHorizIllum(state);
2248 :
2249 2925340 : if (!state.dataEnvrn->IsRain) {
2250 2924012 : state.dataWeather->RptIsRain = 0;
2251 : } else {
2252 1328 : state.dataWeather->RptIsRain = 1;
2253 : }
2254 :
2255 2925340 : if (!state.dataEnvrn->IsSnow) {
2256 2925340 : state.dataWeather->RptIsSnow = 0;
2257 : } else {
2258 0 : state.dataWeather->RptIsSnow = 1;
2259 : }
2260 2925340 : }
2261 :
2262 4224 : void ReadWeatherForDay(EnergyPlusData &state,
2263 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2264 : int const Environ, // Environment being simulated
2265 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2266 : )
2267 : {
2268 :
2269 : // SUBROUTINE INFORMATION:
2270 : // AUTHOR Linda K. Lawrie
2271 : // DATE WRITTEN April 1999
2272 :
2273 : // PURPOSE OF THIS SUBROUTINE:
2274 : // This subroutine is the driving routine behind reading the weather data.
2275 : // Theoretically, several kinds of weather files could be read here. As
2276 : // distributed only EPW files are allowed.
2277 :
2278 4224 : ReadEPlusWeatherForDay(state, DayToRead, Environ, BackSpaceAfterRead);
2279 4224 : }
2280 :
2281 4224 : void ReadEPlusWeatherForDay(EnergyPlusData &state,
2282 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2283 : int const Environ, // Environment being simulated
2284 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2285 : )
2286 : {
2287 :
2288 : // SUBROUTINE INFORMATION:
2289 : // AUTHOR Linda K. Lawrie
2290 : // DATE WRITTEN April 1999
2291 : // MODIFIED March 2012; add actual weather read.
2292 :
2293 : // PURPOSE OF THIS SUBROUTINE:
2294 : // This subroutine reads the appropriate day of EPW weather data.
2295 :
2296 : int WYear;
2297 : int WMonth;
2298 : int WDay;
2299 : int WHour;
2300 : int WMinute;
2301 : Real64 DryBulb;
2302 : Real64 DewPoint;
2303 : Real64 RelHum;
2304 : Real64 AtmPress;
2305 : Real64 ETHoriz;
2306 : Real64 ETDirect;
2307 : Real64 IRHoriz;
2308 : Real64 GLBHoriz;
2309 : Real64 DirectRad;
2310 : Real64 DiffuseRad;
2311 : Real64 GLBHorizIllum;
2312 : Real64 DirectNrmIllum;
2313 : Real64 DiffuseHorizIllum;
2314 : Real64 ZenLum;
2315 : Real64 WindDir;
2316 : Real64 WindSpeed;
2317 : Real64 TotalSkyCover;
2318 : Real64 OpaqueSkyCover;
2319 : Real64 Visibility;
2320 : Real64 CeilHeight;
2321 : Real64 PrecipWater;
2322 : Real64 AerosolOptDepth;
2323 : Real64 SnowDepth;
2324 : Real64 DaysSinceLastSnow;
2325 : Real64 Albedo;
2326 : Real64 LiquidPrecip;
2327 : int PresWeathObs;
2328 4224 : Array1D_int PresWeathConds(9);
2329 :
2330 4224 : constexpr std::string_view routineName = "ReadEPlusWeatherForDay";
2331 :
2332 4224 : Array1D<WeatherVars> wvarsHr = Array1D<WeatherVars>(Constant::iHoursInDay);
2333 :
2334 4224 : auto &thisEnviron = state.dataWeather->Environment(Environ);
2335 :
2336 4224 : if (DayToRead == 1) {
2337 :
2338 : // Checks whether Weather file contains just one year of data. If yes then rewind and position to first
2339 : // day of weather file. The rest of code appropriately positions to the start day.
2340 :
2341 1233 : bool Ready = false;
2342 1233 : int NumRewinds = 0;
2343 : // Must position file to proper day
2344 : // File already position to first data record
2345 : // Set Current Day of Week to "start of Data Period"
2346 1233 : state.dataWeather->ReadEPlusWeatherCurTime = 1.0 / double(state.dataWeather->NumIntervalsPerHour);
2347 1233 : state.dataWeather->CurDayOfWeek = state.dataWeather->DataPeriods(1).WeekDay - 1;
2348 1233 : WYear = 0;
2349 1233 : WMonth = 0;
2350 1233 : WDay = 0;
2351 1233 : WHour = 0;
2352 1233 : WMinute = 0;
2353 1233 : state.dataWeather->LastHourSet = false;
2354 1233 : InputFile::ReadResult<std::string> WeatherDataLine{"", true, false};
2355 96384 : while (!Ready) {
2356 95151 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2357 95151 : if (WeatherDataLine.good) {
2358 : bool ErrorFound;
2359 95151 : InterpretWeatherDataLine(state,
2360 : WeatherDataLine.data,
2361 : ErrorFound,
2362 : WYear,
2363 : WMonth,
2364 : WDay,
2365 : WHour,
2366 : WMinute,
2367 : DryBulb,
2368 : DewPoint,
2369 : RelHum,
2370 : AtmPress,
2371 : ETHoriz,
2372 : ETDirect,
2373 : IRHoriz,
2374 : GLBHoriz,
2375 : DirectRad,
2376 : DiffuseRad,
2377 : GLBHorizIllum,
2378 : DirectNrmIllum,
2379 : DiffuseHorizIllum,
2380 : ZenLum,
2381 : WindDir,
2382 : WindSpeed,
2383 : TotalSkyCover,
2384 : OpaqueSkyCover,
2385 : Visibility,
2386 : CeilHeight,
2387 : PresWeathObs,
2388 : PresWeathConds,
2389 : PrecipWater,
2390 : AerosolOptDepth,
2391 : SnowDepth,
2392 : DaysSinceLastSnow,
2393 : Albedo,
2394 : LiquidPrecip);
2395 0 : } else if (WeatherDataLine.eof) {
2396 0 : if (NumRewinds > 0) {
2397 0 : std::string date = fmt::to_string(thisEnviron.StartMonth) + '/' + fmt::to_string(thisEnviron.StartDay);
2398 0 : if (thisEnviron.MatchYear) {
2399 0 : date += '/' + fmt::to_string(thisEnviron.StartYear);
2400 : }
2401 0 : ShowSevereError(state, format("Multiple rewinds on EPW while searching for first day {}", date));
2402 0 : } else {
2403 0 : state.files.inputWeatherFile.rewind();
2404 0 : ++NumRewinds;
2405 0 : SkipEPlusWFHeader(state);
2406 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2407 : bool ErrorFound;
2408 0 : InterpretWeatherDataLine(state,
2409 : WeatherDataLine.data,
2410 : ErrorFound,
2411 : WYear,
2412 : WMonth,
2413 : WDay,
2414 : WHour,
2415 : WMinute,
2416 : DryBulb,
2417 : DewPoint,
2418 : RelHum,
2419 : AtmPress,
2420 : ETHoriz,
2421 : ETDirect,
2422 : IRHoriz,
2423 : GLBHoriz,
2424 : DirectRad,
2425 : DiffuseRad,
2426 : GLBHorizIllum,
2427 : DirectNrmIllum,
2428 : DiffuseHorizIllum,
2429 : ZenLum,
2430 : WindDir,
2431 : WindSpeed,
2432 : TotalSkyCover,
2433 : OpaqueSkyCover,
2434 : Visibility,
2435 : CeilHeight,
2436 : PresWeathObs,
2437 : PresWeathConds,
2438 : PrecipWater,
2439 : AerosolOptDepth,
2440 : SnowDepth,
2441 : DaysSinceLastSnow,
2442 : Albedo,
2443 : LiquidPrecip);
2444 : }
2445 : }
2446 95151 : if (!WeatherDataLine.good) {
2447 0 : ShowFatalError(state,
2448 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2449 : WYear,
2450 : WMonth,
2451 : WDay,
2452 : WHour,
2453 : WMinute,
2454 0 : state.files.inputWeatherFile.error_state_to_string()),
2455 0 : OptionalOutputFileRef{state.files.eso});
2456 : }
2457 95151 : if (state.dataWeather->CurDayOfWeek <= 7) {
2458 95151 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2459 : }
2460 95151 : bool RecordDateMatch =
2461 189069 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && !thisEnviron.MatchYear) ||
2462 93918 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && thisEnviron.MatchYear && WYear == thisEnviron.StartYear);
2463 95151 : if (RecordDateMatch) {
2464 1233 : state.files.inputWeatherFile.backspace();
2465 1233 : Ready = true;
2466 1233 : if (state.dataWeather->CurDayOfWeek <= 7) {
2467 1233 : --state.dataWeather->CurDayOfWeek;
2468 : }
2469 : // Do the range checks on the first set of fields -- no others.
2470 1233 : bool ErrorsFound = false;
2471 1233 : if (DryBulb < 99.9 && (DryBulb < -90.0 || DryBulb > 70.0)) {
2472 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2473 0 : ShowContinueError(state, format("DryBulb Temperature ({:.2R}) is out of range [-90.0, 70.0]", DryBulb));
2474 0 : ErrorsFound = true;
2475 : }
2476 :
2477 1233 : if (DewPoint < 99.9 && (DewPoint < -90.0 || DewPoint > 70.0)) {
2478 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2479 0 : ShowContinueError(state, format("DewPoint Temperature ({:.2R}) is out of range [-90.0, 70.0]", DewPoint));
2480 0 : ErrorsFound = true;
2481 : }
2482 :
2483 1233 : if (RelHum < 999.0 && (RelHum < 0.0 || RelHum > 110.0)) {
2484 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2485 0 : ShowContinueError(state, format("Relative Humidity ({:.2R}) is out of range [0.0, 100.0]", RelHum));
2486 0 : ErrorsFound = true;
2487 : }
2488 :
2489 1233 : if (AtmPress < 999999.0 && (AtmPress <= 31000.0 || AtmPress > 120000.0)) {
2490 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2491 0 : ShowContinueError(state, format("Atmospheric Pressure ({:.0R}) is out of range [31000, 120000]", AtmPress));
2492 0 : ErrorsFound = true;
2493 : }
2494 :
2495 1233 : if (DirectRad < 0.0) {
2496 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2497 0 : ShowContinueError(state, format("Direct Radiation ({:.2R}) is out of range [0.0, -]", DirectRad));
2498 0 : ErrorsFound = true;
2499 : }
2500 :
2501 1233 : if (DiffuseRad < 0.0) {
2502 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2503 0 : ShowContinueError(state, format("Diffuse Radiation ({:.2R}) is out of range [0.0, -]", DiffuseRad));
2504 0 : ErrorsFound = true;
2505 : }
2506 :
2507 1233 : if (WindDir < 999.0 && (WindDir < 0.0 || WindDir > 360.0)) {
2508 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2509 0 : ShowContinueError(state, format("Wind Direction ({:.2R}) is out of range [0.0, 360.0]", WindDir));
2510 0 : ErrorsFound = true;
2511 : }
2512 :
2513 1233 : if (WindSpeed < 999.0 && (WindSpeed < 0.0 || WindSpeed > 40.0)) {
2514 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2515 0 : ShowContinueError(state, format("Wind Speed ({:.2R}) is out of range [0.0, 40.0]", WindSpeed));
2516 0 : ErrorsFound = true;
2517 : }
2518 :
2519 1233 : if (ErrorsFound) {
2520 0 : ShowSevereError(state, "Out of Range errors found with initial day of WeatherFile");
2521 : }
2522 : } else {
2523 : // Must skip this day
2524 93918 : for (int i = 2; i <= state.dataWeather->NumIntervalsPerHour; ++i) {
2525 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2526 0 : if (!WeatherDataLine.good) {
2527 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2528 0 : ShowFatalError(state,
2529 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2530 : WYear,
2531 : WMonth,
2532 : WDay,
2533 : WHour,
2534 : WMinute,
2535 0 : state.files.inputWeatherFile.error_state_to_string()),
2536 0 : OptionalOutputFileRef{state.files.eso});
2537 : }
2538 : }
2539 2254032 : for (int i = 1; i <= 23 * state.dataWeather->NumIntervalsPerHour; ++i) {
2540 2160114 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2541 2160114 : if (!WeatherDataLine.good) {
2542 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2543 0 : ShowFatalError(state,
2544 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2545 : WYear,
2546 : WMonth,
2547 : WDay,
2548 : WHour,
2549 : WMinute,
2550 0 : state.files.inputWeatherFile.error_state_to_string()),
2551 0 : OptionalOutputFileRef{state.files.eso});
2552 : }
2553 : }
2554 : }
2555 : }
2556 :
2557 1233 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2558 : // Why do some things here use state.dataWeather->Envrn and some the parameter Environ?
2559 :
2560 : // Positioned to proper day
2561 1245 : if (!state.dataGlobal->KickOffSimulation && !state.dataGlobal->DoingSizing &&
2562 12 : thisEnviron.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
2563 7 : ++thisEnviron.CurrentCycle;
2564 7 : if (!thisEnviron.RollDayTypeOnRepeat) {
2565 0 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2566 0 : if (state.dataWeather->DaylightSavingIsActive) {
2567 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2568 : }
2569 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2570 7 : } else if (thisEnviron.CurrentCycle == 1) {
2571 7 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2572 7 : thisEnviron.SetWeekDays = true;
2573 7 : if (state.dataWeather->DaylightSavingIsActive) {
2574 2 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2575 : }
2576 7 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2577 : } else {
2578 0 : state.dataWeather->CurDayOfWeek = state.dataEnvrn->DayOfWeekTomorrow;
2579 : }
2580 : } else {
2581 1226 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2582 : }
2583 1233 : }
2584 :
2585 4224 : bool TryAgain = true;
2586 4224 : bool SkipThisDay = false;
2587 :
2588 8448 : while (TryAgain) {
2589 :
2590 4224 : TryAgain = false;
2591 :
2592 105600 : for (int hour = 1; hour <= 24; ++hour) {
2593 202752 : for (int CurTimeStep = 1; CurTimeStep <= state.dataWeather->NumIntervalsPerHour; ++CurTimeStep) {
2594 101376 : state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour) = WeatherVars();
2595 101376 : auto WeatherDataLine = state.files.inputWeatherFile.readLine();
2596 101376 : if (!WeatherDataLine.good) {
2597 0 : WeatherDataLine.data.clear();
2598 : }
2599 101376 : if (WeatherDataLine.data.empty()) {
2600 0 : if (hour == 1) {
2601 0 : WeatherDataLine.eof = true;
2602 0 : WeatherDataLine.good = false;
2603 : } else {
2604 0 : WeatherDataLine.good = false;
2605 : }
2606 : }
2607 101376 : if (WeatherDataLine.good) {
2608 : bool ErrorFound;
2609 101376 : InterpretWeatherDataLine(state,
2610 : WeatherDataLine.data,
2611 : ErrorFound,
2612 : WYear,
2613 : WMonth,
2614 : WDay,
2615 : WHour,
2616 : WMinute,
2617 : DryBulb,
2618 : DewPoint,
2619 : RelHum,
2620 : AtmPress,
2621 : ETHoriz,
2622 : ETDirect,
2623 : IRHoriz,
2624 : GLBHoriz,
2625 : DirectRad,
2626 : DiffuseRad,
2627 : GLBHorizIllum,
2628 : DirectNrmIllum,
2629 : DiffuseHorizIllum,
2630 : ZenLum,
2631 : WindDir,
2632 : WindSpeed,
2633 : TotalSkyCover,
2634 : OpaqueSkyCover,
2635 : Visibility,
2636 : CeilHeight,
2637 : PresWeathObs,
2638 : PresWeathConds,
2639 : PrecipWater,
2640 : AerosolOptDepth,
2641 : SnowDepth,
2642 : DaysSinceLastSnow,
2643 : Albedo,
2644 : LiquidPrecip);
2645 : } else { // ReadStatus /=0
2646 0 : if (WeatherDataLine.eof &&
2647 0 : state.dataWeather->NumDataPeriods == 1) { // Standard End-of-file, rewind and position to first day...
2648 0 : if (state.dataWeather->DataPeriods(1).NumDays >= state.dataWeather->NumDaysInYear) {
2649 0 : state.files.inputWeatherFile.rewind();
2650 0 : SkipEPlusWFHeader(state);
2651 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2652 : bool ErrorFound;
2653 0 : InterpretWeatherDataLine(state,
2654 : WeatherDataLine.data,
2655 : ErrorFound,
2656 : WYear,
2657 : WMonth,
2658 : WDay,
2659 : WHour,
2660 : WMinute,
2661 : DryBulb,
2662 : DewPoint,
2663 : RelHum,
2664 : AtmPress,
2665 : ETHoriz,
2666 : ETDirect,
2667 : IRHoriz,
2668 : GLBHoriz,
2669 : DirectRad,
2670 : DiffuseRad,
2671 : GLBHorizIllum,
2672 : DirectNrmIllum,
2673 : DiffuseHorizIllum,
2674 : ZenLum,
2675 : WindDir,
2676 : WindSpeed,
2677 : TotalSkyCover,
2678 : OpaqueSkyCover,
2679 : Visibility,
2680 : CeilHeight,
2681 : PresWeathObs,
2682 : PresWeathConds,
2683 : PrecipWater,
2684 : AerosolOptDepth,
2685 : SnowDepth,
2686 : DaysSinceLastSnow,
2687 : Albedo,
2688 : LiquidPrecip);
2689 : } else {
2690 0 : ShowFatalError(state,
2691 0 : format("End-of-File encountered after {}/{}/{} {}:{}, starting from first day of Weather File would "
2692 : "not be \"next day\"",
2693 : WYear,
2694 : WMonth,
2695 : WDay,
2696 : WHour,
2697 : WMinute));
2698 : }
2699 : } else {
2700 0 : ShowFatalError(state,
2701 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2702 : WYear,
2703 : WMonth,
2704 : WDay,
2705 : WHour,
2706 : WMinute),
2707 0 : OptionalOutputFileRef{state.files.eso});
2708 : }
2709 : }
2710 :
2711 101376 : if (hour != WHour) {
2712 0 : ShowFatalError(state,
2713 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2714 : WYear,
2715 : WMonth,
2716 : WDay,
2717 : WHour,
2718 : WMinute),
2719 0 : OptionalOutputFileRef{state.files.eso});
2720 : }
2721 :
2722 : // Set possible missing values
2723 101376 : if (ETHoriz < 0.0) {
2724 0 : ETHoriz = 9999.0;
2725 : }
2726 101376 : if (ETDirect < 0.0) {
2727 0 : ETDirect = 9999.0;
2728 : }
2729 101376 : if (IRHoriz <= 0.0) {
2730 0 : IRHoriz = 9999.0;
2731 : }
2732 101376 : if (GLBHoriz < 0.0) {
2733 0 : GLBHoriz = 9999.0;
2734 : }
2735 101376 : if (state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
2736 0 : if (DirectRad >= 9999.0) {
2737 0 : ++state.dataWeather->wvarsMissedCounts.BeamSolarRad;
2738 : }
2739 0 : if (DiffuseRad >= 9999.0) {
2740 0 : state.dataWeather->wvarsMissedCounts.DifSolarRad = state.dataWeather->wvarsMissedCounts.BeamSolarRad + 1;
2741 : }
2742 0 : if (DirectRad < 0.0) {
2743 0 : DirectRad = 9999.0;
2744 0 : ++state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad;
2745 : }
2746 0 : if (DiffuseRad < 0.0) {
2747 0 : DiffuseRad = 9999.0;
2748 0 : ++state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad;
2749 : }
2750 : }
2751 101376 : if (GLBHorizIllum < 0.0) {
2752 0 : GLBHorizIllum = 999999.0;
2753 : }
2754 101376 : if (DirectNrmIllum < 0.0) {
2755 0 : DirectNrmIllum = 999999.0;
2756 : }
2757 101376 : if (DiffuseHorizIllum < 0.0) {
2758 0 : DiffuseHorizIllum = 999999.0;
2759 : }
2760 101376 : if (ZenLum < 0.0) {
2761 0 : ZenLum = 99999.0;
2762 : }
2763 101376 : if (AtmPress < 0.0) {
2764 0 : AtmPress = 999999.0;
2765 : }
2766 101376 : if (WindSpeed < 0.0) {
2767 0 : WindSpeed = 999.0;
2768 : }
2769 101376 : if (WindDir < -360.0 || WindDir > 360.0) {
2770 0 : WindDir = 999.0;
2771 : }
2772 101376 : if (TotalSkyCover < 0.0) {
2773 0 : TotalSkyCover = 99.0;
2774 : }
2775 101376 : if (RelHum < 0.0) {
2776 0 : RelHum = 999.0;
2777 : }
2778 101376 : if (OpaqueSkyCover < 0.0) {
2779 0 : OpaqueSkyCover = 99.0;
2780 : }
2781 101376 : if (Visibility < 0.0) {
2782 0 : Visibility = 9999.0;
2783 : }
2784 101376 : if (CeilHeight < 0.0) {
2785 0 : CeilHeight = 9999.0;
2786 : }
2787 101376 : if (PresWeathObs < 0) {
2788 0 : PresWeathObs = 9;
2789 : }
2790 101376 : if (PrecipWater < 0.0) {
2791 0 : PrecipWater = 999.0;
2792 : }
2793 101376 : if (AerosolOptDepth < 0.0) {
2794 0 : AerosolOptDepth = 999.0;
2795 : }
2796 101376 : if (SnowDepth < 0.0) {
2797 0 : SnowDepth = 999.0;
2798 : }
2799 101376 : if (DaysSinceLastSnow < 0.0) {
2800 0 : DaysSinceLastSnow = 99.0;
2801 : }
2802 101376 : if (Albedo < 0.0) {
2803 0 : Albedo = 999.0;
2804 : }
2805 101376 : if (LiquidPrecip < 0.0) {
2806 0 : LiquidPrecip = 999.0;
2807 : }
2808 :
2809 101376 : if (hour == 1 && CurTimeStep == 1) {
2810 4224 : if (WMonth == 2 && WDay == 29 && (!state.dataEnvrn->CurrentYearIsLeapYear || !state.dataWeather->WFAllowsLeapYears)) {
2811 0 : state.dataWeather->EndDayOfMonth(2) = 28;
2812 0 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = 28;
2813 0 : SkipThisDay = true;
2814 0 : TryAgain = true;
2815 0 : ShowWarningError(state, "ReadEPlusWeatherForDay: Feb29 data encountered but will not be processed.");
2816 0 : if (!state.dataWeather->WFAllowsLeapYears) {
2817 0 : ShowContinueError(
2818 : state, "...WeatherFile does not allow Leap Years. HOLIDAYS/DAYLIGHT SAVINGS header must indicate \"Yes\".");
2819 : }
2820 0 : continue;
2821 : } else {
2822 4224 : TryAgain = false;
2823 4224 : SkipThisDay = false;
2824 : }
2825 :
2826 4224 : if (thisEnviron.ActualWeather && state.dataEnvrn->CurrentYearIsLeapYear) {
2827 0 : if (WMonth == 3 && WDay == 1 && state.dataEnvrn->Month == 2 && state.dataEnvrn->DayOfMonth == 28) {
2828 0 : ShowFatalError(state, "ReadEPlusWeatherForDay: Current year is a leap year, but Feb29 data is missing.");
2829 : }
2830 : }
2831 :
2832 4224 : state.dataWeather->TomorrowVariables.Year = WYear;
2833 4224 : state.dataWeather->TomorrowVariables.Month = WMonth;
2834 4224 : state.dataWeather->TomorrowVariables.DayOfMonth = WDay;
2835 4224 : state.dataWeather->TomorrowVariables.DayOfYear = General::OrdinalDay(WMonth, WDay, state.dataWeather->LeapYearAdd);
2836 4224 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule = General::OrdinalDay(WMonth, WDay, 1);
2837 : Real64 A;
2838 : Real64 B;
2839 : Real64 C;
2840 : Real64 AVSC;
2841 4224 : CalculateDailySolarCoeffs(state,
2842 4224 : state.dataWeather->TomorrowVariables.DayOfYear,
2843 : A,
2844 : B,
2845 : C,
2846 : AVSC,
2847 4224 : state.dataWeather->TomorrowVariables.EquationOfTime,
2848 4224 : state.dataWeather->TomorrowVariables.SinSolarDeclinAngle,
2849 4224 : state.dataWeather->TomorrowVariables.CosSolarDeclinAngle);
2850 4224 : if (state.dataWeather->CurDayOfWeek <= 7) {
2851 4124 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2852 : }
2853 4224 : state.dataWeather->TomorrowVariables.DayOfWeek = state.dataWeather->CurDayOfWeek;
2854 8448 : state.dataWeather->TomorrowVariables.DaylightSavingIndex =
2855 4224 : state.dataWeather->DSTIndex(state.dataWeather->TomorrowVariables.DayOfYear);
2856 4224 : state.dataWeather->TomorrowVariables.HolidayIndex =
2857 4224 : state.dataWeather->SpecialDayTypes(state.dataWeather->TomorrowVariables.DayOfYear);
2858 : }
2859 :
2860 101376 : if (SkipThisDay) {
2861 0 : continue;
2862 : }
2863 :
2864 : // Check out missing values
2865 :
2866 101376 : if (DryBulb >= 99.9) {
2867 0 : DryBulb = state.dataWeather->wvarsMissing.OutDryBulbTemp;
2868 0 : ++state.dataWeather->wvarsMissedCounts.OutDryBulbTemp;
2869 : }
2870 101376 : if (DryBulb < -90.0 || DryBulb > 70.0) {
2871 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp;
2872 : }
2873 :
2874 101376 : if (DewPoint >= 99.9) {
2875 0 : DewPoint = state.dataWeather->wvarsMissing.OutDewPointTemp;
2876 0 : ++state.dataWeather->wvarsMissedCounts.OutDewPointTemp;
2877 : }
2878 101376 : if (DewPoint < -90.0 || DewPoint > 70.0) {
2879 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp;
2880 : }
2881 :
2882 101376 : if (RelHum >= 999.0) {
2883 0 : RelHum = state.dataWeather->wvarsMissing.OutRelHum;
2884 0 : ++state.dataWeather->wvarsMissedCounts.OutRelHum;
2885 : }
2886 101376 : if (RelHum < 0.0 || RelHum > 110.0) {
2887 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutRelHum;
2888 : }
2889 :
2890 101376 : if (AtmPress >= 999999.0) {
2891 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2892 0 : ++state.dataWeather->wvarsMissedCounts.OutBaroPress;
2893 : }
2894 101376 : if (AtmPress <= 31000.0 || AtmPress > 120000.0) {
2895 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress;
2896 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2897 : }
2898 :
2899 101376 : if (WindDir >= 999.0) {
2900 0 : WindDir = state.dataWeather->wvarsMissing.WindDir;
2901 0 : ++state.dataWeather->wvarsMissedCounts.WindDir;
2902 : }
2903 101376 : if (WindDir < 0.0 || WindDir > 360.0) {
2904 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindDir;
2905 : }
2906 :
2907 101376 : if (WindSpeed >= 999.0) {
2908 0 : WindSpeed = state.dataWeather->wvarsMissing.WindSpeed;
2909 0 : ++state.dataWeather->wvarsMissedCounts.WindSpeed;
2910 : }
2911 101376 : if (WindSpeed < 0.0 || WindSpeed > 40.0) {
2912 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindSpeed;
2913 : }
2914 :
2915 101376 : if (TotalSkyCover >= 99.0) {
2916 0 : TotalSkyCover = state.dataWeather->wvarsMissing.TotalSkyCover;
2917 0 : ++state.dataWeather->wvarsMissedCounts.TotalSkyCover;
2918 : }
2919 :
2920 101376 : if (OpaqueSkyCover >= 99.0) {
2921 0 : OpaqueSkyCover = state.dataWeather->wvarsMissing.OpaqueSkyCover;
2922 0 : ++state.dataWeather->wvarsMissedCounts.OpaqueSkyCover;
2923 : }
2924 :
2925 101376 : if (SnowDepth >= 999.0) {
2926 72 : SnowDepth = state.dataWeather->wvarsMissing.SnowDepth;
2927 72 : ++state.dataWeather->wvarsMissedCounts.SnowDepth;
2928 : }
2929 :
2930 101376 : if (Albedo >= 999.0) {
2931 73992 : Albedo = state.dataWeather->wvarsMissing.Albedo;
2932 73992 : ++state.dataWeather->wvarsMissedCounts.Albedo;
2933 : }
2934 :
2935 101376 : if (LiquidPrecip >= 999.0) {
2936 82566 : LiquidPrecip = state.dataWeather->wvarsMissing.LiquidPrecip;
2937 82566 : ++state.dataWeather->wvarsMissedCounts.LiquidPrecip;
2938 : }
2939 :
2940 101376 : auto &tomorrow = state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour);
2941 101376 : tomorrow.OutDryBulbTemp = DryBulb;
2942 101376 : tomorrow.OutDewPointTemp = DewPoint;
2943 101376 : tomorrow.OutBaroPress = AtmPress;
2944 101376 : tomorrow.OutRelHum = RelHum;
2945 101376 : RelHum *= 0.01;
2946 101376 : tomorrow.WindSpeed = WindSpeed;
2947 101376 : tomorrow.WindDir = WindDir;
2948 101376 : tomorrow.LiquidPrecip = LiquidPrecip;
2949 101376 : tomorrow.TotalSkyCover = TotalSkyCover;
2950 101376 : tomorrow.OpaqueSkyCover = OpaqueSkyCover;
2951 :
2952 101376 : calcSky(state, tomorrow.HorizIRSky, tomorrow.SkyTemp, OpaqueSkyCover, DryBulb, DewPoint, RelHum, IRHoriz);
2953 :
2954 101376 : if (ETHoriz >= 9999.0) {
2955 0 : ETHoriz = 0.0;
2956 : }
2957 101376 : if (ETDirect >= 9999.0) {
2958 48 : ETDirect = 0.0;
2959 : }
2960 101376 : if (GLBHoriz >= 9999.0) {
2961 0 : GLBHoriz = 0.0;
2962 : }
2963 101376 : if (DirectRad >= 9999.0) {
2964 0 : DirectRad = 0.0;
2965 : }
2966 101376 : if (DiffuseRad >= 9999.0) {
2967 0 : DiffuseRad = 0.0;
2968 : }
2969 101376 : if (GLBHorizIllum >= 999900.0) {
2970 96 : GLBHorizIllum = 0.0;
2971 : }
2972 101376 : if (DirectNrmIllum >= 999900.0) {
2973 96 : DirectNrmIllum = 0.0;
2974 : }
2975 101376 : if (DiffuseHorizIllum >= 999900.0) {
2976 96 : DiffuseHorizIllum = 0.0;
2977 : }
2978 101376 : if (ZenLum >= 99990.0) {
2979 142 : ZenLum = 0.0;
2980 : }
2981 101376 : if (state.dataEnvrn->IgnoreSolarRadiation) {
2982 0 : GLBHoriz = 0.0;
2983 0 : DirectRad = 0.0;
2984 0 : DiffuseRad = 0.0;
2985 : }
2986 101376 : if (state.dataEnvrn->IgnoreBeamRadiation) {
2987 0 : DirectRad = 0.0;
2988 : }
2989 101376 : if (state.dataEnvrn->IgnoreDiffuseRadiation) {
2990 0 : DiffuseRad = 0.0;
2991 : }
2992 :
2993 101376 : tomorrow.BeamSolarRad = DirectRad;
2994 101376 : tomorrow.DifSolarRad = DiffuseRad;
2995 :
2996 101376 : tomorrow.IsRain = false;
2997 101376 : if (PresWeathObs == 0) {
2998 144 : if (PresWeathConds(1) < 9 || PresWeathConds(2) < 9 || PresWeathConds(3) < 9) {
2999 0 : tomorrow.IsRain = true;
3000 : }
3001 : } else {
3002 101232 : tomorrow.IsRain = false;
3003 : }
3004 101376 : tomorrow.IsSnow = (SnowDepth > 0.0);
3005 :
3006 : // default if rain but none on weather file
3007 101376 : if (tomorrow.IsRain && tomorrow.LiquidPrecip == 0.0) {
3008 0 : tomorrow.LiquidPrecip = 2.0; // 2mm in an hour ~ .08 inch
3009 : }
3010 :
3011 101376 : state.dataWeather->wvarsMissing.OutDryBulbTemp = DryBulb;
3012 101376 : state.dataWeather->wvarsMissing.OutDewPointTemp = DewPoint;
3013 101376 : state.dataWeather->wvarsMissing.OutRelHum = static_cast<int>(std::round(RelHum * 100.0));
3014 101376 : state.dataWeather->wvarsMissing.OutBaroPress = AtmPress;
3015 101376 : state.dataWeather->wvarsMissing.WindDir = WindDir;
3016 101376 : state.dataWeather->wvarsMissing.WindSpeed = WindSpeed;
3017 101376 : state.dataWeather->wvarsMissing.TotalSkyCover = TotalSkyCover;
3018 101376 : state.dataWeather->wvarsMissing.OpaqueSkyCover = OpaqueSkyCover;
3019 101376 : state.dataWeather->wvarsMissing.Visibility = Visibility;
3020 101376 : state.dataWeather->wvarsMissing.Ceiling = CeilHeight;
3021 101376 : state.dataWeather->wvarsMissing.WaterPrecip = PrecipWater;
3022 101376 : state.dataWeather->wvarsMissing.AerOptDepth = AerosolOptDepth;
3023 101376 : state.dataWeather->wvarsMissing.SnowDepth = SnowDepth;
3024 101376 : state.dataWeather->wvarsMissing.DaysLastSnow = DaysSinceLastSnow;
3025 101376 : state.dataWeather->wvarsMissing.Albedo = Albedo;
3026 :
3027 101376 : } // for (CurTimeStep)
3028 :
3029 : } // for (Hour)
3030 :
3031 : } // Try Again While Loop
3032 :
3033 4224 : if (BackSpaceAfterRead) {
3034 0 : state.files.inputWeatherFile.backspace();
3035 : }
3036 :
3037 4224 : if (state.dataWeather->NumIntervalsPerHour == 1 && state.dataGlobal->TimeStepsInHour > 1) {
3038 : // Create interpolated weather for timestep orientation
3039 : // First copy ts=1 (hourly) from data arrays to Wthr structure
3040 87050 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3041 83568 : wvarsHr(hour) = state.dataWeather->wvarsHrTsTomorrow(1, hour);
3042 : }
3043 :
3044 3482 : if (!state.dataWeather->LastHourSet) {
3045 : // For first day of weather, all time steps of the first hour will be
3046 : // equal to the first hour's value.
3047 : // 2021-06: An additional input is added to here to allow the user to have chosen which hour to use
3048 1219 : int HrUsedtoInterp = thisEnviron.firstHrInterpUseHr1 ? 1 : 24;
3049 1219 : state.dataWeather->wvarsLastHr = wvarsHr(HrUsedtoInterp);
3050 1219 : state.dataWeather->LastHourSet = true;
3051 : }
3052 :
3053 87050 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3054 :
3055 83568 : int NextHr = (hour == Constant::iHoursInDay) ? 1 : hour + 1;
3056 :
3057 83568 : state.dataWeather->wvarsNextHr.BeamSolarRad = wvarsHr(NextHr).BeamSolarRad;
3058 83568 : state.dataWeather->wvarsNextHr.DifSolarRad = wvarsHr(NextHr).DifSolarRad;
3059 83568 : state.dataWeather->wvarsNextHr.LiquidPrecip = wvarsHr(NextHr).LiquidPrecip;
3060 :
3061 500784 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
3062 :
3063 417216 : Real64 wgtCurrHr = state.dataWeather->Interpolation(ts);
3064 417216 : Real64 wgtPrevHr = 1.0 - wgtCurrHr;
3065 :
3066 : // Do Solar "weighting"
3067 :
3068 417216 : Real64 wgtCurrHrSolar = state.dataWeather->SolarInterpolation(ts);
3069 : Real64 wgtPrevHrSolar;
3070 : Real64 wgtNextHrSolar;
3071 :
3072 417216 : if (state.dataGlobal->TimeStepsInHour == 1) {
3073 0 : wgtNextHrSolar = 1.0 - wgtCurrHr;
3074 0 : wgtPrevHrSolar = 0.0;
3075 417216 : } else if (wgtCurrHrSolar == 1.0) {
3076 : // It's at the half hour
3077 83568 : wgtPrevHrSolar = 0.0;
3078 83568 : wgtNextHrSolar = 0.0;
3079 333648 : } else if (ts * state.dataWeather->TimeStepFraction < 0.5) {
3080 125040 : wgtPrevHrSolar = 1.0 - wgtCurrHrSolar;
3081 125040 : wgtNextHrSolar = 0.0;
3082 : } else { // After the half hour
3083 208608 : wgtPrevHrSolar = 0.0;
3084 208608 : wgtNextHrSolar = 1.0 - wgtCurrHrSolar;
3085 : }
3086 :
3087 417216 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
3088 417216 : auto const &wvarsH = wvarsHr(hour);
3089 417216 : tomorrowTs.OutDryBulbTemp = state.dataWeather->wvarsLastHr.OutDryBulbTemp * wgtPrevHr + wvarsH.OutDryBulbTemp * wgtCurrHr;
3090 417216 : tomorrowTs.OutBaroPress = state.dataWeather->wvarsLastHr.OutBaroPress * wgtPrevHr + wvarsH.OutBaroPress * wgtCurrHr;
3091 417216 : tomorrowTs.OutDewPointTemp = state.dataWeather->wvarsLastHr.OutDewPointTemp * wgtPrevHr + wvarsH.OutDewPointTemp * wgtCurrHr;
3092 417216 : tomorrowTs.OutRelHum = state.dataWeather->wvarsLastHr.OutRelHum * wgtPrevHr + wvarsH.OutRelHum * wgtCurrHr;
3093 417216 : tomorrowTs.WindSpeed = state.dataWeather->wvarsLastHr.WindSpeed * wgtPrevHr + wvarsH.WindSpeed * wgtCurrHr;
3094 417216 : tomorrowTs.WindDir = interpolateWindDirection(state.dataWeather->wvarsLastHr.WindDir, wvarsH.WindDir, wgtCurrHr);
3095 417216 : tomorrowTs.TotalSkyCover = state.dataWeather->wvarsLastHr.TotalSkyCover * wgtPrevHr + wvarsH.TotalSkyCover * wgtCurrHr;
3096 417216 : tomorrowTs.OpaqueSkyCover = state.dataWeather->wvarsLastHr.OpaqueSkyCover * wgtPrevHr + wvarsH.OpaqueSkyCover * wgtCurrHr;
3097 : // Sky emissivity now takes interpolated timestep inputs rather than interpolated calculation esky results
3098 417216 : calcSky(state,
3099 417216 : tomorrowTs.HorizIRSky,
3100 417216 : tomorrowTs.SkyTemp,
3101 : tomorrowTs.OpaqueSkyCover,
3102 : tomorrowTs.OutDryBulbTemp,
3103 : tomorrowTs.OutDewPointTemp,
3104 417216 : tomorrowTs.OutRelHum * 0.01,
3105 417216 : state.dataWeather->wvarsLastHr.HorizIRSky * wgtPrevHr + wvarsH.HorizIRSky * wgtCurrHr);
3106 :
3107 417216 : tomorrowTs.DifSolarRad = state.dataWeather->wvarsLastHr.DifSolarRad * wgtPrevHrSolar + wvarsH.DifSolarRad * wgtCurrHrSolar +
3108 417216 : state.dataWeather->wvarsNextHr.DifSolarRad * wgtNextHrSolar;
3109 417216 : tomorrowTs.BeamSolarRad = state.dataWeather->wvarsLastHr.BeamSolarRad * wgtPrevHrSolar + wvarsH.BeamSolarRad * wgtCurrHrSolar +
3110 417216 : state.dataWeather->wvarsNextHr.BeamSolarRad * wgtNextHrSolar;
3111 :
3112 417216 : tomorrowTs.LiquidPrecip = state.dataWeather->wvarsLastHr.LiquidPrecip * wgtPrevHr + wvarsH.LiquidPrecip * wgtCurrHr;
3113 417216 : tomorrowTs.LiquidPrecip /= double(state.dataGlobal->TimeStepsInHour);
3114 417216 : tomorrowTs.IsRain = tomorrowTs.LiquidPrecip >= state.dataWeather->IsRainThreshold; // Wthr%IsRain
3115 417216 : tomorrowTs.IsSnow = wvarsH.IsSnow;
3116 : } // End of TS Loop
3117 :
3118 83568 : state.dataWeather->wvarsLastHr = wvarsHr(hour);
3119 : } // End of Hour Loop
3120 : }
3121 :
3122 4224 : if (thisEnviron.WP_Type1 != 0) {
3123 0 : switch (state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).skyTempModel) {
3124 0 : case SkyTempModel::ScheduleValue: {
3125 : std::vector<Real64> const &dayVals =
3126 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3127 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3128 :
3129 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3130 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3131 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3132 : }
3133 : }
3134 0 : } break;
3135 :
3136 0 : case SkyTempModel::DryBulbDelta: {
3137 : std::vector<Real64> const &dayVals =
3138 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3139 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3140 :
3141 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3142 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3143 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3144 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3145 : }
3146 : }
3147 0 : } break;
3148 :
3149 0 : case SkyTempModel::DewPointDelta: {
3150 : std::vector<Real64> const &dayVals =
3151 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3152 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3153 :
3154 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3155 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3156 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3157 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3158 : }
3159 : }
3160 0 : } break;
3161 :
3162 0 : default:
3163 0 : break;
3164 : }
3165 : }
3166 4224 : }
3167 :
3168 417216 : Real64 interpolateWindDirection(Real64 const prevHrWindDir, Real64 const curHrWindDir, Real64 const curHrWeight)
3169 : {
3170 : // adapted from http://stackoverflow.com/questions/2708476/rotation-interpolation
3171 417216 : Real64 curAng = curHrWindDir;
3172 417216 : Real64 prevAng = prevHrWindDir;
3173 417216 : Real64 diff = std::abs(curAng - prevAng);
3174 417216 : if (diff > 180.) {
3175 33826 : if (curAng > prevAng) {
3176 14854 : prevAng += 360.;
3177 : } else {
3178 18972 : curAng += 360.;
3179 : }
3180 : }
3181 417216 : Real64 interpAng = prevAng + (curAng - prevAng) * curHrWeight;
3182 417216 : return (fmod(interpAng, 360.)); // fmod is float modulus function
3183 : }
3184 :
3185 784512 : Real64 CalcSkyEmissivity(
3186 : EnergyPlusData &state, SkyTempModel const ESkyCalcType, Real64 const OSky, Real64 const DryBulb, Real64 const DewPoint, Real64 const RelHum)
3187 : {
3188 : // Calculate Sky Emissivity
3189 : // References:
3190 : // M. Li, Y. Jiang and C. F. M. Coimbra,
3191 : // "On the determination of atmospheric longwave irradiance under all-sky conditions,"
3192 : // Solar Energy 144, 2017, pp. 40–48,
3193 : // G. Clark and C. Allen, "The Estimation of Atmospheric Radiation for Clear and
3194 : // Cloudy Skies," Proc. 2nd National Passive Solar Conference (AS/ISES), 1978, pp. 675-678.
3195 :
3196 : Real64 ESky;
3197 :
3198 784512 : if (ESkyCalcType == SkyTempModel::Brunt) {
3199 0 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3200 0 : ESky = 0.618 + 0.056 * pow(PartialPress, 0.5);
3201 784512 : } else if (ESkyCalcType == SkyTempModel::Idso) {
3202 0 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3203 0 : ESky = 0.685 + 0.000032 * PartialPress * exp(1699 / (DryBulb + Constant::Kelvin));
3204 784512 : } else if (ESkyCalcType == SkyTempModel::BerdahlMartin) {
3205 0 : double const TDewC = min(DryBulb, DewPoint);
3206 0 : ESky = 0.758 + 0.521 * (TDewC / 100) + 0.625 * pow_2(TDewC / 100);
3207 : } else {
3208 784512 : ESky = 0.787 + 0.764 * std::log((min(DryBulb, DewPoint) + Constant::Kelvin) / Constant::Kelvin);
3209 : }
3210 784512 : return ESky * (1.0 + 0.0224 * OSky - 0.0035 * pow_2(OSky) + 0.00028 * pow_3(OSky));
3211 : }
3212 :
3213 1233 : void SetDayOfWeekInitialValues(int const EnvironDayOfWeek, // Starting Day of Week for the (Weather) RunPeriod (User Input)
3214 : int ¤tDayOfWeek // Current Day of Week
3215 : )
3216 : {
3217 :
3218 : // SUBROUTINE INFORMATION:
3219 : // AUTHOR Linda Lawrie
3220 : // DATE WRITTEN March 2012
3221 :
3222 : // PURPOSE OF THIS SUBROUTINE:
3223 : // Set of begin day of week for an environment. Similar sets but slightly different
3224 : // conditions. Improve code readability by having three routine calls instead of three
3225 : // IF blocks.
3226 :
3227 1233 : if (EnvironDayOfWeek != 0) {
3228 1233 : if (EnvironDayOfWeek <= 7) {
3229 1211 : currentDayOfWeek = EnvironDayOfWeek - 1;
3230 : } else {
3231 22 : currentDayOfWeek = EnvironDayOfWeek;
3232 : }
3233 : }
3234 1233 : }
3235 :
3236 0 : void ErrorInterpretWeatherDataLine(EnergyPlusData &state,
3237 : int const WYear,
3238 : int const WMonth,
3239 : int const WDay,
3240 : int const WHour,
3241 : int const WMinute,
3242 : std::string_view SaveLine,
3243 : std::string_view Line)
3244 : {
3245 0 : ShowSevereError(state, fmt::format("Invalid Weather Line at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3246 0 : ShowContinueError(state, fmt::format("Full Data Line={}", SaveLine));
3247 0 : ShowContinueError(state, fmt::format("Remainder of line={}", Line));
3248 0 : ShowFatalError(state, "Error in Reading Weather Data");
3249 0 : }
3250 :
3251 284127 : void InterpretWeatherDataLine(EnergyPlusData &state,
3252 : std::string_view Line,
3253 : bool &ErrorFound, // True if an error is found, false otherwise
3254 : int &WYear,
3255 : int &WMonth,
3256 : int &WDay,
3257 : int &WHour,
3258 : int &WMinute,
3259 : Real64 &DryBulb,
3260 : Real64 &DewPoint,
3261 : Real64 &RelHum,
3262 : Real64 &AtmPress,
3263 : Real64 Ðoriz,
3264 : Real64 &ETDirect,
3265 : Real64 &IRHoriz,
3266 : Real64 &GLBHoriz,
3267 : Real64 &DirectRad,
3268 : Real64 &DiffuseRad,
3269 : Real64 &GLBHorizIllum,
3270 : Real64 &DirectNrmIllum,
3271 : Real64 &DiffuseHorizIllum,
3272 : Real64 &ZenLum,
3273 : Real64 &WindDir,
3274 : Real64 &WindSpeed,
3275 : Real64 &TotalSkyCover,
3276 : Real64 &OpaqueSkyCover,
3277 : Real64 &Visibility,
3278 : Real64 &CeilHeight,
3279 : int &WObs, // PresWeathObs
3280 : Array1D_int &WCodesArr, // PresWeathConds
3281 : Real64 &PrecipWater,
3282 : Real64 &AerosolOptDepth,
3283 : Real64 &SnowDepth,
3284 : Real64 &DaysSinceLastSnow,
3285 : Real64 &Albedo,
3286 : Real64 &LiquidPrecip)
3287 : {
3288 :
3289 : // SUBROUTINE INFORMATION:
3290 : // AUTHOR Linda Lawrie
3291 : // DATE WRITTEN April 2001
3292 :
3293 : // PURPOSE OF THIS SUBROUTINE:
3294 : // This subroutine interprets the EPW weather data line because comma delimited fields
3295 : // may cause problems with some compilers. (Particularly character variables in
3296 : // comma delimited lines.
3297 :
3298 : // METHODOLOGY EMPLOYED:
3299 : // Field by field interpretation, eliminating the "data source field" which is also
3300 : // likely to contain blanks. Note that the "Weatherconditions" must be a 9 character
3301 : // alpha field with no intervening blanks.
3302 :
3303 284127 : EP_SIZE_CHECK(WCodesArr, 9); // NOLINT(misc-static-assert)
3304 :
3305 : static constexpr std::string_view ValidDigits("0123456789");
3306 :
3307 284127 : std::string_view::size_type pos = 0;
3308 284127 : std::string_view current_line = Line;
3309 :
3310 284127 : ErrorFound = false;
3311 :
3312 : // Do the first five. (To get to the DataSource field)
3313 : {
3314 284127 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 5); // Returns the position **after** the nth occurrence of ','
3315 284127 : const bool succeeded = readList(current_line.substr(pos, (nth_pos - 1) - pos), WYear, WMonth, WDay, WHour, WMinute);
3316 284127 : if (!succeeded) {
3317 0 : ShowSevereError(state, "Invalid Date info in Weather Line");
3318 0 : ShowContinueError(state, fmt::format("Entire Data Line={}", Line));
3319 0 : ShowFatalError(state, "Error in Reading Weather Data");
3320 : }
3321 : }
3322 :
3323 284127 : bool DateInError = false;
3324 284127 : if (WMonth >= 1 && WMonth <= 12) {
3325 : // Month number is valid
3326 284127 : if (WMonth != 2) {
3327 257223 : if (WDay > state.dataWeather->EndDayOfMonth(WMonth)) {
3328 0 : DateInError = true;
3329 : }
3330 26904 : } else if (WDay > state.dataWeather->EndDayOfMonth(WMonth) + 1) { // Whether actually used is determined by calling routine.
3331 0 : DateInError = true;
3332 : }
3333 : } else {
3334 0 : DateInError = true;
3335 : }
3336 :
3337 284127 : if (DateInError) {
3338 0 : ShowSevereError(state, format("Reading Weather Data Line, Invalid Date, Year={}, Month={}, Day={}", WYear, WMonth, WDay));
3339 0 : ShowFatalError(state, "Program terminates due to previous condition.");
3340 : }
3341 :
3342 : // index, unlike nth_occurrence returns the position of the search char, not the position after it
3343 284127 : pos = index(Line, ','); // WYear
3344 284127 : if (pos == std::string::npos) {
3345 0 : ShowSevereError(
3346 0 : state, format("Invalid Weather Line (no commas) at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3347 0 : ShowContinueError(state, fmt::format("Full Data Line={}", Line));
3348 0 : ShowFatalError(state, "Error in Reading Weather Data");
3349 : }
3350 284127 : current_line.remove_prefix(nth_occurrence(Line, ',', 6)); // remove WYear,WMonth,WDay,WHour,WMinute,Data Source/Integrity
3351 :
3352 : // Now read more numerics with List Directed I/O (note there is another "character" field lurking)
3353 : Real64 RField21;
3354 : {
3355 284127 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 21);
3356 :
3357 284127 : const bool succeeded = readList(current_line.substr(0, nth_pos - 1),
3358 : DryBulb,
3359 : DewPoint,
3360 : RelHum,
3361 : AtmPress,
3362 : ETHoriz,
3363 : ETDirect,
3364 : IRHoriz,
3365 : GLBHoriz,
3366 : DirectRad,
3367 : DiffuseRad,
3368 : GLBHorizIllum,
3369 : DirectNrmIllum,
3370 : DiffuseHorizIllum,
3371 : ZenLum,
3372 : WindDir,
3373 : WindSpeed,
3374 : TotalSkyCover,
3375 : OpaqueSkyCover,
3376 : Visibility,
3377 : CeilHeight,
3378 : RField21);
3379 :
3380 284127 : if (!succeeded) {
3381 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3382 : }
3383 284127 : current_line.remove_prefix(nth_pos);
3384 : }
3385 284127 : pos = index(current_line, ',');
3386 284127 : std::string PresWeathCodes;
3387 284127 : if (pos != std::string::npos && pos != 0) {
3388 284127 : PresWeathCodes = current_line.substr(0, pos);
3389 : } else {
3390 0 : PresWeathCodes = "999999999";
3391 : }
3392 284127 : current_line.remove_prefix(pos + 1);
3393 :
3394 284127 : auto readNextNumber = // (AUTO_OK_LAMBDA)
3395 1704762 : [reachedEndOfCommands = false, &state, &WYear, &WMonth, &WDay, &WHour, &WMinute, &Line, ¤t_line]() mutable -> Real64 {
3396 1704762 : if (reachedEndOfCommands) {
3397 100 : return 999.0;
3398 : }
3399 : Real64 target;
3400 1704662 : std::string_view::size_type pos = index(current_line, ',');
3401 : // We found a comma
3402 1704662 : if (pos != std::string::npos) {
3403 : // Content is not empty
3404 1704637 : if (pos != 0) {
3405 1704637 : bool error = false;
3406 1704637 : target = Util::ProcessNumber(current_line.substr(0, pos), error);
3407 1704637 : if (error) {
3408 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3409 : }
3410 : } else {
3411 0 : target = 999.0;
3412 : }
3413 1704637 : current_line.remove_prefix(pos + 1);
3414 : } else {
3415 : // Couldn't find next comma, but we need to process the potential current number
3416 25 : reachedEndOfCommands = true;
3417 25 : if (current_line.empty()) {
3418 0 : target = 999.0;
3419 : } else {
3420 25 : bool error = false;
3421 25 : target = Util::ProcessNumber(current_line, error);
3422 25 : if (error) {
3423 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3424 : }
3425 : }
3426 : }
3427 1704662 : return target;
3428 284127 : };
3429 :
3430 284127 : PrecipWater = readNextNumber();
3431 284127 : AerosolOptDepth = readNextNumber();
3432 284127 : SnowDepth = readNextNumber();
3433 284127 : DaysSinceLastSnow = readNextNumber();
3434 284127 : Albedo = readNextNumber();
3435 284127 : LiquidPrecip = readNextNumber();
3436 :
3437 284127 : WObs = nint(RField21);
3438 284127 : if (WObs == 0) { // Obs Indicator indicates Weather Codes valid
3439 : // Check for miscellaneous characters
3440 350 : pos = index(PresWeathCodes, '\'');
3441 350 : while (pos != std::string::npos) {
3442 0 : PresWeathCodes[pos] = ' ';
3443 0 : pos = index(PresWeathCodes, '\'');
3444 : }
3445 350 : pos = index(PresWeathCodes, '"');
3446 350 : while (pos != std::string::npos) {
3447 0 : PresWeathCodes[pos] = ' ';
3448 0 : pos = index(PresWeathCodes, '"');
3449 : }
3450 350 : strip(PresWeathCodes);
3451 350 : if (len(PresWeathCodes) == 9) {
3452 3500 : for (pos = 0; pos < 9; ++pos) {
3453 3150 : if (!has(ValidDigits, PresWeathCodes[pos])) {
3454 0 : PresWeathCodes[pos] = '9';
3455 : }
3456 : }
3457 :
3458 : // we are trying to read a string of 9 integers with no spaces, each
3459 : // into its own integer, like:
3460 : // "123456789"
3461 : // becomes
3462 : // std::vector<int>{1,2,3,4,5,6,7,8,9};
3463 350 : std::stringstream reader = stringReader(PresWeathCodes);
3464 3500 : for (auto &value : WCodesArr) {
3465 3150 : char c[2] = {0, 0}; // a string of 2 characters, init both to 0
3466 3150 : reader >> c[0]; // read next char into the first byte
3467 3150 : value = std::atoi(c); // convert this short string into the appropriate int to read
3468 : }
3469 350 : } else {
3470 0 : ++state.dataWeather->wvarsMissedCounts.WeathCodes;
3471 0 : WCodesArr = 9;
3472 : }
3473 : } else {
3474 283777 : WCodesArr = 9;
3475 : }
3476 284127 : }
3477 :
3478 5985 : void SetUpDesignDay(EnergyPlusData &state, int const EnvrnNum) // Environment number passed into the routine
3479 : {
3480 :
3481 : // SUBROUTINE INFORMATION:
3482 : // AUTHOR Linda Lawrie
3483 : // DATE WRITTEN February 1977
3484 : // MODIFIED June 1997 (RKS); May 2013 (LKL) add temperature profile for drybulb.
3485 : // RE-ENGINEERED August 2003;LKL -- to generate timestep weather for design days.
3486 :
3487 : // PURPOSE OF THIS SUBROUTINE:
3488 : // This purpose of this subroutine is to convert the user supplied input
3489 : // values for the design day parameters into an entire weather day
3490 : // record. This now bypasses any file I/O by keeping all of the
3491 : // weather day record information in the local module level derived type
3492 : // called DesignDay.
3493 :
3494 5985 : constexpr Real64 GlobalSolarConstant = 1367.0;
3495 5985 : constexpr Real64 ZHGlobalSolarConstant = 1355.0;
3496 :
3497 5985 : Real64 constexpr ZhangHuang_C0 = 0.5598; // 37.6865d0
3498 5985 : Real64 constexpr ZhangHuang_C1 = 0.4982; // 13.9263d0
3499 5985 : Real64 constexpr ZhangHuang_C2 = -0.6762; // -20.2354d0
3500 5985 : Real64 constexpr ZhangHuang_C3 = 0.02842; // 0.9695d0
3501 5985 : Real64 constexpr ZhangHuang_C4 = -0.00317; // -0.2046d0
3502 5985 : Real64 constexpr ZhangHuang_C5 = 0.014; // -0.0980d0
3503 5985 : Real64 constexpr ZhangHuang_D = -17.853; // -10.8568d0
3504 5985 : Real64 constexpr ZhangHuang_K = 0.843; // 49.3112d0
3505 : static constexpr std::string_view RoutineNamePsyWFnTdbTwbPb("SetUpDesignDay:PsyWFnTdbTwbPb");
3506 : static constexpr std::string_view RoutineNamePsyWFnTdpPb("SetUpDesignDay:PsyWFnTdpPb");
3507 : static constexpr std::string_view RoutineNamePsyWFnTdbH("SetUpDesignDay:PsyWFnTdbH");
3508 : static constexpr std::string_view WeatherManager("WeatherManager");
3509 : static constexpr std::string_view RoutineNameLong("WeatherManager.cc subroutine SetUpDesignDay");
3510 :
3511 5985 : std::string StringOut;
3512 : // For reporting purposes, set year to current system year
3513 :
3514 : struct HourlyWeatherData
3515 : {
3516 : // Members
3517 : Array1D<Real64> BeamSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly direct normal solar irradiance
3518 : Array1D<Real64> DifSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly sky diffuse horizontal solar irradiance
3519 : };
3520 :
3521 : // Object Data
3522 5985 : HourlyWeatherData Wthr;
3523 :
3524 5985 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
3525 :
3526 5985 : bool SaveWarmupFlag = state.dataGlobal->WarmupFlag;
3527 5985 : state.dataGlobal->WarmupFlag = true;
3528 :
3529 5985 : Array1D_int Date0(8);
3530 5985 : date_and_time(_, _, _, Date0);
3531 5985 : int CurrentYear = Date0(1);
3532 :
3533 5985 : if (state.dataGlobal->BeginSimFlag) {
3534 801 : state.dataWeather->PrintDDHeader = true;
3535 : }
3536 :
3537 5985 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
3538 5985 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
3539 5985 : designDay.Year = CurrentYear; // f90 date_and_time implemented. full 4 digit year !+ 1900
3540 5985 : designDay.Month = desDayInput.Month;
3541 5985 : designDay.DayOfMonth = desDayInput.DayOfMonth;
3542 5985 : designDay.DayOfYear = General::OrdinalDay(designDay.Month, designDay.DayOfMonth, 0);
3543 : static constexpr std::string_view MnDyFmt("{:02}/{:02}");
3544 5985 : state.dataEnvrn->CurMnDy = format(MnDyFmt, desDayInput.Month, desDayInput.DayOfMonth);
3545 : // EnvironmentName = DesDayInput( EnvrnNum ).Title;
3546 5985 : state.dataEnvrn->RunPeriodEnvironment = false;
3547 : // Following builds Environment start/end for ASHRAE 55 warnings
3548 5985 : state.dataEnvrn->EnvironmentStartEnd = state.dataEnvrn->CurMnDy + " - " + state.dataEnvrn->CurMnDy;
3549 :
3550 : // Check that barometric pressure is within range
3551 5985 : if (desDayInput.PressureEntered) {
3552 5961 : if (std::abs((desDayInput.PressBarom - state.dataEnvrn->StdBaroPress) / state.dataEnvrn->StdBaroPress) > 0.1) { // 10% off
3553 108 : ShowWarningError(state,
3554 108 : format("SetUpDesignDay: Entered DesignDay Barometric Pressure={:.0R} differs by more than 10% from Standard "
3555 : "Barometric Pressure={:.0R}.",
3556 54 : desDayInput.PressBarom,
3557 54 : state.dataEnvrn->StdBaroPress));
3558 108 : ShowContinueError(
3559 : state,
3560 108 : format("...occurs in DesignDay={}, Standard Pressure (based on elevation) will be used.", state.dataEnvrn->EnvironmentName));
3561 54 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3562 : }
3563 : } else {
3564 24 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3565 : }
3566 :
3567 : // verify that design WB or DP <= design DB
3568 5985 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint && desDayInput.DewPointNeedsSet) {
3569 : // dew-point
3570 0 : Real64 testval = Psychrometrics::PsyWFnTdbRhPb(state, desDayInput.MaxDryBulb, 1.0, desDayInput.PressBarom);
3571 0 : desDayInput.HumIndValue = Psychrometrics::PsyTdpFnWPb(state, testval, desDayInput.PressBarom);
3572 : }
3573 :
3574 : // Day of week defaults to Monday, if day type specified, then that is used.
3575 5985 : designDay.DayOfWeek = 2;
3576 5985 : if (desDayInput.DayType <= 7) {
3577 52 : designDay.DayOfWeek = desDayInput.DayType;
3578 : }
3579 :
3580 : // set Holiday as indicated by user input
3581 5985 : designDay.HolidayIndex = 0;
3582 5985 : if (desDayInput.DayType > 7) {
3583 5933 : designDay.HolidayIndex = desDayInput.DayType;
3584 : }
3585 :
3586 5985 : designDay.DaylightSavingIndex = desDayInput.DSTIndicator;
3587 :
3588 : // Set up Solar parameters for day
3589 : Real64 A; // Apparent solar irradiation at air mass = 0
3590 : Real64 B; // Atmospheric extinction coefficient
3591 : Real64 C; // ASHRAE diffuse radiation factor
3592 : Real64 AVSC; // Annual variation in the solar constant
3593 5985 : CalculateDailySolarCoeffs(
3594 5985 : state, designDay.DayOfYear, A, B, C, AVSC, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle);
3595 :
3596 5985 : if (state.dataWeather->PrintDDHeader && state.dataReportFlag->DoWeatherInitReporting) {
3597 : static constexpr std::string_view EnvDDHdFormat(
3598 : "! <Environment:Design Day Data>, Max Dry-Bulb Temp {C}, Temp Range {dC}, Temp Range Ind Type, "
3599 : "Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units, Pressure {Pa}, Wind Direction {deg CW from N}, Wind "
3600 : "Speed {m/s}, Clearness, Rain, Snow");
3601 793 : print(state.files.eio, "{}\n", EnvDDHdFormat);
3602 : static constexpr std::string_view DDayMiscHdFormat(
3603 : "! <Environment:Design Day Misc>,DayOfYear,ASHRAE A Coeff,ASHRAE B Coeff,ASHRAE C Coeff,Solar "
3604 : "Constant-Annual Variation,Eq of Time {minutes}, Solar Declination Angle {deg}, Solar Model");
3605 793 : print(state.files.eio, "{}\n", DDayMiscHdFormat);
3606 793 : state.dataWeather->PrintDDHeader = false;
3607 : }
3608 5985 : if (state.dataReportFlag->DoWeatherInitReporting) {
3609 1775 : std::string_view const AlpUseRain = (desDayInput.RainInd == 1) ? "Yes" : "No";
3610 1775 : std::string_view const AlpUseSnow = (desDayInput.SnowInd == 1) ? "Yes" : "No";
3611 1775 : print(state.files.eio, "Environment:Design Day Data,");
3612 1775 : print(state.files.eio, "{:.2R},", desDayInput.MaxDryBulb);
3613 1775 : print(state.files.eio, "{:.2R},", desDayInput.DailyDBRange);
3614 :
3615 : static constexpr std::array<std::string_view, (int)DesDayDryBulbRangeType::Num> DesDayDryBulbRangeTypeStrings = {
3616 : "DefaultMultipliers,", "MultiplierSchedule,", "DifferenceSchedule,", "TemperatureProfile,"};
3617 :
3618 1775 : print(state.files.eio, "{}", DesDayDryBulbRangeTypeStrings[(int)desDayInput.dryBulbRangeType]);
3619 :
3620 : static constexpr std::array<std::string_view, (int)DesDayHumIndType::Num> DesDayHumIndTypeStrings = {
3621 : "Wetbulb,{:.2R},{{C}},",
3622 : "Dewpoint,{:.2R},{{C}},",
3623 : "Enthalpy,{:.2R},{{J/kgDryAir}},",
3624 : "HumidityRatio,{:.4R},{{kgWater/kgDryAir}},",
3625 : "Schedule,<schedule values from 0.0 to 100.0>,{{percent}},",
3626 : "WetBulbProfileDefaultMultipliers,{:.2R},{{C}},",
3627 : "WetBulbProfileDifferenceSchedule,{:.2R},{{C}},",
3628 : "WetBulbProfileMultiplierSchedule,{:.2R},{{C}},"};
3629 :
3630 : // Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units
3631 1775 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
3632 3 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType]);
3633 1772 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
3634 12 : print(state.files.eio,
3635 12 : DesDayHumIndTypeStrings[(int)desDayInput.HumIndType],
3636 12 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).HumIndValue);
3637 : } else {
3638 1760 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType], desDayInput.HumIndValue);
3639 : }
3640 :
3641 1775 : print(state.files.eio, "{:.0R},", desDayInput.PressBarom);
3642 1775 : print(state.files.eio, "{:.0R},", desDayInput.WindDir);
3643 1775 : print(state.files.eio, "{:.1R},", desDayInput.WindSpeed);
3644 1775 : print(state.files.eio, "{:.2R},", desDayInput.SkyClear);
3645 :
3646 1775 : print(state.files.eio, "{},{}\n", AlpUseRain, AlpUseSnow);
3647 :
3648 : static constexpr std::string_view DDayMiscFormat("Environment:Design Day Misc,{:3},");
3649 1775 : print(state.files.eio, DDayMiscFormat, designDay.DayOfYear);
3650 1775 : print(state.files.eio, "{:.1R},", A);
3651 1775 : print(state.files.eio, "{:.4R},", B);
3652 1775 : print(state.files.eio, "{:.4R},", C);
3653 1775 : print(state.files.eio, "{:.1R},", AVSC);
3654 1775 : print(state.files.eio, "{:.2R},", designDay.EquationOfTime * 60.0);
3655 1775 : print(state.files.eio, "{:.1R},", std::asin(designDay.SinSolarDeclinAngle) / Constant::DegToRad);
3656 :
3657 : // Why have a different string for "Schedule" here than the one used for input? Really, why?
3658 : static constexpr std::array<std::string_view, (int)DesDaySolarModel::Num> DesDaySolarModelStrings = {
3659 : "ASHRAEClearSky", "ZhangHuang", "User supplied beam/diffuse from schedules", "ASHRAETau", "ASHRAETau2017"};
3660 :
3661 1775 : print(state.files.eio, "{}\n", DesDaySolarModelStrings[(int)desDayInput.solarModel]);
3662 : }
3663 :
3664 : // Must set up weather values for Design Day. User can specify the "humidity indicator" as
3665 : // Wetbulb, DewPoint or input the relative humidity schedule. For both wetbulb and dewpoint indicators, the
3666 : // humidity for the day will be constant, using the drybulb (max) and humidity indicator temperature to
3667 : // set the values. For the scheduled values, these are already set in the DDxxx array.
3668 :
3669 5985 : state.dataGlobal->CurrentTime = 25.0;
3670 : Real64 HumidityRatio; // Humidity Ratio -- when constant for day
3671 : bool ConstantHumidityRatio;
3672 :
3673 5985 : switch (desDayInput.HumIndType) {
3674 5909 : case DesDayHumIndType::WetBulb: {
3675 5909 : HumidityRatio = Psychrometrics::PsyWFnTdbTwbPb(
3676 : state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdbTwbPb);
3677 5909 : ConstantHumidityRatio = true;
3678 5909 : } break;
3679 25 : case DesDayHumIndType::DewPoint: {
3680 25 : HumidityRatio = Psychrometrics::PsyWFnTdpPb(state, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdpPb);
3681 25 : ConstantHumidityRatio = true;
3682 25 : } break;
3683 0 : case DesDayHumIndType::HumRatio: {
3684 0 : HumidityRatio = desDayInput.HumIndValue;
3685 0 : ConstantHumidityRatio = true;
3686 0 : } break;
3687 15 : case DesDayHumIndType::Enthalpy: {
3688 : // HumIndValue is already in J/kg, so no conversions needed
3689 15 : HumidityRatio = Psychrometrics::PsyWFnTdbH(state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, RoutineNamePsyWFnTdbH);
3690 15 : ConstantHumidityRatio = true;
3691 15 : } break;
3692 6 : case DesDayHumIndType::RelHumSch: {
3693 : // nothing to do -- DDHumIndModifier already contains the scheduled Relative Humidity
3694 6 : ConstantHumidityRatio = false;
3695 150 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3696 720 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3697 576 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).OutRelHum =
3698 576 : state.dataWeather->desDayMods(EnvrnNum)(ts + 1, hr + 1).OutRelHum;
3699 : }
3700 : }
3701 6 : } break;
3702 30 : case DesDayHumIndType::WBProfDef:
3703 : case DesDayHumIndType::WBProfDif:
3704 : case DesDayHumIndType::WBProfMul: {
3705 30 : ConstantHumidityRatio = false;
3706 30 : } break;
3707 0 : default: {
3708 0 : ShowSevereError(state, "SetUpDesignDay: Invalid Humidity Indicator type");
3709 0 : ShowContinueError(state, format("Occurred in Design Day={}", desDayInput.Title));
3710 0 : } break;
3711 : } // switch
3712 :
3713 : int OSky; // Opaque Sky Cover (tenths)
3714 5985 : if (desDayInput.RainInd != 0) {
3715 0 : OSky = 10;
3716 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3717 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3718 0 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3719 0 : wvars.IsRain = true;
3720 0 : wvars.LiquidPrecip = 3.0;
3721 : }
3722 : }
3723 : } else {
3724 5985 : OSky = 0;
3725 149625 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3726 928152 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3727 784512 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3728 784512 : wvars.IsRain = false;
3729 784512 : wvars.LiquidPrecip = 0.0;
3730 : }
3731 : }
3732 : }
3733 :
3734 : Real64 GndReflet; // Ground Reflectivity
3735 5985 : if (desDayInput.SnowInd == 0) {
3736 5985 : GndReflet = 0.2;
3737 149625 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3738 928152 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3739 784512 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3740 784512 : wvars.IsSnow = false;
3741 : }
3742 : }
3743 : } else { // Snow
3744 0 : GndReflet = 0.7;
3745 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3746 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3747 0 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3748 0 : wvars.IsSnow = true;
3749 : }
3750 : }
3751 : }
3752 :
3753 : // Some values are constant
3754 :
3755 149625 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3756 928152 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3757 784512 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3758 784512 : wvars.OutBaroPress = desDayInput.PressBarom;
3759 784512 : wvars.WindSpeed = desDayInput.WindSpeed;
3760 784512 : wvars.WindDir = desDayInput.WindDir;
3761 784512 : wvars.Albedo = 0.0;
3762 : }
3763 : }
3764 :
3765 : // resolve daily ranges
3766 : Real64 DBRange; // working copy of dry-bulb daily range, C (or 1 if input is difference)
3767 5985 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
3768 4 : DBRange = 1.0; // use unscaled multiplier values if difference
3769 5981 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
3770 2 : DBRange = 0.0;
3771 : } else {
3772 5979 : DBRange = desDayInput.DailyDBRange;
3773 : }
3774 : Real64 WBRange; // working copy of wet-bulb daily range. C (or 1 if input is difference)
3775 5985 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
3776 2 : WBRange = 1.0; // use unscaled multiplier values if difference
3777 : } else {
3778 5983 : WBRange = desDayInput.DailyWBRange;
3779 : }
3780 :
3781 5985 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3782 149625 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3783 928152 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
3784 784512 : auto const &desDayModsTS = desDayModsEnvrn(ts, hour);
3785 784512 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
3786 784512 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
3787 : // dry-bulb profile
3788 784320 : tomorrowTs.OutDryBulbTemp = desDayInput.MaxDryBulb - desDayModsTS.OutDryBulbTemp * DBRange;
3789 : } else { // DesDayInput(EnvrnNum)%DBTempRangeType == DesDayDryBulbRangeType::Profile
3790 192 : tomorrowTs.OutDryBulbTemp = desDayModsTS.OutDryBulbTemp;
3791 : }
3792 :
3793 : // wet-bulb - generate from profile, humidity ratio, or dew point
3794 784512 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
3795 782016 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul) {
3796 2880 : Real64 WetBulb = desDayInput.HumIndValue - desDayModsTS.OutRelHum * WBRange;
3797 2880 : WetBulb = min(WetBulb, tomorrowTs.OutDryBulbTemp); // WB must be <= DB
3798 2880 : Real64 OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3799 2880 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3800 2880 : tomorrowTs.OutRelHum =
3801 2880 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3802 784512 : } else if (ConstantHumidityRatio) {
3803 : // Need Dew Point Temperature. Use Relative Humidity to get Humidity Ratio, unless Humidity Ratio is constant
3804 : // BG 9-26-07 moved following inside this IF statment; when HumIndType is 'Schedule' HumidityRatio wasn't being initialized
3805 : Real64 WetBulb =
3806 781056 : Psychrometrics::PsyTwbFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, RoutineNameLong);
3807 :
3808 781056 : Real64 OutHumRat = Psychrometrics::PsyWFnTdpPb(state, tomorrowTs.OutDryBulbTemp, desDayInput.PressBarom);
3809 781056 : if (HumidityRatio > OutHumRat) {
3810 276042 : WetBulb = tomorrowTs.OutDryBulbTemp;
3811 : } else {
3812 505014 : OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3813 : }
3814 781056 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3815 781056 : tomorrowTs.OutRelHum =
3816 781056 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3817 : } else {
3818 : HumidityRatio =
3819 576 : Psychrometrics::PsyWFnTdbRhPb(state, tomorrowTs.OutDryBulbTemp, desDayModsTS.OutRelHum / 100.0, desDayInput.PressBarom);
3820 576 : tomorrowTs.OutRelHum =
3821 576 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, WeatherManager) *
3822 : 100.0;
3823 : // TomorrowOutRelHum values set earlier
3824 576 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, HumidityRatio, desDayInput.PressBarom);
3825 : }
3826 :
3827 784512 : double DryBulb = tomorrowTs.OutDryBulbTemp;
3828 784512 : double RelHum = tomorrowTs.OutRelHum * 0.01;
3829 : Real64 ESky =
3830 784512 : CalcSkyEmissivity(state, envCurr.skyTempModel, OSky, DryBulb, tomorrowTs.OutDewPointTemp, RelHum); // Emissivitity of Sky
3831 784512 : tomorrowTs.HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
3832 :
3833 784512 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
3834 784512 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
3835 : // Design day not scheduled
3836 784320 : tomorrowTs.SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
3837 : }
3838 : // Generate solar values for timestep
3839 : // working results = BeamRad and DiffRad
3840 : // stored to program globals at end of loop
3841 : Real64 BeamRad;
3842 : Real64 DiffRad;
3843 784512 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
3844 : // scheduled: set value unconditionally (whether sun up or not)
3845 576 : BeamRad = desDayModsTS.BeamSolarRad;
3846 576 : DiffRad = desDayModsTS.DifSolarRad;
3847 : } else {
3848 :
3849 : // calc time = fractional hour of day
3850 : Real64 CurTime;
3851 783936 : if (state.dataGlobal->TimeStepsInHour != 1) {
3852 782400 : CurTime = double(hour - 1) + double(ts) * state.dataWeather->TimeStepFraction;
3853 : } else {
3854 1536 : CurTime = double(hour) + state.dataEnvrn->TS1TimeOffset;
3855 : }
3856 :
3857 783936 : Vector3<Real64> SUNCOS; // Sun direction cosines
3858 783936 : CalculateSunDirectionCosines(
3859 : state, CurTime, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle, SUNCOS);
3860 783936 : Real64 CosZenith = SUNCOS.z; // Cosine of Zenith Angle of Sun
3861 783936 : if (CosZenith < DataEnvironment::SunIsUpValue) {
3862 389060 : BeamRad = 0.0;
3863 389060 : DiffRad = 0.0;
3864 : } else {
3865 394876 : Real64 SinSolarAltitude = SUNCOS.z;
3866 :
3867 394876 : switch (desDayInput.solarModel) {
3868 374410 : case DesDaySolarModel::ASHRAE_ClearSky: {
3869 374410 : Real64 Exponent = B / CosZenith;
3870 : Real64 TotHoriz; // Total Radiation on Horizontal Surface
3871 374410 : if (Exponent > 700.0) {
3872 12 : TotHoriz = 0.0;
3873 : } else {
3874 374398 : TotHoriz = desDayInput.SkyClear * A * (C + CosZenith) * std::exp(-B / CosZenith);
3875 : }
3876 : // Radiation on an extraterrestial horizontal surface
3877 374410 : Real64 HO = GlobalSolarConstant * AVSC * CosZenith;
3878 374410 : Real64 KT = TotHoriz / HO; // Radiation ratio
3879 374410 : KT = min(KT, 0.75);
3880 374410 : DiffRad = TotHoriz * (1.0045 + KT * (0.04349 + KT * (-3.5227 + 2.6313 * KT)));
3881 374410 : if (desDayInput.SkyClear > 0.70) {
3882 224656 : DiffRad = TotHoriz * C / (C + CosZenith);
3883 : }
3884 374410 : BeamRad = (TotHoriz - DiffRad) / CosZenith;
3885 374410 : DiffRad = max(0.0, DiffRad);
3886 374410 : BeamRad = max(0.0, BeamRad);
3887 :
3888 374410 : } break;
3889 20276 : case DesDaySolarModel::ASHRAE_Tau:
3890 : case DesDaySolarModel::ASHRAE_Tau2017: {
3891 20276 : Real64 ETR = GlobalSolarConstant * AVSC; // radiation of an extraterrestrial normal surface, W/m2
3892 : Real64 GloHorzRad;
3893 20276 : ASHRAETauModel(
3894 : state, desDayInput.solarModel, ETR, CosZenith, desDayInput.TauB, desDayInput.TauD, BeamRad, DiffRad, GloHorzRad);
3895 20276 : } break;
3896 190 : case DesDaySolarModel::Zhang_Huang: {
3897 190 : int Hour3Ago = mod(hour + 20, 24) + 1; // hour 3 hours before
3898 190 : Real64 const TotSkyCover = max(1.0 - desDayInput.SkyClear, 0.0);
3899 380 : Real64 GloHorzRad = (ZHGlobalSolarConstant * SinSolarAltitude *
3900 190 : (ZhangHuang_C0 + ZhangHuang_C1 * TotSkyCover + ZhangHuang_C2 * pow_2(TotSkyCover) +
3901 380 : ZhangHuang_C3 * (tomorrowTs.OutDryBulbTemp -
3902 190 : state.dataWeather->wvarsHrTsTomorrow(ts, Hour3Ago).OutDryBulbTemp) +
3903 190 : ZhangHuang_C4 * tomorrowTs.OutRelHum + ZhangHuang_C5 * tomorrowTs.WindSpeed) +
3904 : ZhangHuang_D) /
3905 190 : ZhangHuang_K;
3906 190 : GloHorzRad = max(GloHorzRad, 0.0);
3907 190 : Real64 ClearnessIndex_kt = GloHorzRad / (GlobalSolarConstant * SinSolarAltitude);
3908 : // ClearnessIndex_kt=DesDayInput(EnvrnNum)%SkyClear
3909 190 : Real64 ClearnessIndex_ktc = 0.4268 + 0.1934 * SinSolarAltitude;
3910 : Real64 ClearnessIndex_kds;
3911 190 : if (ClearnessIndex_kt < ClearnessIndex_ktc) {
3912 96 : ClearnessIndex_kds = (3.996 - 3.862 * SinSolarAltitude + 1.54 * pow_2(SinSolarAltitude)) * pow_3(ClearnessIndex_kt);
3913 : } else {
3914 188 : ClearnessIndex_kds = ClearnessIndex_kt - (1.107 + 0.03569 * SinSolarAltitude + 1.681 * pow_2(SinSolarAltitude)) *
3915 94 : pow_3(1.0 - ClearnessIndex_kt);
3916 : }
3917 : // Calculate direct normal radiation, W/m2
3918 190 : BeamRad = ZHGlobalSolarConstant * SinSolarAltitude * ClearnessIndex_kds *
3919 190 : ((1.0 - ClearnessIndex_kt) / (1.0 - ClearnessIndex_kds));
3920 : // Calculation diffuse horizontal radiation, W/m2
3921 190 : DiffRad =
3922 190 : ZHGlobalSolarConstant * SinSolarAltitude * ((ClearnessIndex_kt - ClearnessIndex_kds) / (1.0 - ClearnessIndex_kds));
3923 :
3924 190 : } break;
3925 0 : default:
3926 0 : break;
3927 : }
3928 : }
3929 783936 : }
3930 :
3931 : // override result to 0 per environment var (for testing)
3932 784512 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreBeamRadiation) {
3933 0 : BeamRad = 0.0;
3934 : }
3935 784512 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreDiffuseRadiation) {
3936 0 : DiffRad = 0.0;
3937 : }
3938 :
3939 784512 : tomorrowTs.BeamSolarRad = BeamRad;
3940 784512 : tomorrowTs.DifSolarRad = DiffRad;
3941 :
3942 : } // Timestep (TS) Loop
3943 : } // Hour Loop
3944 :
3945 : // back-fill hour values from timesteps
3946 : // hour values = integrated over hour ending at time of hour
3947 : // insurance: hourly values not known to be needed
3948 149625 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3949 143640 : int Hour1Ago = mod(hour + 22, Constant::iHoursInDay) + 1;
3950 143640 : auto const &tomorrowHr = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, hour);
3951 143640 : auto const &tomorrowHr1Ago = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, Hour1Ago);
3952 :
3953 143640 : Real64 BeamRad = (tomorrowHr1Ago.BeamSolarRad + tomorrowHr.BeamSolarRad) / 2.0;
3954 143640 : Real64 DiffRad = (tomorrowHr1Ago.DifSolarRad + tomorrowHr.DifSolarRad) / 2.0;
3955 143640 : if (state.dataGlobal->TimeStepsInHour > 1) {
3956 782976 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour - 1; ++iTS) {
3957 640872 : BeamRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).BeamSolarRad;
3958 640872 : DiffRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).DifSolarRad;
3959 : }
3960 : }
3961 143640 : Wthr.BeamSolarRad(hour) = BeamRad / state.dataGlobal->TimeStepsInHour;
3962 143640 : Wthr.DifSolarRad(hour) = DiffRad / state.dataGlobal->TimeStepsInHour;
3963 : }
3964 :
3965 5985 : if (envCurr.WP_Type1 != 0) {
3966 :
3967 2 : switch (state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel) {
3968 2 : case SkyTempModel::ScheduleValue: {
3969 2 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3970 2 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3971 50 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3972 240 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3973 192 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = desDayModsEnvrn(ts + 1, hr + 1).SkyTemp =
3974 192 : dayVals[hr * state.dataGlobal->TimeStepsInHour];
3975 : }
3976 : }
3977 2 : } break;
3978 :
3979 0 : case SkyTempModel::DryBulbDelta: {
3980 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3981 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3982 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3983 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3984 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3985 0 : desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3986 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3987 : }
3988 : }
3989 0 : } break;
3990 :
3991 0 : case SkyTempModel::DewPointDelta: {
3992 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3993 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3994 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3995 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3996 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3997 0 : desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3998 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3999 : }
4000 : }
4001 0 : } break;
4002 :
4003 0 : default: {
4004 0 : } break;
4005 : } // switch (skyTempModel)
4006 : } // if (envCurr.WP_Type1 != 0)
4007 :
4008 5985 : state.dataGlobal->WarmupFlag = SaveWarmupFlag;
4009 5985 : }
4010 :
4011 20276 : Real64 AirMass(Real64 const CosZen) // COS( solar zenith), 0 - 1
4012 : {
4013 :
4014 : // SUBROUTINE INFORMATION:
4015 : // AUTHOR C Barnaby
4016 : // DATE WRITTEN Nov 2010
4017 :
4018 : // PURPOSE OF THIS SUBROUTINE:
4019 : // Calculate relative air mass using Kasten and Young approximation
4020 :
4021 : // METHODOLOGY EMPLOYED:
4022 : // Eqn (16), ASHRAE HOF 2009, p. 14.9
4023 :
4024 : // REFERENCES:
4025 : // ASHRAE HOF 2009 Chapter 14
4026 : // Kasten, F and T. Young. 1989. Revised optical air mass tables
4027 : // and approximating formula. Applied Optics 28:4735-4738.
4028 :
4029 : Real64 AirMass;
4030 : Real64 SunAltD;
4031 :
4032 20276 : if (CosZen <= 0.001) {
4033 6 : AirMass = 37.07837343; // limit value calc'd with Excel
4034 : // value increases little as CosZen -> 0
4035 20270 : } else if (CosZen >= 1.0) {
4036 0 : AirMass = 1.0;
4037 : } else {
4038 : // note: COS( Zen) = SIN( Alt)
4039 20270 : SunAltD = std::asin(CosZen) / Constant::DegToRad; // altitude, degrees
4040 20270 : AirMass = 1.0 / (CosZen + 0.50572 * std::pow(6.07995 + SunAltD, -1.6364));
4041 : }
4042 20276 : return AirMass;
4043 : }
4044 :
4045 : //------------------------------------------------------------------------------
4046 :
4047 20276 : void ASHRAETauModel([[maybe_unused]] EnergyPlusData &state,
4048 : DesDaySolarModel const TauModel, // ASHRAETau solar model type ASHRAE_Tau or ASHRAE_Tau2017
4049 : Real64 const ETR, // extraterrestrial normal irradiance, W/m2
4050 : Real64 const CosZen, // COS( solar zenith angle), 0 - 1
4051 : Real64 const TauB, // beam tau factor
4052 : Real64 const TauD, // dif tau factor
4053 : Real64 &IDirN, // returned: direct (beam) irradiance on normal surface, W/m2
4054 : Real64 &IDifH, // returned: diffuse irradiance on horiz surface, W/m2
4055 : Real64 &IGlbH // returned: global irradiance on horiz surface, W/m2
4056 : )
4057 : {
4058 :
4059 : // SUBROUTINE INFORMATION:
4060 : // AUTHOR C Barnaby
4061 : // DATE WRITTEN Nov 2010
4062 :
4063 : // PURPOSE OF THIS SUBROUTINE:
4064 : // Calculate clear-sky direct and diffuse irradiance using ASHRAE "tau" model
4065 :
4066 : // METHODOLOGY EMPLOYED:
4067 : // Eqns (17-18), ASHRAE HOF 2009, p. 14.9
4068 : // Eqns (19-20), ASHRAE HOF 2013 p. 14.9 and 2017 p. 14.10
4069 :
4070 : // REFERENCES:
4071 : // ASHRAE HOF 2009 Chapter 14
4072 :
4073 : Real64 AB; // air mass exponents
4074 : Real64 AD;
4075 : Real64 M; // air mass
4076 :
4077 20276 : if (CosZen < DataEnvironment::SunIsUpValue || TauB <= 0.0 || TauD <= 0.0) {
4078 0 : IDirN = 0.0;
4079 0 : IDifH = 0.0;
4080 0 : IGlbH = 0.0;
4081 : } else {
4082 20276 : if (TauModel == DesDaySolarModel::ASHRAE_Tau) {
4083 20276 : AB = 1.219 - 0.043 * TauB - 0.151 * TauD - 0.204 * TauB * TauD;
4084 20276 : AD = 0.202 + 0.852 * TauB - 0.007 * TauD - 0.357 * TauB * TauD;
4085 : } else {
4086 : // TauModelType == ASHRAE_Tau2017
4087 0 : AB = 1.454 - 0.406 * TauB - 0.268 * TauD + 0.021 * TauB * TauD;
4088 0 : AD = 0.507 + 0.205 * TauB - 0.080 * TauD - 0.190 * TauB * TauD;
4089 : }
4090 20276 : M = AirMass(CosZen);
4091 20276 : IDirN = ETR * std::exp(-TauB * std::pow(M, AB));
4092 20276 : IDifH = ETR * std::exp(-TauD * std::pow(M, AD));
4093 20276 : IGlbH = IDirN * CosZen + IDifH;
4094 : }
4095 20276 : }
4096 :
4097 801 : void AllocateWeatherData(EnergyPlusData &state)
4098 : {
4099 :
4100 : // SUBROUTINE INFORMATION:
4101 : // AUTHOR Linda Lawrie
4102 : // DATE WRITTEN December 2000
4103 :
4104 : // PURPOSE OF THIS SUBROUTINE:
4105 : // This subroutine allocates the weather data structures (Today, Tomorrow,
4106 : // Design Day) to the proper number of "time steps in hour" requested by the user.
4107 : // Interpolation of data is done later after either setting up the design day (hourly
4108 : // data) or reading in hourly weather data.
4109 :
4110 801 : state.dataWeather->wvarsHrTsToday.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
4111 801 : state.dataWeather->wvarsHrTsTomorrow.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
4112 801 : }
4113 :
4114 10209 : void CalculateDailySolarCoeffs(EnergyPlusData const &state,
4115 : int const DayOfYear, // Day of year (1 - 366)
4116 : Real64 &A, // ASHRAE "A" - Apparent solar irradiation at air mass = 0 [W/M**2]
4117 : Real64 &B, // ASHRAE "B" - Atmospheric extinction coefficient
4118 : Real64 &C, // ASHRAE "C" - Diffuse radiation factor
4119 : Real64 &AnnVarSolConstant, // Annual variation in the solar constant
4120 : Real64 &EquationOfTime, // Equation of Time
4121 : Real64 &SineSolarDeclination, // Sine of Solar Declination
4122 : Real64 &CosineSolarDeclination // Cosine of Solar Declination
4123 : )
4124 : {
4125 :
4126 : // SUBROUTINE INFORMATION:
4127 : // AUTHOR George Walton
4128 : // DATE WRITTEN May 1985
4129 : // MODIFIED 1999 for EnergyPlus
4130 : // RE-ENGINEERED 2001; LKL; Remove need for English -> SI conversion
4131 : // Implement Tarp "fix" for Southern Hemisphere
4132 :
4133 : // PURPOSE OF THIS SUBROUTINE:
4134 : // This subroutine computes the daily solar coefficients used in other
4135 : // calculations. Specifically, this routine computes values of the solar declination, equation
4136 : // of time, and ashrae sky coefficients a, b, and c for a given
4137 : // day of the year.
4138 :
4139 : // METHODOLOGY EMPLOYED:
4140 : // The method is the same as that recommended in the ASHRAE loads
4141 : // algorithms manual, except that the fourier series expressions
4142 : // have been extended by two terms for greater accuracy.
4143 : // coefficients for the new expressions were determined at USACERL
4144 : // using data from the cited references.
4145 :
4146 : // REFERENCES:
4147 : // J. L. Threlkeld, "Thermal Environmental Engineering", 1970,
4148 : // p.316, for declination and equation of time.
4149 : // "ASHRAE Handbook of Fundamentals", 1972, p.387 for sky
4150 : // coefficients a, b, and c.
4151 : // See SUN3 in SolarShading. See SUN2 in BLAST. See SUN3 in Tarp.
4152 :
4153 10209 : Real64 const DayCorrection(Constant::Pi * 2.0 / 366.0);
4154 :
4155 : // Fitted coefficients of Fourier series | Sine of declination coefficients
4156 : static constexpr std::array<Real64, 9> SineSolDeclCoef = {
4157 : 0.00561800, 0.0657911, -0.392779, 0.00064440, -0.00618495, -0.00010101, -0.00007951, -0.00011691, 0.00002096};
4158 : // Fitted coefficients of Fourier Series | Equation of Time coefficients
4159 : static constexpr std::array<Real64, 9> EqOfTimeCoef = {
4160 : 0.00021971, -0.122649, 0.00762856, -0.156308, -0.0530028, -0.00388702, -0.00123978, -0.00270502, -0.00167992};
4161 : // Fitted coefficients of Fourier Series | ASHRAE A Factor coefficients
4162 : static constexpr std::array<Real64, 9> ASHRAE_A_Coef = {1161.6685, 1.1554, 77.3575, -0.5359, -3.7622, 0.9875, -3.3924, -1.7445, 1.1198};
4163 : // Fitted coefficients of Fourier Series | ASHRAE B Factor coefficients
4164 : static constexpr std::array<Real64, 9> ASHRAE_B_Coef = {
4165 : 0.171631, -0.00400448, -0.0344923, 0.00000209, 0.00325428, -0.00085429, 0.00229562, 0.0009034, -0.0011867};
4166 : // Fitted coefficients of Fourier Series | ASHRAE C Factor coefficients
4167 : static constexpr std::array<Real64, 9> ASHRAE_C_Coef = {
4168 : 0.0905151, -0.00322522, -0.0407966, 0.000104164, 0.00745899, -0.00086461, 0.0013111, 0.000808275, -0.00170515};
4169 :
4170 : // Day of Year in Radians (Computed from Input DayOfYear)
4171 10209 : Real64 X = DayCorrection * DayOfYear; // Convert Julian date (Day of Year) to angle X
4172 :
4173 : // Calculate sines and cosines of X
4174 10209 : Real64 SinX = std::sin(X);
4175 10209 : Real64 CosX = std::cos(X);
4176 :
4177 10209 : SineSolarDeclination = SineSolDeclCoef[0] + SineSolDeclCoef[1] * SinX + SineSolDeclCoef[2] * CosX + SineSolDeclCoef[3] * (SinX * CosX * 2.0) +
4178 10209 : SineSolDeclCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4179 10209 : SineSolDeclCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4180 10209 : SineSolDeclCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4181 10209 : SineSolDeclCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4182 10209 : SineSolDeclCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4183 10209 : CosineSolarDeclination = std::sqrt(1.0 - pow_2(SineSolarDeclination));
4184 :
4185 10209 : EquationOfTime = EqOfTimeCoef[0] + EqOfTimeCoef[1] * SinX + EqOfTimeCoef[2] * CosX + EqOfTimeCoef[3] * (SinX * CosX * 2.0) +
4186 10209 : EqOfTimeCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4187 10209 : EqOfTimeCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4188 10209 : EqOfTimeCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4189 10209 : EqOfTimeCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4190 10209 : EqOfTimeCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4191 :
4192 10209 : AnnVarSolConstant = 1.000047 + 0.000352615 * SinX + 0.0334454 * CosX;
4193 :
4194 10209 : A = ASHRAE_A_Coef[0] + ASHRAE_A_Coef[1] * SinX + ASHRAE_A_Coef[2] * CosX + ASHRAE_A_Coef[3] * (SinX * CosX * 2.0) +
4195 10209 : ASHRAE_A_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_A_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4196 10209 : ASHRAE_A_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4197 10209 : ASHRAE_A_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4198 10209 : ASHRAE_A_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4199 :
4200 : // Compute B and C coefficients
4201 :
4202 10209 : if (state.dataEnvrn->Latitude < 0.0) {
4203 : // If in southern hemisphere, compute B and C with a six month time shift.
4204 0 : X -= Constant::Pi;
4205 0 : SinX = std::sin(X);
4206 0 : CosX = std::cos(X);
4207 : }
4208 :
4209 10209 : B = ASHRAE_B_Coef[0] + ASHRAE_B_Coef[1] * SinX + ASHRAE_B_Coef[2] * CosX + ASHRAE_B_Coef[3] * (SinX * CosX * 2.0) +
4210 10209 : ASHRAE_B_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_B_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4211 10209 : ASHRAE_B_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4212 10209 : ASHRAE_B_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4213 10209 : ASHRAE_B_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4214 :
4215 10209 : C = ASHRAE_C_Coef[0] + ASHRAE_C_Coef[1] * SinX + ASHRAE_C_Coef[2] * CosX + ASHRAE_C_Coef[3] * (SinX * CosX * 2.0) +
4216 10209 : ASHRAE_C_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_C_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4217 10209 : ASHRAE_C_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4218 10209 : ASHRAE_C_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4219 10209 : ASHRAE_C_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4220 10209 : }
4221 :
4222 783936 : void CalculateSunDirectionCosines(EnergyPlusData const &state,
4223 : Real64 const TimeValue, // Current Time of Day
4224 : Real64 const EqOfTime, // Equation of Time
4225 : Real64 const SinSolDeclin, // Sine of Solar Declination
4226 : Real64 const CosSolDeclin, // Cosine of Solar Declination
4227 : Vector3<Real64> &SUNCOS)
4228 : {
4229 :
4230 : // SUBROUTINE INFORMATION:
4231 : // AUTHOR George Walton
4232 : // DATE WRITTEN May 1975
4233 : // MODIFIED 1999 for EnergyPlus
4234 :
4235 : // PURPOSE OF THIS SUBROUTINE:
4236 : // This routine computes the solar direction cosines for hourly
4237 : // radiation calculations.
4238 :
4239 : // REFERENCES:
4240 : // "NECAP Engineering Manual", 1974, p.3-117
4241 :
4242 783936 : EP_SIZE_CHECK(SUNCOS, 3); // NOLINT(misc-static-assert)
4243 :
4244 : // COMPUTE THE HOUR ANGLE
4245 783936 : Real64 H = (15.0 * (12.0 - (TimeValue + EqOfTime)) + (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude)) * Constant::DegToRad;
4246 783936 : Real64 COSH = std::cos(H);
4247 : // COMPUTE THE COSINE OF THE SOLAR ZENITH ANGLE.
4248 : // This is also the Sine of the Solar Altitude Angle
4249 :
4250 783936 : SUNCOS.z = SinSolDeclin * state.dataEnvrn->SinLatitude + CosSolDeclin * state.dataEnvrn->CosLatitude * COSH;
4251 :
4252 783936 : if (SUNCOS.z >= DataEnvironment::SunIsUpValue) { // If Sun above horizon, compute other direction cosines
4253 394876 : SUNCOS.y = SinSolDeclin * state.dataEnvrn->CosLatitude - CosSolDeclin * state.dataEnvrn->SinLatitude * COSH;
4254 394876 : SUNCOS.x = CosSolDeclin * std::sin(H);
4255 : } else { // Sun is down, set to 0.0
4256 389060 : SUNCOS.x = 0.0;
4257 389060 : SUNCOS.y = 0.0;
4258 : }
4259 783936 : }
4260 :
4261 2925340 : void DetermineSunUpDown(EnergyPlusData &state, Vector3<Real64> &SunCOS)
4262 : {
4263 :
4264 : // SUBROUTINE INFORMATION:
4265 : // AUTHOR Linda Lawrie
4266 : // DATE WRITTEN 1999
4267 :
4268 : // PURPOSE OF THIS SUBROUTINE:
4269 : // This subroutine determines if the sun is up or down for the current
4270 : // hour/timestep.
4271 :
4272 : // REFERENCES:
4273 : // Sun routines from IBLAST, authored by Walton.
4274 :
4275 : // COMPUTE THE HOUR ANGLE
4276 2925340 : if (state.dataGlobal->TimeStepsInHour != 1) {
4277 5807312 : state.dataWeather->HrAngle = (15.0 * (12.0 - (state.dataGlobal->CurrentTime + state.dataWeather->TodayVariables.EquationOfTime)) +
4278 2903656 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4279 : } else {
4280 21684 : state.dataWeather->HrAngle =
4281 21684 : (15.0 *
4282 21684 : (12.0 - ((state.dataGlobal->CurrentTime + state.dataEnvrn->TS1TimeOffset) + state.dataWeather->TodayVariables.EquationOfTime)) +
4283 21684 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4284 : }
4285 2925340 : Real64 H = state.dataWeather->HrAngle * Constant::DegToRad;
4286 :
4287 : // Compute the Cosine of the Solar Zenith (Altitude) Angle.
4288 2925340 : Real64 CosZenith = state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle +
4289 2925340 : state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H);
4290 :
4291 2925340 : Real64 SolarZenith = std::acos(CosZenith);
4292 2925340 : Real64 SinAltitude = state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H) +
4293 2925340 : state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle;
4294 2925340 : Real64 SolarAltitude = std::asin(SinAltitude);
4295 2925340 : Real64 CosAzimuth = -(state.dataEnvrn->SinLatitude * CosZenith - state.dataWeather->TodayVariables.SinSolarDeclinAngle) /
4296 2925340 : (state.dataEnvrn->CosLatitude * std::sin(SolarZenith));
4297 : // Following because above can yield invalid cos value. (e.g. at south pole)
4298 2925340 : CosAzimuth = max(CosAzimuth, -1.0);
4299 2925340 : CosAzimuth = min(1.0, CosAzimuth);
4300 2925340 : Real64 SolarAzimuth = std::acos(CosAzimuth);
4301 :
4302 2925340 : state.dataWeather->SolarAltitudeAngle = SolarAltitude / Constant::DegToRad;
4303 2925340 : state.dataWeather->SolarAzimuthAngle = SolarAzimuth / Constant::DegToRad;
4304 2925340 : if (state.dataWeather->HrAngle < 0.0) {
4305 1465180 : state.dataWeather->SolarAzimuthAngle = 360.0 - state.dataWeather->SolarAzimuthAngle;
4306 : }
4307 :
4308 2925340 : SunCOS.z = CosZenith;
4309 2925340 : state.dataEnvrn->SunIsUpPrevTS = state.dataEnvrn->SunIsUp;
4310 2925340 : if (CosZenith < DataEnvironment::SunIsUpValue) {
4311 1463062 : state.dataEnvrn->SunIsUp = false;
4312 1463062 : SunCOS.y = 0.0;
4313 1463062 : SunCOS.x = 0.0;
4314 : } else {
4315 1462278 : state.dataEnvrn->SunIsUp = true;
4316 1462278 : SunCOS.y = state.dataWeather->TodayVariables.SinSolarDeclinAngle * state.dataEnvrn->CosLatitude -
4317 1462278 : state.dataWeather->TodayVariables.CosSolarDeclinAngle * state.dataEnvrn->SinLatitude * std::cos(H);
4318 1462278 : SunCOS.x = state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::sin(H);
4319 : }
4320 2925340 : }
4321 :
4322 801 : void OpenWeatherFile(EnergyPlusData &state, bool &ErrorsFound)
4323 : {
4324 :
4325 : // SUBROUTINE INFORMATION:
4326 : // AUTHOR Linda Lawrie
4327 : // DATE WRITTEN June 1999
4328 :
4329 : // PURPOSE OF THIS SUBROUTINE:
4330 : // This subroutine checks to see if a weather file and what kind of weather file
4331 : // exists in the working directory and calls appropriate routines to
4332 : // open the files and set up for use.
4333 :
4334 801 : state.dataWeather->WeatherFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
4335 801 : if (state.dataWeather->WeatherFileExists) {
4336 793 : OpenEPlusWeatherFile(state, ErrorsFound, true);
4337 : }
4338 801 : }
4339 :
4340 4569 : void OpenEPlusWeatherFile(EnergyPlusData &state,
4341 : bool &ErrorsFound, // Will be set to true if errors found
4342 : bool const ProcessHeader // Set to true when headers should be processed (rather than just read)
4343 : )
4344 : {
4345 :
4346 : // SUBROUTINE INFORMATION:
4347 : // AUTHOR Linda K. Lawrie
4348 : // DATE WRITTEN June 1999
4349 :
4350 : // PURPOSE OF THIS SUBROUTINE:
4351 : // This subroutine opens the EnergyPlus Weather File (in.epw) and processes
4352 : // the initial header records.
4353 :
4354 : // METHODOLOGY EMPLOYED:
4355 : // List directed reads, as possible.
4356 :
4357 4569 : state.files.inputWeatherFile.close();
4358 4569 : state.files.inputWeatherFile.filePath = state.files.inputWeatherFilePath.filePath;
4359 4569 : state.files.inputWeatherFile.open();
4360 4569 : if (!state.files.inputWeatherFile.good()) {
4361 0 : ShowFatalError(state, "OpenWeatherFile: Could not OPEN EPW Weather File", OptionalOutputFileRef(state.files.eso));
4362 : }
4363 :
4364 4569 : if (ProcessHeader) {
4365 : // Read in Header Information
4366 :
4367 : // Headers should come in order
4368 7137 : for (int typeNum = static_cast<int>(EpwHeaderType::Location); typeNum < static_cast<int>(EpwHeaderType::Num); ++typeNum) {
4369 6344 : auto Line = state.files.inputWeatherFile.readLine();
4370 6344 : if (Line.eof) {
4371 0 : ShowFatalError(
4372 : state,
4373 0 : format("OpenWeatherFile: Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}",
4374 0 : epwHeaders[typeNum]),
4375 0 : OptionalOutputFileRef(state.files.eso));
4376 : }
4377 :
4378 6344 : int endcol = len(Line.data);
4379 6344 : if (endcol > 0) {
4380 6344 : if (int(Line.data[endcol - 1]) == DataSystemVariables::iUnicode_end) {
4381 0 : ShowSevereError(state,
4382 : "OpenWeatherFile: EPW Weather File appears to be a Unicode or binary file.",
4383 0 : OptionalOutputFileRef(state.files.eso));
4384 0 : ShowContinueError(state, "...This file cannot be read by this program. Please save as PC or Unix file and try again");
4385 0 : ShowFatalError(state, "Program terminates due to previous condition.");
4386 : }
4387 : }
4388 6344 : std::string::size_type const Pos = FindNonSpace(Line.data);
4389 6344 : std::string::size_type const HdPos = index(Line.data, epwHeaders[typeNum]);
4390 6344 : if (Pos != HdPos) {
4391 0 : continue;
4392 : }
4393 6344 : ProcessEPWHeader(state, static_cast<EpwHeaderType>(typeNum), Line.data, ErrorsFound);
4394 6344 : }
4395 : } else { // Header already processed, just read
4396 3776 : SkipEPlusWFHeader(state);
4397 : }
4398 4569 : }
4399 :
4400 12154 : void CloseWeatherFile(EnergyPlusData &state)
4401 : {
4402 12154 : state.files.inputWeatherFile.close();
4403 12154 : }
4404 :
4405 801 : void ResolveLocationInformation(EnergyPlusData &state, bool &ErrorsFound) // Set to true if no location evident
4406 : {
4407 :
4408 : // SUBROUTINE INFORMATION:
4409 : // AUTHOR Rick Strand
4410 : // DATE WRITTEN June 1997
4411 :
4412 : // PURPOSE OF THIS SUBROUTINE:
4413 : // This subroutine is currently the main interface between the old data
4414 : // structure on the BLAST Weather file and the new data structure contained
4415 : // in this module. At some point, this subroutine will be converted
4416 : // to read information directly from the new input file.
4417 :
4418 1577 : if (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather &&
4419 776 : state.dataWeather->WeatherFileExists) {
4420 768 : if (state.dataWeather->LocationGathered) {
4421 : // See if "matching" location
4422 761 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4423 1471 : if (std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude) > 1.0 ||
4424 1422 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude) > 1.0 ||
4425 2182 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone) > 0.0 ||
4426 711 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) / max(state.dataEnvrn->Elevation, 1.0) >
4427 : 0.10) {
4428 142 : ShowWarningError(state, "Weather file location will be used rather than entered (IDF) Location object.");
4429 71 : ShowContinueError(state, format("..Location object={}", state.dataWeather->LocationTitle));
4430 71 : ShowContinueError(state, format("..Weather File Location={}", state.dataEnvrn->WeatherFileLocationTitle));
4431 142 : ShowContinueError(
4432 : state,
4433 142 : format("..due to location differences, Latitude difference=[{:.2R}] degrees, Longitude difference=[{:.2R}] degrees.",
4434 71 : std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude),
4435 71 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude)));
4436 142 : ShowContinueError(state,
4437 142 : format("..Time Zone difference=[{:.1R}] hour(s), Elevation difference=[{:.2R}] percent, [{:.2R}] meters.",
4438 71 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone),
4439 142 : std::abs((state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) /
4440 71 : max(state.dataEnvrn->Elevation, 1.0) * 100.0),
4441 142 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation)));
4442 : }
4443 : }
4444 : }
4445 768 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4446 767 : state.dataWeather->LocationTitle = state.dataEnvrn->WeatherFileLocationTitle;
4447 767 : state.dataEnvrn->Latitude = state.dataWeather->WeatherFileLatitude;
4448 767 : state.dataEnvrn->Longitude = state.dataWeather->WeatherFileLongitude;
4449 767 : state.dataEnvrn->TimeZoneNumber = state.dataWeather->WeatherFileTimeZone;
4450 767 : state.dataEnvrn->Elevation = state.dataWeather->WeatherFileElevation;
4451 : }
4452 33 : } else if (!state.dataWeather->LocationGathered) {
4453 0 : state.dataWeather->LocationTitle = "Not Entered";
4454 0 : ShowSevereError(state, "No Location given. Must have location information for simulation.");
4455 0 : ErrorsFound = true;
4456 : }
4457 :
4458 801 : if (!ErrorsFound) {
4459 801 : state.dataEnvrn->StdBaroPress = DataEnvironment::StdPressureSeaLevel * std::pow(1.0 - 2.25577e-05 * state.dataEnvrn->Elevation, 5.2559);
4460 1602 : state.dataEnvrn->StdRhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(
4461 801 : state, state.dataEnvrn->StdBaroPress, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
4462 : // Write Final Location Information to the initialization output file
4463 : static constexpr std::string_view LocHdFormat(
4464 : "! <Site:Location>, Location Name, Latitude {N+/S- Deg}, Longitude {E+/W- Deg}, Time Zone Number "
4465 : "{GMT+/-}, Elevation {m}, Standard Pressure at Elevation {Pa}, Standard RhoAir at Elevation\n");
4466 801 : print(state.files.eio, "{}", LocHdFormat);
4467 :
4468 : static constexpr std::string_view LocFormat("Site:Location,{},{:.2R},{:.2R},{:.2R},{:.2R},{:.0R},{:.4R}\n");
4469 801 : print(state.files.eio,
4470 : LocFormat,
4471 801 : state.dataWeather->LocationTitle,
4472 801 : state.dataEnvrn->Latitude,
4473 801 : state.dataEnvrn->Longitude,
4474 801 : state.dataEnvrn->TimeZoneNumber,
4475 801 : state.dataEnvrn->Elevation,
4476 801 : state.dataEnvrn->StdBaroPress,
4477 801 : state.dataEnvrn->StdRhoAir);
4478 : }
4479 801 : }
4480 :
4481 2145 : void CheckLocationValidity(EnergyPlusData &state)
4482 : {
4483 :
4484 : // SUBROUTINE INFORMATION:
4485 : // AUTHOR Rick Strand
4486 : // DATE WRITTEN June 1997
4487 :
4488 : // PURPOSE OF THIS SUBROUTINE:
4489 : // This subroutine is checks to see whether the user specified location
4490 : // or the weather file location (if one exists) is valid. The standard
4491 : // time meridian is also calculated and compared to the user supplied
4492 : // or weather file time zone number.
4493 :
4494 2145 : bool LocationError = false; // Set to true if there is a problem detected
4495 :
4496 2145 : if ((state.dataEnvrn->Latitude == -999.0) && (state.dataEnvrn->Longitude == -999.0) && (state.dataEnvrn->TimeZoneNumber != -999.0)) {
4497 0 : ShowSevereError(state, "No location specified");
4498 0 : LocationError = true;
4499 : }
4500 :
4501 2145 : if ((state.dataEnvrn->Latitude < -90.0) || (state.dataEnvrn->Latitude > 90.0)) {
4502 0 : ShowSevereError(state, format("Latitude must be between -90 and 90; Entered={:.2R}", state.dataEnvrn->Latitude));
4503 0 : LocationError = true;
4504 : }
4505 :
4506 2145 : if ((state.dataEnvrn->Longitude < -180.0) || (state.dataEnvrn->Longitude > 180.0)) {
4507 0 : ShowSevereError(state, format("Longitude must be between -180 and 180; Entered={:.2R}", state.dataEnvrn->Longitude));
4508 0 : LocationError = true;
4509 : }
4510 :
4511 2145 : if ((state.dataEnvrn->TimeZoneNumber < -12.00) || (state.dataEnvrn->TimeZoneNumber > 14.00)) {
4512 0 : ShowSevereError(state, format("Time Zone must be between -12 and +14; Entered={:.2R}", state.dataEnvrn->TimeZoneNumber));
4513 0 : LocationError = true;
4514 : }
4515 :
4516 2145 : Real64 const StdTimeMerid = GetSTM(state.dataEnvrn->Longitude); // Standard time meridian.
4517 :
4518 : // Compare the standard time meridian with the time zone number. If
4519 : // different, notify the user. If StdTimeMerid couldn't be calculated,
4520 : // produce an error message.
4521 :
4522 2145 : if (state.dataEnvrn->varyingLocationLatSched != nullptr || state.dataEnvrn->varyingLocationLongSched != nullptr) {
4523 : // don't do any warnings, the building is moving
4524 801 : } else if (StdTimeMerid >= -12.0 && StdTimeMerid <= 12.0) {
4525 801 : if (state.dataEnvrn->TimeZoneNumber != StdTimeMerid) {
4526 : // Difference between Standard Time Meridian and TimeZone
4527 10 : Real64 const DiffCalc = std::abs(state.dataEnvrn->TimeZoneNumber - StdTimeMerid);
4528 10 : if (DiffCalc > 1.0 && DiffCalc < 24.0) {
4529 0 : if (DiffCalc < 3.0) {
4530 0 : ShowWarningError(state,
4531 0 : format("Standard Time Meridian and Time Zone differ by more than 1, Difference=\"{:.1R}\"", DiffCalc));
4532 0 : ShowContinueError(state, "Solar Positions may be incorrect");
4533 : } else {
4534 0 : ShowSevereError(state, format("Standard Time Meridian and Time Zone differ by more than 2, Difference=\"{:.1R}\"", DiffCalc));
4535 0 : ShowContinueError(state, "Solar Positions will be incorrect");
4536 : // LocationError=.TRUE.
4537 : }
4538 : }
4539 : }
4540 801 : } else {
4541 0 : ShowSevereError(state, "Unable to calculate the standard time meridian");
4542 0 : LocationError = true;
4543 : }
4544 :
4545 : // Error handling: if there are any errors in the location information
4546 : // the simulation must be terminated
4547 :
4548 2145 : if (LocationError) {
4549 0 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4550 : }
4551 :
4552 2145 : if (state.dataEnvrn->TimeZoneNumber <= 12.00) {
4553 2145 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0;
4554 : } else {
4555 0 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0 - 360.0;
4556 : }
4557 2145 : state.dataEnvrn->SinLatitude = std::sin(Constant::DegToRad * state.dataEnvrn->Latitude);
4558 2145 : state.dataEnvrn->CosLatitude = std::cos(Constant::DegToRad * state.dataEnvrn->Latitude);
4559 :
4560 2145 : if (state.dataEnvrn->Latitude == 0.0 && state.dataEnvrn->Longitude == 0.0 && state.dataEnvrn->TimeZoneNumber == 0.0) {
4561 0 : ShowWarningError(state,
4562 : "Did you realize that you have Latitude=0.0, Longitude=0.0 and TimeZone=0.0? Your building site is in the middle of "
4563 : "the Atlantic Ocean.");
4564 : }
4565 2145 : }
4566 :
4567 776 : void CheckWeatherFileValidity(EnergyPlusData &state)
4568 : {
4569 :
4570 : // SUBROUTINE INFORMATION:
4571 : // AUTHOR Linda Lawrie
4572 : // DATE WRITTEN February 1977
4573 : // MODIFIED June 1997 (RKS)
4574 :
4575 : // PURPOSE OF THIS SUBROUTINE:
4576 : // This subroutine contains a portion of the legacy subroutine CKBLDE.
4577 : // The main purpose of this routine is to check the validity of the
4578 : // weather dates provided by the user and the attached weather file.
4579 : // These functions may eventually be pushed to an interface. This
4580 : // routine also sends the weather file header information at the
4581 : // Environment derived type.
4582 :
4583 776 : if (!state.dataWeather->WeatherFileExists) { // No weather file exists but the user requested one--print error message
4584 :
4585 8 : if (state.dataGlobal->DoWeathSim) {
4586 0 : ShowSevereError(state, "GetNextEnvironment: Weather Environment(s) requested, but no weather file found");
4587 0 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4588 : }
4589 :
4590 : } // ... end of WeatherFileExists IF-THEN
4591 776 : }
4592 :
4593 801 : void ReportOutputFileHeaders(EnergyPlusData &state)
4594 : {
4595 :
4596 : // SUBROUTINE INFORMATION:
4597 : // AUTHOR Rick Strand
4598 : // DATE WRITTEN June 1997
4599 : // MODIFIED December 2017; Jason DeGraw
4600 :
4601 : // PURPOSE OF THIS SUBROUTINE:
4602 : // This subroutine prints out the necessary header information required
4603 : // by the EnergyPlus output file format. This subroutine can be
4604 : // replicated in any other modules which must send data to the output
4605 : // file.
4606 :
4607 : // METHODOLOGY EMPLOYED:
4608 : // For each report, the report flag integer must be saved from the
4609 : // global report number counter. Then, the report counter must be
4610 : // incremented. Finally, the header information for the report must
4611 : // be sent to the output file.
4612 :
4613 : using OutputProcessor::ReportFreq;
4614 :
4615 : static constexpr std::string_view EnvironmentString(",5,Environment Title[],Latitude[deg],Longitude[deg],Time Zone[],Elevation[m]");
4616 :
4617 : static constexpr std::array<std::string_view, (int)ReportFreq::Num> freqStrings = {
4618 : "", // No EachCall string
4619 : ",8,Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],Hour[],StartMinute[],EndMinute[],DayType",
4620 : "", // No Hour string
4621 : ",5,Cumulative Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],DayType ! When Daily ",
4622 : ",2,Cumulative Days of Simulation[],Month[] ! When Monthly ",
4623 : ",1,Cumulative Days of Simulation[] ! When Run Period ",
4624 : ",1,Calendar Year of Simulation[] ! When Annual "};
4625 :
4626 801 : auto &op = state.dataOutputProcessor;
4627 :
4628 801 : state.dataWeather->EnvironmentReportNbr = ++op->ReportNumberCounter;
4629 801 : if (state.dataWeather->EnvironmentReportNbr != 1) { // problem
4630 0 : ShowFatalError(state, "ReportOutputFileHeaders: Assigned report number for Environment title is not 1. Contact Support.");
4631 : }
4632 801 : state.dataWeather->EnvironmentReportChr = fmt::to_string(state.dataWeather->EnvironmentReportNbr);
4633 801 : strip(state.dataWeather->EnvironmentReportChr);
4634 801 : print(state.files.eso, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4635 801 : print(state.files.mtr, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4636 :
4637 : // TImeStep and Hour share a stamp
4638 801 : op->freqStampReportNums[(int)ReportFreq::Hour] = op->freqStampReportNums[(int)ReportFreq::TimeStep] = ++op->ReportNumberCounter;
4639 801 : print(state.files.eso, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4640 801 : print(state.files.mtr, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4641 :
4642 4005 : for (ReportFreq freq : {ReportFreq::Day, ReportFreq::Month, ReportFreq::Simulation, ReportFreq::Year}) {
4643 3204 : op->freqStampReportNums[(int)freq] = ++op->ReportNumberCounter;
4644 3204 : print(state.files.eso, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Report Variables Requested");
4645 3204 : print(state.files.mtr, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Meters Requested");
4646 : }
4647 801 : }
4648 :
4649 2925340 : void ReportWeatherAndTimeInformation(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
4650 : {
4651 :
4652 : // SUBROUTINE INFORMATION:
4653 : // AUTHOR Rick Strand
4654 : // DATE WRITTEN June 1997
4655 :
4656 : // PURPOSE OF THIS SUBROUTINE:
4657 : // This subroutine is the main driver of the weather reporting. This
4658 : // routine is also responsible for printing the time and environment
4659 : // stamps.
4660 :
4661 : // METHODOLOGY EMPLOYED:
4662 : // Reporting is only done for non-warmup days. The environment stamp
4663 : // is only reported at the beginning of an environment, but after the
4664 : // warmup days (to allow all modules to print the report headers to the
4665 : // output file. This is controlled by the PrintEnvrnStamp variable
4666 : // which is passed in and reset if necessary.
4667 :
4668 : // Report the time stamp and the current weather to the output file
4669 :
4670 2925340 : if (!state.dataGlobal->WarmupFlag && !state.dataWeather->RPReadAllWeatherData) { // Write the required output information
4671 :
4672 : // The first time through in a non-warmup day, the environment header
4673 : // must be printed. This must be done here and not in the generic
4674 : // DataGlobals::BeginEnvrnFlag block above because other modules in the simulation
4675 : // must also print out header information. This can be done during
4676 : // the simulation warmup if the environment stamp printing is delayed
4677 : // until the warmup is completed. The stamp should only be printed once
4678 : // per environment (set/reset of PrintEnvrnStamp). In addition, before
4679 : // the first environment, the end of the header block flag must also be
4680 : // sent to the output file.
4681 :
4682 701160 : if (printEnvrnStamp) {
4683 :
4684 210986 : if (state.dataReportFlag->PrintEndDataDictionary && state.dataGlobal->DoOutputReporting) {
4685 : static constexpr std::string_view EndOfHeaderString("End of Data Dictionary"); // End of data dictionary marker
4686 796 : print(state.files.eso, "{}\n", EndOfHeaderString);
4687 796 : print(state.files.mtr, "{}\n", EndOfHeaderString);
4688 796 : state.dataReportFlag->PrintEndDataDictionary = false;
4689 : }
4690 210986 : if (state.dataGlobal->DoOutputReporting) {
4691 1706 : std::string const &Title = state.dataWeather->Environment(state.dataWeather->Envrn).Title;
4692 : static constexpr std::string_view EnvironmentStampFormatStr(
4693 : "{},{},{:7.2F},{:7.2F},{:7.2F},{:7.2F}\n"); // Format descriptor for environ stamp
4694 1706 : print(state.files.eso,
4695 : EnvironmentStampFormatStr,
4696 1706 : state.dataWeather->EnvironmentReportChr,
4697 : Title,
4698 1706 : state.dataEnvrn->Latitude,
4699 1706 : state.dataEnvrn->Longitude,
4700 1706 : state.dataEnvrn->TimeZoneNumber,
4701 1706 : state.dataEnvrn->Elevation);
4702 1706 : print(state.files.mtr,
4703 : EnvironmentStampFormatStr,
4704 1706 : state.dataWeather->EnvironmentReportChr,
4705 : Title,
4706 1706 : state.dataEnvrn->Latitude,
4707 1706 : state.dataEnvrn->Longitude,
4708 1706 : state.dataEnvrn->TimeZoneNumber,
4709 1706 : state.dataEnvrn->Elevation);
4710 1706 : printEnvrnStamp = false;
4711 : }
4712 : }
4713 : } // ... end of .NOT.WarmupFlag IF-THEN block.
4714 2925340 : }
4715 :
4716 801 : void ReadUserWeatherInput(EnergyPlusData &state)
4717 : {
4718 :
4719 : // SUBROUTINE INFORMATION:
4720 : // AUTHOR Richard Liesen
4721 : // DATE WRITTEN September 1997
4722 :
4723 : // PURPOSE OF THIS SUBROUTINE:
4724 : // This subroutine is the main driver of the weather manager module.
4725 : // It controls the assignment of weather related global variables as
4726 : // well as the reads and writes for retrieving weather information.
4727 :
4728 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4729 801 : bool ErrorsFound(false);
4730 :
4731 : // Get the number of design days and annual runs from user inpout
4732 801 : state.dataEnvrn->TotDesDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:DesignDay");
4733 801 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
4734 801 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
4735 801 : state.dataWeather->TotRunPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriod");
4736 801 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4737 801 : state.dataGlobal->WeathSimReq = state.dataWeather->TotRunPers > 0;
4738 801 : state.dataWeather->TotReportPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Output:Table:ReportPeriod");
4739 : #ifdef GET_OUT
4740 : state.dataWeather->SPSiteScheduleNamePtr.allocate(state.dataEnvrn->TotDesDays * 5);
4741 : state.dataWeather->SPSiteScheduleUnits.allocate(state.dataEnvrn->TotDesDays * 5);
4742 :
4743 : state.dataWeather->SPSiteScheduleNamePtr = 0;
4744 : state.dataWeather->SPSiteScheduleUnits = "";
4745 : #endif //
4746 : // Allocate the Design Day and Environment array to the # of DD's or/and
4747 : // Annual runs on input file
4748 801 : state.dataWeather->DesignDay.allocate(state.dataEnvrn->TotDesDays);
4749 801 : state.dataWeather->Environment.allocate(state.dataWeather->NumOfEnvrn);
4750 :
4751 : // Set all Environments to DesignDay and then the weather environment will be set
4752 : // in the get annual run data subroutine
4753 2478 : for (int Env = 1; Env <= state.dataEnvrn->TotDesDays; ++Env) {
4754 1677 : state.dataWeather->Environment(Env).KindOfEnvrn = Constant::KindOfSim::DesignDay;
4755 : }
4756 806 : for (int Env = 1; Env <= RPD1 + RPD2; ++Env) {
4757 5 : if (!state.dataSysVars->DDOnly) {
4758 0 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
4759 : } else {
4760 5 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4761 : }
4762 : }
4763 1992 : for (int Env = 1; Env <= state.dataWeather->TotRunPers; ++Env) {
4764 1191 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + RPD1 + RPD2 + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4765 : }
4766 :
4767 801 : if (state.dataEnvrn->TotDesDays >= 1) {
4768 801 : GetDesignDayData(state, state.dataEnvrn->TotDesDays, ErrorsFound);
4769 : }
4770 :
4771 801 : if (RPD1 >= 1 || RPD2 >= 1) {
4772 3 : GetRunPeriodDesignData(state, ErrorsFound);
4773 : }
4774 :
4775 : // the last environment(s) is designated the weather environment if an annual run
4776 : // is selected. All of the design systems is done from the design day info
4777 : // which will have to be completed to run the annual run.
4778 801 : if (state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4779 776 : GetRunPeriodData(state, state.dataWeather->TotRunPers, ErrorsFound);
4780 : }
4781 :
4782 801 : if (state.dataWeather->TotReportPers > 0) {
4783 4 : GetReportPeriodData(state, state.dataWeather->TotReportPers, ErrorsFound);
4784 4 : GroupReportPeriodByType(state, state.dataWeather->TotReportPers);
4785 : }
4786 :
4787 801 : if (state.dataSysVars->FullAnnualRun) {
4788 : // GetRunPeriodData may have reset the value of TotRunPers
4789 7 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4790 : }
4791 :
4792 801 : if (RPD1 >= 1 || RPD2 >= 1 || state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4793 776 : GetSpecialDayPeriodData(state, ErrorsFound);
4794 776 : GetDSTData(state, ErrorsFound);
4795 776 : if (state.dataWeather->IDFDaylightSaving) {
4796 183 : state.dataWeather->DST = state.dataWeather->IDFDST;
4797 : }
4798 : }
4799 :
4800 801 : GetLocationInfo(state, ErrorsFound);
4801 :
4802 801 : GetGroundTemps(state);
4803 :
4804 801 : GetGroundReflectances(state, ErrorsFound);
4805 :
4806 801 : GetSnowGroundRefModifiers(state, ErrorsFound);
4807 :
4808 801 : GetWaterMainsTemperatures(state, ErrorsFound);
4809 :
4810 801 : GetWeatherStation(state, ErrorsFound);
4811 :
4812 801 : SetupEnvironmentTypes(state);
4813 :
4814 801 : GetWeatherProperties(state, ErrorsFound);
4815 : #ifdef GET_OUT
4816 : // Deallocate ones used for schedule pointers
4817 : state.dataWeather->SPSiteScheduleNamePtr.deallocate();
4818 : state.dataWeather->SPSiteScheduleUnits.deallocate();
4819 : #endif //
4820 801 : if (ErrorsFound) {
4821 0 : ShowFatalError(state, "GetWeatherInput: Above errors cause termination");
4822 : }
4823 801 : }
4824 :
4825 1037 : static int findYearForWeekday(int const month, int const day, Sched::DayType const weekday)
4826 : {
4827 : // Find a year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
4828 : // the date with the weekday specified.
4829 :
4830 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4831 : static std::array<int, 13> const defaultYear{{2013, 2014, 2015, 2010, 2011, 2017, 2007, 2013, 2014, 2015, 2010, 2011, 2017}};
4832 :
4833 1037 : int rem = calculateDayOfYear(month, day) % 7;
4834 1037 : return defaultYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4835 : }
4836 :
4837 0 : static int findLeapYearForWeekday(int const month, int const day, Sched::DayType const weekday)
4838 : {
4839 : // Find a leap year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
4840 : // the date with the weekday specified.
4841 :
4842 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4843 : static std::array<int, 13> const defaultLeapYear{{2008, 1992, 2004, 2016, 2000, 2012, 1996, 2008, 1992, 2004, 2016, 2000, 2012}};
4844 :
4845 0 : int rem = calculateDayOfYear(month, day, true) % 7;
4846 0 : return defaultLeapYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4847 : }
4848 :
4849 4 : void GetReportPeriodData(EnergyPlusData &state,
4850 : int nReportPeriods, // Total number of Report Periods requested
4851 : bool &ErrorsFound)
4852 : {
4853 4 : constexpr std::string_view routineName = "GetReportPeriodData";
4854 4 : state.dataWeather->ReportPeriodInput.allocate(nReportPeriods);
4855 :
4856 4 : auto const &ipsc = state.dataIPShortCut;
4857 4 : ipsc->cCurrentModuleObject = "Output:Table:ReportPeriod";
4858 4 : int Count = 0;
4859 : int NumAlpha; // Number of alphas being input
4860 : int NumNumeric; // Number of numbers being input
4861 : int IOStat; // IO Status when calling get input subroutine
4862 13 : for (int i = 1; i <= nReportPeriods; ++i) {
4863 18 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4864 9 : ipsc->cCurrentModuleObject,
4865 : i,
4866 9 : ipsc->cAlphaArgs,
4867 : NumAlpha,
4868 9 : ipsc->rNumericArgs,
4869 : NumNumeric,
4870 : IOStat,
4871 9 : ipsc->lNumericFieldBlanks,
4872 9 : ipsc->lAlphaFieldBlanks,
4873 9 : ipsc->cAlphaFieldNames,
4874 9 : ipsc->cNumericFieldNames);
4875 :
4876 9 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
4877 9 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
4878 : // A1, \field Name
4879 9 : if (std::find_if(state.dataWeather->ReportPeriodInput.begin(),
4880 9 : state.dataWeather->ReportPeriodInput.end(),
4881 48 : [&newName](ReportPeriodData const &rpd) { return newName == rpd.title; }) !=
4882 9 : state.dataWeather->ReportPeriodInput.end()) {
4883 0 : ShowSevereDuplicateName(state, eoh);
4884 0 : ErrorsFound = true;
4885 : }
4886 :
4887 9 : ++Count;
4888 :
4889 9 : auto &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4890 : // Loop = RP + Ptr;
4891 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "ReportPeriod 1" anyways
4892 : // which is fine for our reporting below
4893 9 : reportPeriodInput.title = newName;
4894 : // A2, \field Report Name
4895 9 : reportPeriodInput.reportName = ipsc->cAlphaArgs(2);
4896 :
4897 : // set the start and end day of month from user input
4898 : // N1, \field Begin Year
4899 : // N2, \field Begin Month
4900 : // N3, \field Begin Day of Month
4901 : // N4, \field Begin Hour of Day
4902 : // N5, \field End Year
4903 : // N6, \field End Month
4904 : // N7, \field End Day of Month
4905 : // N8; \field End Hour of Day
4906 9 : reportPeriodInput.startYear = int(ipsc->rNumericArgs(1));
4907 9 : reportPeriodInput.startMonth = int(ipsc->rNumericArgs(2));
4908 9 : reportPeriodInput.startDay = int(ipsc->rNumericArgs(3));
4909 9 : reportPeriodInput.startHour = int(ipsc->rNumericArgs(4));
4910 9 : reportPeriodInput.endYear = int(ipsc->rNumericArgs(5));
4911 9 : reportPeriodInput.endMonth = int(ipsc->rNumericArgs(6));
4912 9 : reportPeriodInput.endDay = int(ipsc->rNumericArgs(7));
4913 9 : reportPeriodInput.endHour = int(ipsc->rNumericArgs(8));
4914 :
4915 : // Validate year inputs
4916 9 : if (reportPeriodInput.startYear == 0) {
4917 9 : if (reportPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
4918 0 : ShowSevereError(state,
4919 0 : format("{}: object={}, end year cannot be specified if the start year is not.",
4920 0 : ipsc->cCurrentModuleObject,
4921 0 : reportPeriodInput.title));
4922 0 : ErrorsFound = true;
4923 : }
4924 0 : } else if (reportPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
4925 0 : ShowSevereError(state,
4926 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
4927 0 : ipsc->cCurrentModuleObject,
4928 0 : reportPeriodInput.title,
4929 0 : reportPeriodInput.startYear));
4930 0 : ErrorsFound = true;
4931 : }
4932 :
4933 9 : if (reportPeriodInput.endYear != 0 && reportPeriodInput.startYear > reportPeriodInput.endYear) {
4934 0 : ShowSevereError(state,
4935 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
4936 0 : ipsc->cCurrentModuleObject,
4937 0 : reportPeriodInput.title,
4938 0 : reportPeriodInput.startYear,
4939 0 : reportPeriodInput.endYear));
4940 0 : ErrorsFound = true;
4941 : }
4942 :
4943 9 : reportPeriodInput.startJulianDate =
4944 9 : computeJulianDate(reportPeriodInput.startYear, reportPeriodInput.startMonth, reportPeriodInput.startDay);
4945 9 : reportPeriodInput.endJulianDate = computeJulianDate(reportPeriodInput.endYear, reportPeriodInput.endMonth, reportPeriodInput.endDay);
4946 9 : }
4947 4 : }
4948 :
4949 4 : void GroupReportPeriodByType(EnergyPlusData &state, const int nReportPeriods)
4950 : {
4951 : // transfer data from the reporting period object to the corresponding report period type arrays
4952 : // ThermalResilienceSummary, CO2ResilienceSummary, VisualResilienceSummary, and AllResilienceSummaries
4953 13 : for (auto const &reportPeriodInput : state.dataWeather->ReportPeriodInput) {
4954 :
4955 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4956 4 : ++state.dataWeather->TotThermalReportPers;
4957 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4958 2 : ++state.dataWeather->TotCO2ReportPers;
4959 3 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4960 2 : ++state.dataWeather->TotVisualReportPers;
4961 1 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4962 1 : ++state.dataWeather->TotThermalReportPers;
4963 1 : ++state.dataWeather->TotCO2ReportPers;
4964 1 : ++state.dataWeather->TotVisualReportPers;
4965 : }
4966 : }
4967 :
4968 4 : state.dataWeather->ThermalReportPeriodInput.allocate(state.dataWeather->TotThermalReportPers);
4969 4 : state.dataWeather->CO2ReportPeriodInput.allocate(state.dataWeather->TotCO2ReportPers);
4970 4 : state.dataWeather->VisualReportPeriodInput.allocate(state.dataWeather->TotVisualReportPers);
4971 :
4972 13 : for (int i = 1, iThermal = 1, iVisual = 1, iCO2 = 1; i <= nReportPeriods; ++i) {
4973 9 : auto const &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4974 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4975 4 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4976 4 : ++iThermal;
4977 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4978 2 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4979 2 : ++iCO2;
4980 3 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4981 2 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4982 2 : ++iVisual;
4983 1 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4984 1 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4985 1 : ++iThermal;
4986 1 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4987 1 : ++iCO2;
4988 1 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4989 1 : ++iVisual;
4990 : }
4991 : }
4992 4 : }
4993 :
4994 776 : void GetRunPeriodData(EnergyPlusData &state,
4995 : int nRunPeriods, // Total number of Run Periods requested
4996 : bool &ErrorsFound)
4997 : {
4998 :
4999 : // SUBROUTINE INFORMATION:
5000 : // AUTHOR Richard Liesen
5001 : // DATE WRITTEN October 1997
5002 : // MODIFIED February 1999, Add multiple run periods, Change name.
5003 : // March 2012, LKL, Add features to object; New "actual weather" object;
5004 :
5005 : // PURPOSE OF THIS SUBROUTINE:
5006 : // This subroutine gets the run period info from User input and the
5007 : // simulation dates
5008 :
5009 776 : constexpr std::string_view routineName = "GetRunPeriodData";
5010 : // Call Input Get routine to retrieve annual run data
5011 776 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
5012 :
5013 776 : auto const &ipsc = state.dataIPShortCut;
5014 776 : ipsc->cCurrentModuleObject = "RunPeriod";
5015 776 : int Count = 0;
5016 : int NumAlpha; // Number of alphas being input
5017 : int NumNumeric; // Number of numbers being input
5018 : int IOStat; // IO Status when calling get input subroutine
5019 1967 : for (int i = 1; i <= nRunPeriods; ++i) {
5020 2382 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5021 1191 : ipsc->cCurrentModuleObject,
5022 : i,
5023 1191 : ipsc->cAlphaArgs,
5024 : NumAlpha,
5025 1191 : ipsc->rNumericArgs,
5026 : NumNumeric,
5027 : IOStat,
5028 1191 : ipsc->lNumericFieldBlanks,
5029 1191 : ipsc->lAlphaFieldBlanks,
5030 1191 : ipsc->cAlphaFieldNames,
5031 1191 : ipsc->cNumericFieldNames);
5032 :
5033 : // A1, \field Name
5034 1191 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5035 1191 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5036 :
5037 1191 : if (std::find_if(state.dataWeather->RunPeriodInput.begin(),
5038 1191 : state.dataWeather->RunPeriodInput.end(),
5039 3410 : [&newName](RunPeriodData const &rpd) { return rpd.title == newName; }) != state.dataWeather->RunPeriodInput.end()) {
5040 0 : ShowSevereDuplicateName(state, eoh);
5041 0 : ErrorsFound = true;
5042 : }
5043 :
5044 1191 : ++Count;
5045 : // Loop = RP + Ptr;
5046 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "RUNPERIOD 1" anyways
5047 : // which is fine for our reporting below
5048 1191 : auto &runPeriodInput = state.dataWeather->RunPeriodInput(i);
5049 1191 : runPeriodInput.title = ipsc->cAlphaArgs(1);
5050 :
5051 : // set the start and end day of month from user input
5052 : // N1 , \field Begin Month
5053 : // N2 , \field Begin Day of Month
5054 : // N3, \field Start Year
5055 : // N4 , \field End Month
5056 : // N5 , \field End Day of Month
5057 : // N6, \field End Year
5058 1191 : runPeriodInput.startMonth = int(ipsc->rNumericArgs(1));
5059 1191 : runPeriodInput.startDay = int(ipsc->rNumericArgs(2));
5060 1191 : runPeriodInput.startYear = int(ipsc->rNumericArgs(3));
5061 1191 : runPeriodInput.endMonth = int(ipsc->rNumericArgs(4));
5062 1191 : runPeriodInput.endDay = int(ipsc->rNumericArgs(5));
5063 1191 : runPeriodInput.endYear = int(ipsc->rNumericArgs(6));
5064 1191 : runPeriodInput.TreatYearsAsConsecutive = true;
5065 :
5066 1191 : if (state.dataSysVars->FullAnnualRun && i == 1) {
5067 7 : runPeriodInput.startMonth = 1;
5068 7 : runPeriodInput.startDay = 1;
5069 7 : runPeriodInput.endMonth = 12;
5070 7 : runPeriodInput.endDay = 31;
5071 : }
5072 :
5073 : // Validate year inputs
5074 1191 : if (runPeriodInput.startYear == 0) {
5075 1182 : if (runPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
5076 0 : ShowSevereError(state,
5077 0 : format("{}: object={}, end year cannot be specified if the start year is not.",
5078 0 : ipsc->cCurrentModuleObject,
5079 0 : runPeriodInput.title));
5080 0 : ErrorsFound = true;
5081 : }
5082 9 : } else if (runPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
5083 0 : ShowSevereError(state,
5084 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
5085 0 : ipsc->cCurrentModuleObject,
5086 0 : runPeriodInput.title,
5087 0 : runPeriodInput.startYear));
5088 0 : ErrorsFound = true;
5089 : }
5090 :
5091 1191 : if (runPeriodInput.endYear != 0 && runPeriodInput.startYear > runPeriodInput.endYear) {
5092 0 : ShowSevereError(state,
5093 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
5094 0 : ipsc->cCurrentModuleObject,
5095 0 : runPeriodInput.title,
5096 0 : runPeriodInput.startYear,
5097 0 : runPeriodInput.endYear));
5098 0 : ErrorsFound = true;
5099 : }
5100 :
5101 : // A2 , \field Day of Week for Start Day
5102 1191 : bool inputWeekday = false;
5103 1191 : if (!state.dataIPShortCut->lAlphaFieldBlanks(2)) { // Have input
5104 1044 : int dayType = getEnumValue(Sched::dayTypeNamesUC, state.dataIPShortCut->cAlphaArgs(2));
5105 1044 : if (dayType < 1) {
5106 0 : ShowWarningError(state,
5107 0 : format("{}: object={}{} invalid (Day of Week) [{}] for Start is not valid, Sunday will be used.",
5108 0 : state.dataIPShortCut->cCurrentModuleObject,
5109 0 : state.dataWeather->RunPeriodInput(i).title,
5110 0 : state.dataIPShortCut->cAlphaFieldNames(2),
5111 0 : state.dataIPShortCut->cAlphaArgs(2)));
5112 0 : runPeriodInput.startWeekDay = Sched::DayType::Sunday;
5113 : } else {
5114 1044 : runPeriodInput.startWeekDay = static_cast<Sched::DayType>(dayType);
5115 1044 : inputWeekday = true;
5116 : }
5117 : } else { // No input, set the default as Sunday. This may get overriden below
5118 147 : runPeriodInput.startWeekDay = Sched::DayType::Sunday;
5119 : }
5120 :
5121 : // Validate the dates now that the weekday field has been looked at
5122 1191 : if (runPeriodInput.startMonth == 2 && runPeriodInput.startDay == 29) {
5123 : // Requested start date is a leap year
5124 0 : if (runPeriodInput.startYear == 0) { // No input starting year
5125 0 : if (inputWeekday) {
5126 0 : runPeriodInput.startYear =
5127 0 : findLeapYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
5128 : } else {
5129 : // 2012 is the default year, 1/1 is a Sunday
5130 0 : runPeriodInput.startYear = 2012;
5131 0 : runPeriodInput.startWeekDay =
5132 0 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5133 : }
5134 : } else { // Have an input start year
5135 0 : if (!isLeapYear(runPeriodInput.startYear)) { // Start year is not a leap year
5136 0 : ShowSevereError(state,
5137 0 : format("{}: object={}, start year ({}) is not a leap year but the requested start date is 2/29.",
5138 0 : ipsc->cCurrentModuleObject,
5139 0 : runPeriodInput.title,
5140 0 : runPeriodInput.startYear));
5141 0 : ErrorsFound = true;
5142 : } else { // Start year is a leap year
5143 : Sched::DayType weekday =
5144 0 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5145 0 : if (inputWeekday) { // Check for correctness of input
5146 0 : if (weekday != runPeriodInput.startWeekDay) {
5147 0 : ShowWarningError(state,
5148 0 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
5149 0 : ipsc->cCurrentModuleObject,
5150 0 : runPeriodInput.title,
5151 0 : ipsc->cAlphaArgs(2),
5152 0 : runPeriodInput.startYear,
5153 0 : Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
5154 0 : runPeriodInput.startWeekDay = weekday;
5155 : }
5156 : } else { // Set the weekday if it was not input
5157 0 : runPeriodInput.startWeekDay = weekday;
5158 : }
5159 : }
5160 : }
5161 0 : } else {
5162 : // Non leap-day start date
5163 1191 : if (!validMonthDay(runPeriodInput.startMonth, runPeriodInput.startDay)) {
5164 0 : ShowSevereError(state,
5165 0 : format("{}: object={}, Invalid input start month/day ({}/{})",
5166 0 : ipsc->cCurrentModuleObject,
5167 0 : runPeriodInput.title,
5168 0 : runPeriodInput.startMonth,
5169 0 : runPeriodInput.startDay));
5170 0 : ErrorsFound = true;
5171 : } else { // Month/day is valid
5172 1191 : if (runPeriodInput.startYear == 0) { // No input starting year
5173 1182 : if (inputWeekday) {
5174 1037 : runPeriodInput.startYear =
5175 1037 : findYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
5176 : } else {
5177 : // 2017 is the default year, 1/1 is a Sunday
5178 145 : runPeriodInput.startYear = 2017;
5179 145 : runPeriodInput.startWeekDay =
5180 145 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5181 : }
5182 : } else { // Have an input starting year
5183 : Sched::DayType weekday =
5184 9 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5185 9 : if (inputWeekday) { // Check for correctness of input
5186 7 : if (weekday != runPeriodInput.startWeekDay) {
5187 2 : ShowWarningError(state,
5188 3 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
5189 1 : ipsc->cCurrentModuleObject,
5190 1 : runPeriodInput.title,
5191 1 : ipsc->cAlphaArgs(2),
5192 1 : runPeriodInput.startYear,
5193 1 : Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
5194 1 : runPeriodInput.startWeekDay = weekday;
5195 : }
5196 : } else { // Set the weekday if it was not input
5197 2 : runPeriodInput.startWeekDay = weekday;
5198 : }
5199 : }
5200 : }
5201 : }
5202 :
5203 : // Compute the Julian date of the start date
5204 1191 : runPeriodInput.startJulianDate = computeJulianDate(runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5205 :
5206 : // Validate the end date
5207 1191 : if (runPeriodInput.endMonth == 2 && runPeriodInput.endDay == 29) {
5208 : // Requested end date is a leap year
5209 0 : if (runPeriodInput.endYear == 0) { // No input end year
5210 0 : if (isLeapYear(runPeriodInput.startYear) && runPeriodInput.startMonth < 3) {
5211 : // The run period is from some date on or before 2/29 through 2/29
5212 0 : runPeriodInput.endYear = runPeriodInput.startYear;
5213 : } else {
5214 : // There might be a better approach here, but for now just loop forward for the next leap year
5215 0 : for (int yr = runPeriodInput.startYear + 1; yr < runPeriodInput.startYear + 10; yr++) {
5216 0 : if (isLeapYear(yr)) {
5217 0 : runPeriodInput.endYear = yr;
5218 0 : break;
5219 : }
5220 : }
5221 : }
5222 : } else { // Have an input end year
5223 0 : if (!isLeapYear(runPeriodInput.endYear)) { // End year is not a leap year
5224 0 : ShowSevereError(state,
5225 0 : format("{}: object={}, end year ({}) is not a leap year but the requested end date is 2/29.",
5226 0 : ipsc->cCurrentModuleObject,
5227 0 : runPeriodInput.title,
5228 0 : runPeriodInput.startYear));
5229 0 : ErrorsFound = true;
5230 : } else {
5231 0 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5232 0 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5233 0 : ShowSevereError(state,
5234 0 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5235 0 : ipsc->cCurrentModuleObject,
5236 0 : runPeriodInput.title,
5237 0 : runPeriodInput.startJulianDate,
5238 0 : runPeriodInput.endJulianDate));
5239 0 : ErrorsFound = true;
5240 : }
5241 : }
5242 : }
5243 0 : } else {
5244 : // Non leap-day end date
5245 1191 : if (!validMonthDay(runPeriodInput.endMonth, runPeriodInput.endDay)) {
5246 0 : ShowSevereError(state,
5247 0 : format("{}: object={}, Invalid input end month/day ({}/{})",
5248 0 : ipsc->cCurrentModuleObject,
5249 0 : runPeriodInput.title,
5250 0 : runPeriodInput.startMonth,
5251 0 : runPeriodInput.startDay));
5252 0 : ErrorsFound = true;
5253 : } else { // Month/day is valid
5254 1191 : if (runPeriodInput.endYear == 0) { // No input end year
5255 : // Assume same year as start year
5256 1182 : runPeriodInput.endYear = runPeriodInput.startYear;
5257 1182 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5258 1182 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5259 5 : runPeriodInput.endJulianDate = 0; // Force recalculation later
5260 5 : runPeriodInput.endYear += 1;
5261 : }
5262 : } else { // Have an input end year
5263 9 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5264 9 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5265 0 : ShowSevereError(state,
5266 0 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5267 0 : ipsc->cCurrentModuleObject,
5268 0 : runPeriodInput.title,
5269 0 : runPeriodInput.startJulianDate,
5270 0 : runPeriodInput.endJulianDate));
5271 0 : ErrorsFound = true;
5272 : }
5273 : }
5274 : }
5275 : }
5276 :
5277 1191 : if (runPeriodInput.endJulianDate == 0) {
5278 5 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5279 : }
5280 :
5281 1191 : runPeriodInput.numSimYears = runPeriodInput.endYear - runPeriodInput.startYear + 1;
5282 :
5283 : // A3, \field Use Weather File Holidays and Special Days
5284 : BooleanSwitch b;
5285 1191 : if (ipsc->lAlphaFieldBlanks(3)) {
5286 9 : runPeriodInput.useHolidays = true;
5287 1182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5288 1182 : runPeriodInput.useHolidays = static_cast<bool>(b);
5289 : } else {
5290 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5291 0 : ErrorsFound = true;
5292 : }
5293 :
5294 : // A4, \field Use Weather File Daylight Saving Period
5295 1191 : if (ipsc->lAlphaFieldBlanks(4)) {
5296 9 : runPeriodInput.useDST = true;
5297 1182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5298 1182 : runPeriodInput.useDST = static_cast<bool>(b);
5299 : } else {
5300 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5301 0 : ErrorsFound = true;
5302 : }
5303 :
5304 : // A5, \field Apply Weekend Holiday Rule
5305 1191 : if (ipsc->lAlphaFieldBlanks(5)) {
5306 18 : runPeriodInput.applyWeekendRule = true;
5307 1173 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5308 1173 : runPeriodInput.applyWeekendRule = static_cast<bool>(b);
5309 : } else {
5310 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5311 0 : ErrorsFound = true;
5312 : }
5313 :
5314 : // A6, \field Use Weather File Rain Indicators
5315 1191 : if (ipsc->lAlphaFieldBlanks(6)) {
5316 18 : runPeriodInput.useRain = true;
5317 1173 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(6)))) != BooleanSwitch::Invalid) {
5318 1173 : runPeriodInput.useRain = static_cast<bool>(b);
5319 : } else {
5320 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6));
5321 0 : ErrorsFound = true;
5322 : }
5323 :
5324 : // A7, \field Use Weather File Snow Indicators
5325 1191 : if (ipsc->lAlphaFieldBlanks(7)) {
5326 18 : runPeriodInput.useSnow = true;
5327 1173 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
5328 1173 : runPeriodInput.useSnow = static_cast<bool>(b);
5329 : } else {
5330 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7));
5331 0 : ErrorsFound = true;
5332 : }
5333 :
5334 : // A8, \field Treat Weather as Actual
5335 1191 : if (ipsc->lAlphaFieldBlanks(8)) {
5336 1191 : runPeriodInput.actualWeather = false;
5337 0 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
5338 0 : runPeriodInput.actualWeather = static_cast<bool>(b);
5339 : } else {
5340 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8));
5341 0 : ErrorsFound = true;
5342 : }
5343 :
5344 : // A9, \field First Hour Interpolation Starting Values
5345 1191 : if (ipsc->lAlphaFieldBlanks(9) || Util::SameString(ipsc->cAlphaArgs(8), "Hour24")) {
5346 1191 : runPeriodInput.firstHrInterpUsingHr1 = false;
5347 0 : } else if (Util::SameString(ipsc->cAlphaArgs(9), "Hour1")) {
5348 0 : runPeriodInput.firstHrInterpUsingHr1 = true;
5349 : } else {
5350 : // fail-safe default
5351 0 : runPeriodInput.firstHrInterpUsingHr1 = false;
5352 : }
5353 :
5354 1191 : runPeriodInput.dayOfWeek = static_cast<int>(runPeriodInput.startWeekDay);
5355 1191 : runPeriodInput.isLeapYear = isLeapYear(runPeriodInput.startYear);
5356 :
5357 : // calculate the annual start and end days from the user inputted month and day
5358 1191 : runPeriodInput.monWeekDay = 0;
5359 1191 : if (runPeriodInput.dayOfWeek != 0 && !ErrorsFound) {
5360 1191 : SetupWeekDaysByMonth(state, runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.dayOfWeek, runPeriodInput.monWeekDay);
5361 : }
5362 1191 : }
5363 :
5364 776 : if (nRunPeriods == 0 && state.dataSysVars->FullAnnualRun) {
5365 0 : ShowWarningError(state, "No Run Periods input but Full Annual Simulation selected. Adding Run Period to 1/1 through 12/31.");
5366 0 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
5367 0 : state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
5368 0 : nRunPeriods = 1;
5369 0 : state.dataGlobal->WeathSimReq = true;
5370 0 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
5371 0 : auto &runPerInput1 = state.dataWeather->RunPeriodInput(1);
5372 0 : runPerInput1.startJulianDate = General::OrdinalDay(runPerInput1.startMonth, runPerInput1.startDay, state.dataWeather->LeapYearAdd);
5373 0 : runPerInput1.endJulianDate = General::OrdinalDay(runPerInput1.endMonth, runPerInput1.endDay, state.dataWeather->LeapYearAdd);
5374 0 : runPerInput1.monWeekDay = 0;
5375 0 : if (runPerInput1.dayOfWeek != 0 && !ErrorsFound) {
5376 0 : SetupWeekDaysByMonth(state, runPerInput1.startMonth, runPerInput1.startDay, runPerInput1.dayOfWeek, runPerInput1.monWeekDay);
5377 : }
5378 776 : } else if (nRunPeriods > 1 && state.dataSysVars->FullAnnualRun) {
5379 0 : nRunPeriods = 1;
5380 : }
5381 776 : }
5382 :
5383 3 : void GetRunPeriodDesignData(EnergyPlusData &state, bool &ErrorsFound)
5384 : {
5385 :
5386 : // SUBROUTINE INFORMATION:
5387 : // AUTHOR Linda Lawrie
5388 : // DATE WRITTEN March 2008
5389 :
5390 : // PURPOSE OF THIS SUBROUTINE:
5391 : // This subroutine gets the run period design info from User input and the
5392 : // simulation dates
5393 :
5394 3 : constexpr std::string_view routineName = "GetRunPeriodDesignData";
5395 : // Call Input Get routine to retrieve annual run data
5396 3 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
5397 3 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
5398 3 : state.dataWeather->TotRunDesPers = RPD1 + RPD2;
5399 :
5400 3 : state.dataWeather->RunPeriodDesignInput.allocate(RPD1 + RPD2);
5401 :
5402 3 : int Count = 0;
5403 3 : auto const &ipsc = state.dataIPShortCut;
5404 3 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileDays";
5405 6 : for (int i = 1; i <= RPD1; ++i) {
5406 : int NumAlphas; // Number of alphas being input
5407 : int NumNumerics; // Number of Numerics being input
5408 : int IOStat; // IO Status when calling get input subroutine
5409 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5410 3 : ipsc->cCurrentModuleObject,
5411 : i,
5412 3 : ipsc->cAlphaArgs,
5413 : NumAlphas,
5414 3 : ipsc->rNumericArgs,
5415 : NumNumerics,
5416 : IOStat,
5417 3 : ipsc->lNumericFieldBlanks,
5418 3 : ipsc->lAlphaFieldBlanks,
5419 3 : ipsc->cAlphaFieldNames,
5420 3 : ipsc->cNumericFieldNames);
5421 :
5422 3 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5423 3 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5424 6 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5425 6 : state.dataWeather->RunPeriodDesignInput.end(),
5426 8 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5427 6 : state.dataWeather->RunPeriodDesignInput.end()) {
5428 0 : ShowSevereDuplicateName(state, eoh);
5429 0 : ErrorsFound = true;
5430 : }
5431 :
5432 3 : ++Count;
5433 :
5434 3 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5435 3 : runPerDesInput.title = newName;
5436 3 : runPerDesInput.periodType = "User Selected WeatherFile RunPeriod (Design)";
5437 :
5438 : // set the start and end day of month from user input
5439 3 : runPerDesInput.startMonth = int(ipsc->rNumericArgs(1));
5440 3 : runPerDesInput.startDay = int(ipsc->rNumericArgs(2));
5441 3 : runPerDesInput.endMonth = int(ipsc->rNumericArgs(3));
5442 3 : runPerDesInput.endDay = int(ipsc->rNumericArgs(4));
5443 :
5444 3 : switch (runPerDesInput.startMonth) {
5445 3 : case 1:
5446 : case 3:
5447 : case 5:
5448 : case 7:
5449 : case 8:
5450 : case 10:
5451 : case 12: {
5452 3 : if (runPerDesInput.startDay > 31) {
5453 0 : ShowSevereError(state,
5454 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5455 0 : ipsc->cCurrentModuleObject,
5456 0 : runPerDesInput.title,
5457 0 : ipsc->cNumericFieldNames(2),
5458 0 : runPerDesInput.startDay));
5459 0 : ErrorsFound = true;
5460 : }
5461 3 : } break;
5462 0 : case 4:
5463 : case 6:
5464 : case 9:
5465 : case 11: {
5466 0 : if (runPerDesInput.startDay > 30) {
5467 0 : ShowSevereError(state,
5468 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5469 0 : ipsc->cCurrentModuleObject,
5470 0 : runPerDesInput.title,
5471 0 : ipsc->cNumericFieldNames(2),
5472 0 : runPerDesInput.startDay));
5473 0 : ErrorsFound = true;
5474 : }
5475 0 : } break;
5476 0 : case 2: {
5477 0 : if (runPerDesInput.startDay > 28 + state.dataWeather->LeapYearAdd) {
5478 0 : ShowSevereError(state,
5479 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5480 0 : ipsc->cCurrentModuleObject,
5481 0 : runPerDesInput.title,
5482 0 : ipsc->cNumericFieldNames(2),
5483 0 : runPerDesInput.startDay));
5484 0 : ErrorsFound = true;
5485 : }
5486 0 : } break;
5487 0 : default: {
5488 0 : ShowSevereError(state,
5489 0 : format("{}: object={} {} invalid (Month) [{}]",
5490 0 : ipsc->cCurrentModuleObject,
5491 0 : runPerDesInput.title,
5492 0 : ipsc->cNumericFieldNames(1),
5493 0 : runPerDesInput.startMonth));
5494 0 : ErrorsFound = true;
5495 0 : } break;
5496 : } // switch
5497 :
5498 3 : if (ipsc->lAlphaFieldBlanks(2)) {
5499 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5500 : } else {
5501 3 : runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
5502 3 : if (runPerDesInput.dayOfWeek < 1 || runPerDesInput.dayOfWeek == 8) {
5503 0 : ShowWarningError(state,
5504 0 : format("{}: object={} {} invalid (Day of Week) [{} for Start is not Valid, Monday will be Used.",
5505 0 : ipsc->cCurrentModuleObject,
5506 0 : runPerDesInput.title,
5507 0 : ipsc->cAlphaFieldNames(1),
5508 0 : ipsc->cAlphaArgs(1)));
5509 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5510 : }
5511 : }
5512 :
5513 : BooleanSwitch b;
5514 3 : if (ipsc->lAlphaFieldBlanks(3)) {
5515 0 : runPerDesInput.useDST = true;
5516 3 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5517 3 : runPerDesInput.useDST = static_cast<bool>(b);
5518 : } else {
5519 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5520 0 : ErrorsFound = true;
5521 : }
5522 :
5523 3 : if (ipsc->lAlphaFieldBlanks(4)) {
5524 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5525 3 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5526 3 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5527 : } else {
5528 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5529 0 : ErrorsFound = true;
5530 : }
5531 :
5532 : // calculate the annual start and end days from the user inputted month and day
5533 3 : runPerDesInput.startJulianDate = General::OrdinalDay(runPerDesInput.startMonth, runPerDesInput.startDay, state.dataWeather->LeapYearAdd);
5534 3 : runPerDesInput.endJulianDate = General::OrdinalDay(runPerDesInput.endMonth, runPerDesInput.endDay, state.dataWeather->LeapYearAdd);
5535 3 : if (runPerDesInput.startJulianDate <= runPerDesInput.endJulianDate) {
5536 3 : runPerDesInput.totalDays = (runPerDesInput.endJulianDate - runPerDesInput.startJulianDate + 1) * runPerDesInput.numSimYears;
5537 : } else {
5538 0 : runPerDesInput.totalDays = (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - runPerDesInput.startJulianDate + 1 +
5539 0 : runPerDesInput.endJulianDate) *
5540 0 : runPerDesInput.numSimYears;
5541 : }
5542 3 : runPerDesInput.monWeekDay = 0;
5543 3 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5544 3 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5545 3 : SetupWeekDaysByMonth(state,
5546 : runPeriodDesignInput1.startMonth,
5547 : runPeriodDesignInput1.startDay,
5548 : runPeriodDesignInput1.dayOfWeek,
5549 3 : runPeriodDesignInput1.monWeekDay);
5550 : }
5551 3 : }
5552 :
5553 3 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileConditionType";
5554 5 : for (int i = 1; i <= RPD2; ++i) {
5555 : int NumAlphas; // Number of alphas being input
5556 : int NumNumerics; // Number of Numerics being input
5557 : int IOStat; // IO Status when calling get input subroutine
5558 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5559 2 : ipsc->cCurrentModuleObject,
5560 : i,
5561 2 : ipsc->cAlphaArgs,
5562 : NumAlphas,
5563 2 : ipsc->rNumericArgs,
5564 : NumNumerics,
5565 : IOStat,
5566 2 : ipsc->lNumericFieldBlanks,
5567 2 : ipsc->lAlphaFieldBlanks,
5568 2 : ipsc->cAlphaFieldNames,
5569 2 : ipsc->cNumericFieldNames);
5570 2 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5571 :
5572 2 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5573 4 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5574 4 : state.dataWeather->RunPeriodDesignInput.end(),
5575 6 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5576 4 : state.dataWeather->RunPeriodDesignInput.end()) {
5577 0 : ShowSevereDuplicateName(state, eoh);
5578 0 : ErrorsFound = true;
5579 : }
5580 :
5581 2 : ++Count;
5582 2 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5583 2 : runPerDesInput.title = ipsc->cAlphaArgs(1);
5584 2 : runPerDesInput.periodType = "User Selected WeatherFile Typical/Extreme Period (Design)=" + ipsc->cAlphaArgs(2);
5585 :
5586 : // Period Selection
5587 2 : if (ipsc->lAlphaFieldBlanks(2)) {
5588 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
5589 0 : ErrorsFound = true;
5590 : } else {
5591 2 : int WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue);
5592 2 : if (WhichPeriod == 0) {
5593 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue1);
5594 : if (WhichPeriod != 0) {
5595 : }
5596 : }
5597 2 : if (WhichPeriod == 0) {
5598 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue2);
5599 0 : if (WhichPeriod != 0) {
5600 0 : ShowWarningError(state,
5601 0 : format("{}: object={} {}={} matched to {}",
5602 0 : ipsc->cCurrentModuleObject,
5603 0 : runPerDesInput.title,
5604 0 : ipsc->cAlphaFieldNames(2),
5605 0 : ipsc->cAlphaArgs(2),
5606 0 : state.dataWeather->TypicalExtremePeriods(WhichPeriod).MatchValue2));
5607 : }
5608 : }
5609 2 : if (WhichPeriod == 0) {
5610 0 : ShowSevereError(state,
5611 0 : format("{}: object={} {} invalid (not on Weather File)={}",
5612 0 : ipsc->cCurrentModuleObject,
5613 0 : runPerDesInput.title,
5614 0 : ipsc->cAlphaFieldNames(2),
5615 0 : ipsc->cAlphaArgs(2)));
5616 0 : ErrorsFound = true;
5617 : } else {
5618 2 : auto const &typicalExtPer = state.dataWeather->TypicalExtremePeriods(WhichPeriod);
5619 2 : runPerDesInput.startDay = typicalExtPer.StartDay;
5620 2 : runPerDesInput.startMonth = typicalExtPer.StartMonth;
5621 2 : runPerDesInput.startJulianDate = typicalExtPer.StartJDay;
5622 2 : runPerDesInput.endDay = typicalExtPer.EndDay;
5623 2 : runPerDesInput.endMonth = typicalExtPer.EndMonth;
5624 2 : runPerDesInput.endJulianDate = typicalExtPer.EndJDay;
5625 2 : runPerDesInput.totalDays = typicalExtPer.TotalDays;
5626 : }
5627 : }
5628 :
5629 2 : if (ipsc->lAlphaFieldBlanks(3)) {
5630 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5631 : } else {
5632 2 : runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(3));
5633 2 : if (runPerDesInput.dayOfWeek < (int)Sched::DayType::Sunday || runPerDesInput.dayOfWeek == (int)Sched::DayType::Holiday) {
5634 : // Sunday-Saturday, SummerDesignDay, WinterDesignDay, CustomDay1, and CustomDay2 are all valid. Holiday is not valid.
5635 : // The input processor should trap invalid key choices, so this should never trip.
5636 0 : assert(false);
5637 : }
5638 : }
5639 :
5640 : BooleanSwitch b;
5641 2 : if (ipsc->lAlphaFieldBlanks(4)) {
5642 0 : runPerDesInput.useDST = true;
5643 2 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5644 2 : runPerDesInput.useDST = static_cast<bool>(b);
5645 : } else {
5646 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5647 0 : ErrorsFound = true;
5648 : }
5649 :
5650 2 : if (ipsc->lAlphaFieldBlanks(5)) {
5651 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5652 2 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5653 2 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5654 : } else {
5655 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5656 0 : ErrorsFound = true;
5657 : }
5658 2 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5659 2 : runPeriodDesignInput1.monWeekDay = 0;
5660 2 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5661 2 : SetupWeekDaysByMonth(state,
5662 : runPeriodDesignInput1.startMonth,
5663 : runPeriodDesignInput1.startDay,
5664 : runPeriodDesignInput1.dayOfWeek,
5665 2 : runPeriodDesignInput1.monWeekDay);
5666 : }
5667 2 : }
5668 3 : }
5669 :
5670 776 : void GetSpecialDayPeriodData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5671 : {
5672 :
5673 : // SUBROUTINE INFORMATION:
5674 : // AUTHOR Linda Lawrie
5675 : // DATE WRITTEN June 2000
5676 :
5677 : // PURPOSE OF THIS SUBROUTINE:
5678 : // This subroutine reads any special day period data from the IDF and
5679 : // processes it into the data structure that will drive the values
5680 : // in the SpecialDayTypes array.
5681 :
5682 : // METHODOLOGY EMPLOYED:
5683 : // Processes the following IDD definition:
5684 : // SpecialDayPeriod,
5685 : // \memo This object sets up holidays/special days to be used during weather file
5686 : // \memo run periods. (These are not used with DesignDay objects.)
5687 : // \memo Depending on the value in the run period, days on the weather file may also
5688 : // \memo be used. However, the weather file specification will take precedence over
5689 : // \memo any specification shown here. (No error message on duplicate days or overlapping
5690 : // \memo days).
5691 : // A1, \field Holiday Name
5692 : // A2, \field StartDate
5693 : // \memo Dates can be several formats:
5694 : // \memo <number>/<number> (month/day)
5695 : // \memo <number> Month
5696 : // \memo Month <number>
5697 : // \memo Months are January, February, March, April, May, June, July, August, September, October, November, December
5698 : // \memo Months can be the first 3 letters of the month
5699 : // \note will eventually allow: 3 Monday April (meaning 3rd Monday in April)
5700 : // N1, \field duration (number of days)
5701 : // A3; \field SpecialDayType
5702 : // \note SpecialDayType selects the schedules appropriate for each day so labeled
5703 : // \type choice
5704 : // \key Holiday
5705 : // \key SummerDesignDay
5706 : // \key WinterDesignDay
5707 : // \key CustomDay1
5708 : // \key CustomDay2
5709 :
5710 776 : constexpr std::string_view routineName = "GetSpecialDayPeriodData";
5711 :
5712 776 : auto const &ipsc = state.dataIPShortCut;
5713 776 : ipsc->cCurrentModuleObject = "RunPeriodControl:SpecialDays";
5714 776 : int NumSpecDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5715 : int Count;
5716 776 : if (allocated(state.dataWeather->SpecialDays)) { // EPW already allocated the array
5717 768 : Count = state.dataWeather->NumSpecialDays - NumSpecDays + 1;
5718 : } else {
5719 8 : state.dataWeather->SpecialDays.allocate(NumSpecDays);
5720 8 : state.dataWeather->NumSpecialDays = NumSpecDays;
5721 8 : Count = 1;
5722 : }
5723 :
5724 776 : Array1D_string AlphArray(3);
5725 : int NumAlphas;
5726 776 : Array1D<Real64> Duration(1);
5727 : int NumNumbers;
5728 : int IOStat;
5729 :
5730 2085 : for (int i = 1; i <= NumSpecDays; ++i, ++Count) {
5731 :
5732 2618 : state.dataInputProcessing->inputProcessor->getObjectItem(
5733 1309 : state, ipsc->cCurrentModuleObject, i, AlphArray, NumAlphas, Duration, NumNumbers, IOStat);
5734 :
5735 1309 : auto &specialDay = state.dataWeather->SpecialDays(Count);
5736 :
5737 1309 : specialDay.Name = AlphArray(1);
5738 1309 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, specialDay.Name};
5739 :
5740 : int PMonth;
5741 : int PDay;
5742 : int PWeekDay;
5743 : DateType dateType;
5744 1309 : General::ProcessDateString(state, AlphArray(2), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
5745 1309 : if (dateType == DateType::MonthDay) {
5746 530 : specialDay.dateType = dateType;
5747 530 : specialDay.Month = PMonth;
5748 530 : specialDay.Day = PDay;
5749 530 : specialDay.WeekDay = 0;
5750 530 : specialDay.CompDate = PMonth * 32 + PDay;
5751 530 : specialDay.WthrFile = false;
5752 779 : } else if (dateType != DateType::Invalid) {
5753 779 : specialDay.dateType = dateType;
5754 779 : specialDay.Month = PMonth;
5755 779 : specialDay.Day = PDay;
5756 779 : specialDay.WeekDay = PWeekDay;
5757 779 : specialDay.CompDate = 0;
5758 779 : specialDay.WthrFile = false;
5759 : } else {
5760 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
5761 0 : ErrorsFound = true;
5762 : }
5763 :
5764 1309 : if (Duration(1) > 0) {
5765 1309 : specialDay.Duration = int(Duration(1));
5766 : } else {
5767 0 : ShowSevereError(
5768 0 : state, format("{}: {} Invalid {}={:.0T}", ipsc->cCurrentModuleObject, AlphArray(1), ipsc->cNumericFieldNames(1), Duration(1)));
5769 0 : ErrorsFound = true;
5770 : }
5771 :
5772 1309 : int DayType = getEnumValue(Sched::dayTypeNamesUC, AlphArray(3));
5773 1309 : if (DayType == 0) {
5774 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), AlphArray(3));
5775 0 : ErrorsFound = true;
5776 : } else {
5777 1309 : specialDay.DayType = DayType;
5778 : }
5779 : }
5780 776 : }
5781 :
5782 7 : void CalcSpecialDayTypes(EnergyPlusData &state)
5783 : {
5784 :
5785 : // SUBROUTINE INFORMATION:
5786 : // AUTHOR Linda Lawrie
5787 : // DATE WRITTEN June 2000
5788 :
5789 : // PURPOSE OF THIS SUBROUTINE:
5790 : // This subroutine creates the array of Special Day types used during
5791 : // the simulation.
5792 :
5793 : // METHODOLOGY EMPLOYED:
5794 : // Sets up the SpecialDayTypes array that then is used during simulation.
5795 :
5796 7 : state.dataWeather->SpecialDayTypes = 0; // Initialize/Reset Special Day Types array
5797 :
5798 37 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
5799 30 : auto const &specialDay = state.dataWeather->SpecialDays(i);
5800 30 : if (specialDay.WthrFile) {
5801 0 : continue;
5802 : }
5803 :
5804 30 : int Warn = 0;
5805 :
5806 30 : int JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd) - 1;
5807 :
5808 60 : for (int j = 1; j <= specialDay.Duration; ++j) {
5809 30 : ++JDay;
5810 30 : if (JDay > 366) {
5811 0 : ShowWarningError(state, format("SpecialDay={} causes index of more than 366, ignoring those beyond 366", specialDay.Name));
5812 : } else {
5813 30 : if (state.dataWeather->SpecialDayTypes(JDay) != 0 && Warn == 0) {
5814 0 : ShowWarningError(state, format("SpecialDay={} attempted overwrite of previous set special day", specialDay.Name));
5815 0 : Warn = 1;
5816 30 : } else if (state.dataWeather->SpecialDayTypes(JDay) == 0) {
5817 30 : state.dataWeather->SpecialDayTypes(JDay) = specialDay.DayType;
5818 : }
5819 : }
5820 : }
5821 : }
5822 7 : }
5823 :
5824 776 : void GetDSTData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5825 : {
5826 :
5827 : // SUBROUTINE INFORMATION:
5828 : // AUTHOR Linda Lawrie
5829 : // DATE WRITTEN August 2000
5830 :
5831 : // PURPOSE OF THIS SUBROUTINE:
5832 : // This subroutine gets a possible "Daylight Saving Period" from the IDF. Using this
5833 : // will overwrite any prior DST data.
5834 :
5835 : // METHODOLOGY EMPLOYED:
5836 : // Processes the following IDD definition:
5837 : // DaylightSavingPeriod,
5838 : // \memo This object sets up the Daylight Saving period for any RunPeriod.
5839 : // \memo Ignores any DaylightSavingperiod values on the weather file and uses this definition.
5840 : // \memo (These are not used with DesignDay objects.)
5841 : // A1, \field StartDate
5842 : // A2, \field EndDate
5843 : // \memo Dates can be several formats:
5844 : // \memo <number>/<number> (month/day)
5845 : // \memo <number> <Month>
5846 : // \memo <Month> <number>
5847 : // \memo <Nth> <Weekday> in <Month)
5848 : // \memo Last <WeekDay> in <Month>
5849 : // \memo <Month> can be January, February, March, April, May, June, July, August, September,
5850 : // October, November, December
5851 : // \memo Months can be the first 3 letters of the month
5852 : // \memo <Weekday> can be Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
5853 : // \memo <Nth> can be 1 or 1st, 2 or 2nd, etc. up to 5(?)
5854 :
5855 776 : constexpr std::string_view routineName = "GetDSTData";
5856 :
5857 776 : auto const &ipsc = state.dataIPShortCut;
5858 776 : ipsc->cCurrentModuleObject = "RunPeriodControl:DaylightSavingTime";
5859 776 : int NumFound = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5860 :
5861 776 : if (NumFound == 1) {
5862 : int NumAlphas;
5863 : int IOStat;
5864 : int NumNumbers;
5865 366 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5866 183 : ipsc->cCurrentModuleObject,
5867 : 1,
5868 183 : ipsc->cAlphaArgs,
5869 : NumAlphas,
5870 183 : ipsc->rNumericArgs,
5871 : NumNumbers,
5872 : IOStat,
5873 183 : ipsc->lNumericFieldBlanks,
5874 183 : ipsc->lAlphaFieldBlanks,
5875 183 : ipsc->cAlphaFieldNames,
5876 183 : ipsc->cNumericFieldNames);
5877 :
5878 183 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
5879 :
5880 183 : if (NumAlphas != 2) {
5881 0 : ShowSevereError(state, format("{}: Insufficient fields, must have Start AND End Dates", ipsc->cCurrentModuleObject));
5882 0 : ErrorsFound = true;
5883 : } else { // Correct number of arguments
5884 366 : General::ProcessDateString(state,
5885 183 : ipsc->cAlphaArgs(1),
5886 183 : state.dataWeather->IDFDST.StMon,
5887 183 : state.dataWeather->IDFDST.StDay,
5888 183 : state.dataWeather->IDFDST.StWeekDay,
5889 183 : state.dataWeather->IDFDST.StDateType,
5890 : ErrorsFound);
5891 183 : if (state.dataWeather->IDFDST.StDateType == DateType::Invalid) {
5892 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
5893 0 : ErrorsFound = true;
5894 : }
5895 366 : General::ProcessDateString(state,
5896 183 : ipsc->cAlphaArgs(2),
5897 183 : state.dataWeather->IDFDST.EnMon,
5898 183 : state.dataWeather->IDFDST.EnDay,
5899 183 : state.dataWeather->IDFDST.EnWeekDay,
5900 183 : state.dataWeather->IDFDST.EnDateType,
5901 : ErrorsFound);
5902 183 : if (state.dataWeather->IDFDST.EnDateType == DateType::Invalid) {
5903 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
5904 0 : ErrorsFound = true;
5905 : }
5906 183 : state.dataWeather->IDFDaylightSaving = true;
5907 : }
5908 593 : } else if (NumFound > 1) {
5909 0 : ShowSevereError(state, format("{}: Too many objects in Input File, only one allowed.", ipsc->cCurrentModuleObject));
5910 0 : ErrorsFound = true;
5911 : }
5912 776 : }
5913 :
5914 801 : void GetDesignDayData(EnergyPlusData &state,
5915 : int TotDesDays, // Total number of Design days to Setup
5916 : bool &ErrorsFound)
5917 : {
5918 :
5919 : // SUBROUTINE INFORMATION:
5920 : // AUTHOR Richard Liesen
5921 : // DATE WRITTEN September 1997
5922 :
5923 : // PURPOSE OF THIS SUBROUTINE:
5924 : // This subroutine retrieves the design day info from user input file
5925 : // which is later to be used in the Setup Design Day Routine.
5926 :
5927 : // REFERENCES:
5928 : // SizingPeriod:DesignDay,
5929 : // A1, \field Name
5930 : // N1, \field Month
5931 : // N2, \field Day of Month
5932 : // A2, \field Day Type
5933 : // N3, \field Maximum Dry-Bulb Temperature
5934 : // N4, \field Daily Dry-Bulb Temperature Range
5935 : // A3, \field Dry-Bulb Temperature Range Modifier Type
5936 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
5937 : // A5, \field Humidity Condition Type
5938 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
5939 : // A6, \field Humidity Condition Day Schedule Name
5940 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
5941 : // N7, \field Enthalpy at Maximum Dry-Bulb !will require units transition.
5942 : // N8, \field Daily Wet-Bulb Temperature Range
5943 : // N9, \field Barometric Pressure
5944 : // N10, \field Wind Speed
5945 : // N11, \field Wind Direction
5946 : // A7, \field Rain Indicator
5947 : // A8, \field Snow Indicator
5948 : // A9, \field Daylight Saving Time Indicator
5949 : // A10, \field Solar Model Indicator
5950 : // A11, \field Beam Solar Day Schedule Name
5951 : // A12, \field Diffuse Solar Day Schedule Name
5952 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
5953 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
5954 : // N14; \field Sky Clearness
5955 :
5956 : static constexpr std::array<std::string_view, static_cast<int>(DesDayHumIndType::Num)> DesDayHumIndTypeStringRep = {
5957 : "Wetbulb [C]",
5958 : "Dewpoint [C]",
5959 : "Enthalpy [J/kg]",
5960 : "Humidity Ratio []",
5961 : "Schedule []",
5962 : "WetBulbProfileDefaultMultipliers []",
5963 : "WetBulbProfileDifferenceSchedule []",
5964 : "WetBulbProfileMultiplierSchedule []"};
5965 :
5966 : // Below are the 2009 fractions, HOF, Chap 14, Table 6
5967 : static constexpr std::array<Real64, 24> DefaultTempRangeMult = {0.88, 0.92, 0.95, 0.98, 1.0, 0.98, 0.91, 0.74, 0.55, 0.38, 0.23, 0.13,
5968 : 0.05, 0.00, 0.00, 0.06, 0.14, 0.24, 0.39, 0.50, 0.59, 0.68, 0.75, 0.82};
5969 :
5970 : static constexpr std::string_view routineName = "GetDesignDayData";
5971 :
5972 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5973 : Constant::Units unitType;
5974 :
5975 801 : state.dataWeather->DesDayInput.allocate(TotDesDays); // Allocate the array to the # of DD's
5976 801 : state.dataWeather->desDayMods.allocate(TotDesDays);
5977 2478 : for (int iDD = 1; iDD <= TotDesDays; ++iDD) {
5978 1677 : state.dataWeather->desDayMods(iDD).allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
5979 : }
5980 :
5981 801 : state.dataWeather->spSiteSchedules.dimension(TotDesDays, Weather::SPSiteSchedules());
5982 :
5983 801 : if (state.dataSysVars->ReverseDD && TotDesDays <= 1) {
5984 0 : ShowSevereError(state, "GetDesignDayData: Reverse Design Day requested but # Design Days <=1");
5985 : }
5986 :
5987 801 : auto const &ipsc = state.dataIPShortCut;
5988 801 : ipsc->cCurrentModuleObject = "SizingPeriod:DesignDay";
5989 2478 : for (int iDesDay = 1; iDesDay <= TotDesDays; ++iDesDay) {
5990 :
5991 : int EnvrnNum;
5992 1677 : if (!state.dataSysVars->ReverseDD) {
5993 1677 : EnvrnNum = iDesDay;
5994 0 : } else if (iDesDay == 1 && TotDesDays > 1) {
5995 0 : EnvrnNum = 2;
5996 0 : } else if (iDesDay == 2) {
5997 0 : EnvrnNum = 1;
5998 : } else {
5999 0 : EnvrnNum = iDesDay;
6000 : }
6001 :
6002 : // Call Input Get routine to retrieve design day data
6003 1677 : bool MaxDryBulbEntered = false;
6004 1677 : bool PressureEntered = false;
6005 : int NumAlpha; // Number of material alpha names being passed
6006 : int NumNumerics; // Number of material properties being passed
6007 : int IOStat; // IO Status when calling get input subroutine
6008 3354 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6009 1677 : ipsc->cCurrentModuleObject,
6010 : iDesDay,
6011 1677 : ipsc->cAlphaArgs,
6012 : NumAlpha,
6013 1677 : ipsc->rNumericArgs,
6014 : NumNumerics,
6015 : IOStat,
6016 1677 : ipsc->lNumericFieldBlanks,
6017 1677 : ipsc->lAlphaFieldBlanks,
6018 1677 : ipsc->cAlphaFieldNames,
6019 1677 : ipsc->cNumericFieldNames);
6020 :
6021 1677 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
6022 1677 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
6023 1677 : desDayInput.Title = ipsc->cAlphaArgs(1); // Environment name
6024 1677 : envCurr.Title = desDayInput.Title;
6025 :
6026 1677 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, desDayInput.Title};
6027 :
6028 : // N3, \field Maximum Dry-Bulb Temperature
6029 : // N4, \field Daily Dry-Bulb Temperature Range
6030 : // N9, \field Barometric Pressure
6031 : // N10, \field Wind Speed
6032 : // N11, \field Wind Direction
6033 1677 : desDayInput.MaxDryBulb = ipsc->rNumericArgs(3); // Maximum Dry-Bulb Temperature (C)
6034 1677 : MaxDryBulbEntered = !ipsc->lNumericFieldBlanks(3);
6035 1677 : desDayInput.DailyDBRange = ipsc->rNumericArgs(4); // Daily dry-bulb temperature range (deltaC)
6036 1677 : desDayInput.PressBarom = ipsc->rNumericArgs(9); // Atmospheric/Barometric Pressure (Pascals)
6037 1677 : PressureEntered = !ipsc->lNumericFieldBlanks(9);
6038 1677 : desDayInput.PressureEntered = PressureEntered;
6039 1677 : desDayInput.WindSpeed = ipsc->rNumericArgs(10); // Wind Speed (m/s)
6040 1677 : desDayInput.WindDir = mod(ipsc->rNumericArgs(11), 360.0); // Wind Direction
6041 : // (degrees clockwise from North, N=0, E=90, S=180, W=270)
6042 : // N1, \field Month
6043 : // N2, \field Day of Month
6044 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
6045 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
6046 : // N8, \field Daily Wet-Bulb Temperature Range
6047 1677 : desDayInput.Month = int(ipsc->rNumericArgs(1)); // Month of Year ( 1 - 12 )
6048 1677 : desDayInput.DayOfMonth = int(ipsc->rNumericArgs(2)); // Day of Month ( 1 - 31 )
6049 1677 : desDayInput.TauB = ipsc->rNumericArgs(12); // beam tau >= 0
6050 1677 : desDayInput.TauD = ipsc->rNumericArgs(13); // diffuse tau >= 0
6051 1677 : desDayInput.DailyWBRange = ipsc->rNumericArgs(8); // Daily wet-bulb temperature range (deltaC)
6052 :
6053 : // N14; \field Sky Clearness
6054 1677 : desDayInput.SkyClear = ipsc->rNumericArgs(14); // Sky Clearness (0 to 1)
6055 :
6056 : // N15, \field Maximum Warmup Days Between Sizing Periods
6057 1677 : if (ipsc->lNumericFieldBlanks(15)) {
6058 : // Default to -1 if not input
6059 1677 : desDayInput.maxWarmupDays = -1;
6060 : } else {
6061 0 : desDayInput.maxWarmupDays = int(ipsc->rNumericArgs(15));
6062 : }
6063 : // A13, \field Begin Environment Reset Mode
6064 1677 : if (ipsc->lAlphaFieldBlanks(13)) {
6065 1677 : desDayInput.suppressBegEnvReset = false;
6066 : } else {
6067 0 : if (Util::SameString(ipsc->cAlphaArgs(13), "FullResetAtBeginEnvironment")) {
6068 0 : desDayInput.suppressBegEnvReset = false;
6069 0 : } else if (Util::SameString(ipsc->cAlphaArgs(13), "SuppressThermalResetAtBeginEnvironment")) {
6070 0 : desDayInput.suppressBegEnvReset = true;
6071 : }
6072 : }
6073 : // for PerformancePrecisionTradeoffs
6074 1677 : if (state.dataEnvrn->forceBeginEnvResetSuppress) {
6075 2 : desDayInput.suppressBegEnvReset = true;
6076 : }
6077 : // A7, \field Rain Indicator
6078 : BooleanSwitch b;
6079 :
6080 1677 : if (ipsc->lAlphaFieldBlanks(7)) {
6081 6 : desDayInput.RainInd = 0;
6082 1671 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
6083 1671 : desDayInput.RainInd = (int)b;
6084 : } else {
6085 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7), "No");
6086 0 : desDayInput.RainInd = 0;
6087 : }
6088 :
6089 : // A8, \field Snow Indicator
6090 1677 : if (ipsc->lAlphaFieldBlanks(8)) {
6091 6 : desDayInput.SnowInd = 0;
6092 1671 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
6093 1671 : desDayInput.SnowInd = (int)b;
6094 : } else {
6095 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8), "No");
6096 0 : desDayInput.SnowInd = 0;
6097 : }
6098 :
6099 : // A3, \field Dry-Bulb Temperature Range Modifier Type
6100 : // check DB profile input
6101 1677 : if (ipsc->lAlphaFieldBlanks(3)) {
6102 1541 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
6103 272 : } else if ((desDayInput.dryBulbRangeType = static_cast<DesDayDryBulbRangeType>(
6104 136 : getEnumValue(DesDayDryBulbRangeTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))))) != DesDayDryBulbRangeType::Invalid) {
6105 : } else {
6106 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6107 0 : ErrorsFound = true;
6108 0 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
6109 : }
6110 :
6111 : // std::string units; // not used
6112 1677 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
6113 : // units = "[]";
6114 3 : unitType = Constant::Units::None;
6115 1674 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
6116 : // units = "[deltaC]";
6117 2 : unitType = Constant::Units::deltaC;
6118 1672 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
6119 : // units = "[C]";
6120 1 : unitType = Constant::Units::C;
6121 : }
6122 :
6123 1677 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile && !MaxDryBulbEntered && ipsc->cAlphaArgs(3) != "invalid field") {
6124 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(3), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6125 0 : ErrorsFound = true;
6126 : }
6127 :
6128 : // Assume either "multiplier" option will make full use of range...
6129 1677 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Difference &&
6130 1675 : desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
6131 1674 : Real64 testval = desDayInput.MaxDryBulb - desDayInput.DailyDBRange;
6132 1674 : if (testval < -90.0 || testval > 70.0) {
6133 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6134 0 : ShowContinueError(state, format("{} ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(3), testval));
6135 0 : ErrorsFound = true;
6136 : }
6137 : }
6138 :
6139 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
6140 1677 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Default) {
6141 : // Default dry-bulb temperature Range
6142 1671 : Real64 LastHrValue = DefaultTempRangeMult[23];
6143 41775 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
6144 269112 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
6145 229008 : Real64 WNow = state.dataWeather->Interpolation(ts);
6146 229008 : Real64 WPrev = 1.0 - WNow;
6147 229008 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutDryBulbTemp =
6148 229008 : LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
6149 : }
6150 40104 : LastHrValue = DefaultTempRangeMult[hour - 1];
6151 : }
6152 :
6153 6 : } else if (ipsc->lAlphaFieldBlanks(4)) {
6154 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaFieldNames(3), "SCHEDULE");
6155 0 : ErrorsFound = true;
6156 6 : } else if ((desDayInput.tempRangeSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(4))) == nullptr) {
6157 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
6158 0 : ErrorsFound = true;
6159 :
6160 : } else {
6161 6 : std::vector<Real64> const &dayVals = desDayInput.tempRangeSched->getDayVals(state);
6162 6 : auto &desDayModEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6163 150 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6164 720 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6165 576 : desDayModEnvrn(ts + 1, hr + 1).OutDryBulbTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6166 : }
6167 : }
6168 :
6169 12 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6170 12 : state.dataWeather->spSiteSchedNums.end(),
6171 18 : desDayInput.tempRangeSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6172 4 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.tempRangeSched->Num);
6173 8 : SetupOutputVariable(state,
6174 : "Sizing Period Site Drybulb Temperature Range Modifier Schedule Value",
6175 : unitType,
6176 4 : state.dataWeather->spSiteSchedules(EnvrnNum).OutDryBulbTemp,
6177 : OutputProcessor::TimeStepType::Zone,
6178 : OutputProcessor::StoreType::Average,
6179 4 : ipsc->cAlphaArgs(4));
6180 : }
6181 :
6182 6 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
6183 3 : if (!desDayInput.tempRangeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
6184 0 : Sched::ShowSevereBadMinMax(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0, Clusive::In, 1.0);
6185 0 : ErrorsFound = true;
6186 : }
6187 3 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) { // delta, must be > 0.0
6188 2 : if (!desDayInput.tempRangeSched->checkMinVal(state, Clusive::In, 0.0)) {
6189 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0);
6190 0 : ErrorsFound = true;
6191 : }
6192 : }
6193 :
6194 6 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6195 6 : Real64 testval = std::numeric_limits<Real64>::min();
6196 150 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
6197 720 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
6198 576 : if (desDayModsEnvrn(iTS, iHr).OutDryBulbTemp > testval) {
6199 45 : testval = desDayModsEnvrn(iTS, iHr).OutDryBulbTemp;
6200 : }
6201 : }
6202 : }
6203 :
6204 6 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
6205 1 : if (MaxDryBulbEntered) {
6206 0 : ShowWarningError(state, format("{}=\"{}\", data override.", ipsc->cCurrentModuleObject, desDayInput.Title));
6207 0 : ShowContinueError(state, format("..{}=[{:.2R}] will be overwritten.", ipsc->cNumericFieldNames(3), desDayInput.MaxDryBulb));
6208 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3)));
6209 0 : ShowContinueError(state, format("..with max value=[{:.2R}].", testval));
6210 : }
6211 1 : desDayInput.MaxDryBulb = testval;
6212 : }
6213 :
6214 6 : testval = desDayInput.MaxDryBulb - testval;
6215 6 : if (testval < -90.0 || testval > 70.0) {
6216 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6217 : // should this be cNumericFieldNames?
6218 0 : ShowContinueError(state, format("{} = ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(4), testval));
6219 0 : ErrorsFound = true;
6220 : }
6221 : }
6222 :
6223 : // A5, \field Humidity Condition Type
6224 1677 : desDayInput.HumIndType = static_cast<DesDayHumIndType>(getEnumValue(DesDayHumIndTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
6225 :
6226 1677 : switch (desDayInput.HumIndType) {
6227 1651 : case DesDayHumIndType::WetBulb: {
6228 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
6229 1651 : if (ipsc->lNumericFieldBlanks(5)) {
6230 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6231 0 : ErrorsFound = true;
6232 : } else {
6233 1651 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6234 : }
6235 :
6236 1651 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6237 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6238 0 : ShowContinueError(
6239 : state,
6240 0 : format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - WetBulb", desDayInput.HumIndValue));
6241 0 : ErrorsFound = true;
6242 : }
6243 1651 : } break;
6244 :
6245 5 : case DesDayHumIndType::DewPoint: {
6246 5 : if (ipsc->lNumericFieldBlanks(5)) {
6247 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6248 0 : ErrorsFound = true;
6249 : } else {
6250 5 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6251 : }
6252 :
6253 5 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6254 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6255 0 : ShowContinueError(
6256 : state,
6257 0 : format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - DewPoint", desDayInput.HumIndValue));
6258 0 : ErrorsFound = true;
6259 : }
6260 5 : } break;
6261 :
6262 0 : case DesDayHumIndType::HumRatio: {
6263 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
6264 0 : if (ipsc->lNumericFieldBlanks(6)) {
6265 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(6), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6266 0 : ErrorsFound = true;
6267 : } else {
6268 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(6); // Humidity Indicating Conditions at Max Dry-Bulb
6269 : }
6270 :
6271 0 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 0.03) {
6272 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6273 0 : ShowContinueError(
6274 : state,
6275 0 : format("{} = {:.2R} is out of range [0.0, 0.03]", ipsc->cAlphaFieldNames(5) + " - Humidity-Ratio", desDayInput.HumIndValue));
6276 0 : ErrorsFound = true;
6277 : }
6278 0 : } break;
6279 :
6280 3 : case DesDayHumIndType::Enthalpy: {
6281 : // N7, \field Enthalpy at Maximum Dry-Bulb {J/kg}.
6282 3 : if (ipsc->lNumericFieldBlanks(7)) {
6283 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(7), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6284 0 : ErrorsFound = true;
6285 : } else {
6286 3 : desDayInput.HumIndValue = ipsc->rNumericArgs(7); // Humidity Indicating Conditions at Max Dry-Bulb
6287 : }
6288 :
6289 3 : desDayInput.HumIndType = DesDayHumIndType::Enthalpy;
6290 3 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 130000.0) {
6291 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6292 0 : ShowContinueError(
6293 : state,
6294 0 : format("{} = {.0R} is out of range [0.0, 130000.0]", ipsc->cAlphaFieldNames(5) + " - Enthalpy", desDayInput.HumIndValue));
6295 0 : ErrorsFound = true;
6296 : }
6297 3 : } break;
6298 :
6299 3 : case DesDayHumIndType::RelHumSch: {
6300 : // units = "[%]";
6301 3 : unitType = Constant::Units::Perc;
6302 3 : } break;
6303 :
6304 2 : case DesDayHumIndType::WBProfMul: {
6305 : // units = "[]";
6306 2 : unitType = Constant::Units::None;
6307 2 : if (ipsc->lNumericFieldBlanks(5)) {
6308 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6309 0 : ErrorsFound = true;
6310 : } else {
6311 2 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6312 : }
6313 2 : } break;
6314 :
6315 1 : case DesDayHumIndType::WBProfDif: {
6316 : // units = "[]";
6317 1 : unitType = Constant::Units::None;
6318 1 : if (ipsc->lNumericFieldBlanks(5)) {
6319 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6320 0 : ErrorsFound = true;
6321 : } else {
6322 1 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6323 : }
6324 1 : } break;
6325 :
6326 12 : case DesDayHumIndType::WBProfDef: {
6327 12 : if (ipsc->lNumericFieldBlanks(5)) {
6328 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6329 0 : ErrorsFound = true;
6330 : } else {
6331 12 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6332 : }
6333 12 : } break;
6334 :
6335 0 : default: {
6336 0 : ShowWarningError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6337 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6338 0 : ShowContinueError(state, "WetBulb will be used. Maximum Dry Bulb will be used as WetBulb at Maximum Dry Bulb.");
6339 0 : desDayInput.HumIndType = DesDayHumIndType::WetBulb;
6340 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(3);
6341 0 : } break;
6342 : } // switch (desDayInput.HumIndType)
6343 :
6344 : // resolve humidity schedule if needed
6345 : // A6, \field Humidity Condition Day Schedule Name
6346 1677 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch || desDayInput.HumIndType == DesDayHumIndType::WBProfMul ||
6347 1672 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6348 6 : if (ipsc->lAlphaFieldBlanks(6)) {
6349 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6350 0 : ErrorsFound = true;
6351 6 : } else if ((desDayInput.humIndSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(6))) == nullptr) {
6352 0 : ShowWarningItemNotFound(state,
6353 : eoh,
6354 0 : ipsc->cAlphaFieldNames(6),
6355 0 : ipsc->cAlphaArgs(6),
6356 : "Default Humidity (constant for day using Humidity Indicator Temp).");
6357 : // reset HumIndType ?
6358 : } else {
6359 6 : std::vector<Real64> const &dayVals = desDayInput.humIndSched->getDayVals(state);
6360 6 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6361 150 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6362 720 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6363 576 : desDayModsEnvrn(ts + 1, hr + 1).OutRelHum = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6364 : }
6365 : }
6366 :
6367 12 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6368 12 : state.dataWeather->spSiteSchedNums.end(),
6369 18 : desDayInput.humIndSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6370 5 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.humIndSched->Num);
6371 10 : SetupOutputVariable(state,
6372 : "Sizing Period Site Humidity Condition Schedule Value",
6373 : unitType,
6374 5 : state.dataWeather->spSiteSchedules(EnvrnNum).OutRelHum,
6375 : OutputProcessor::TimeStepType::Zone,
6376 : OutputProcessor::StoreType::Average,
6377 5 : ipsc->cAlphaArgs(6));
6378 : }
6379 :
6380 6 : switch (desDayInput.HumIndType) {
6381 3 : case DesDayHumIndType::RelHumSch: {
6382 3 : if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 100.0)) {
6383 0 : Sched::ShowSevereBadMinMax(
6384 0 : state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 100.0);
6385 0 : ErrorsFound = true;
6386 : }
6387 3 : } break;
6388 2 : case DesDayHumIndType::WBProfMul: {
6389 : // multiplier: use schedule value, check 0 <= v <= 1
6390 2 : if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
6391 0 : Sched::ShowSevereBadMinMax(
6392 0 : state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 1.0);
6393 0 : ErrorsFound = true;
6394 : }
6395 2 : } break;
6396 1 : case DesDayHumIndType::WBProfDif: {
6397 1 : if (!desDayInput.humIndSched->checkMinVal(state, Clusive::In, 0.0)) {
6398 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0);
6399 0 : ErrorsFound = true;
6400 : }
6401 1 : } break;
6402 0 : default: {
6403 0 : } break;
6404 : } // switch (desDayInput.HumIndType)
6405 : } // if (desDayInput.HumIndSchPtr == 0)
6406 :
6407 1677 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
6408 : // re WetBulbProfileDefaultMultipliers
6409 12 : Real64 LastHrValue = DefaultTempRangeMult[23];
6410 300 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
6411 1440 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
6412 1152 : Real64 WNow = state.dataWeather->Interpolation(ts);
6413 1152 : Real64 WPrev = 1.0 - WNow;
6414 1152 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutRelHum = LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
6415 : }
6416 288 : LastHrValue = DefaultTempRangeMult[hour - 1];
6417 : }
6418 : }
6419 :
6420 : // verify that design WB or DP <= design DB
6421 1677 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint || desDayInput.HumIndType == DesDayHumIndType::WetBulb ||
6422 21 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::WBProfDef ||
6423 7 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6424 1671 : if (desDayInput.HumIndValue > desDayInput.MaxDryBulb) {
6425 0 : ShowWarningError(state, format("{}=\"{}\", range check data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6426 0 : ShowContinueError(state,
6427 0 : format("..Humidity Indicator Temperature at Max Temperature={:.1R} > Max DryBulb={:.1R}",
6428 0 : desDayInput.HumIndValue,
6429 0 : desDayInput.MaxDryBulb));
6430 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6431 0 : ShowContinueError(state, "..Conditions for day will be set to Relative Humidity = 100%");
6432 0 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint) {
6433 0 : desDayInput.DewPointNeedsSet = true;
6434 : } else {
6435 : // wet-bulb
6436 0 : desDayInput.HumIndValue = desDayInput.MaxDryBulb;
6437 : }
6438 : }
6439 : }
6440 :
6441 : // A10, \field Solar Model Indicator
6442 1677 : if (ipsc->lAlphaFieldBlanks(10)) {
6443 6 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6444 3342 : } else if ((desDayInput.solarModel = static_cast<DesDaySolarModel>(
6445 1671 : getEnumValue(DesDaySolarModelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(10))))) != DesDaySolarModel::Invalid) {
6446 : } else {
6447 0 : ShowWarningInvalidKey(state, eoh, ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "ASHRAE ClearSky");
6448 0 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6449 : }
6450 :
6451 1677 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
6452 : // A11, \field Beam Solar Day Schedule Name
6453 3 : if (ipsc->lAlphaFieldBlanks(11)) {
6454 : // should have entered beam schedule
6455 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(11));
6456 0 : ErrorsFound = true;
6457 3 : } else if ((desDayInput.beamSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(11))) == nullptr) {
6458 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11));
6459 0 : ErrorsFound = true;
6460 : } else {
6461 3 : std::vector<Real64> const &dayVals = desDayInput.beamSolarSched->getDayVals(state);
6462 3 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6463 75 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6464 360 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6465 288 : desDayModsEnvrn(ts + 1, hr + 1).BeamSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6466 : }
6467 : }
6468 :
6469 3 : unitType = Constant::Units::W_m2;
6470 : // units = "[W/m2]";
6471 6 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6472 6 : state.dataWeather->spSiteSchedNums.end(),
6473 9 : desDayInput.beamSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6474 3 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.beamSolarSched->Num);
6475 6 : SetupOutputVariable(state,
6476 : "Sizing Period Site Beam Solar Schedule Value",
6477 : unitType,
6478 3 : state.dataWeather->spSiteSchedules(EnvrnNum).BeamSolarRad,
6479 : OutputProcessor::TimeStepType::Zone,
6480 : OutputProcessor::StoreType::Average,
6481 3 : ipsc->cAlphaArgs(11));
6482 : }
6483 :
6484 3 : if (!desDayInput.beamSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
6485 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11), Clusive::In, 0.0);
6486 0 : ErrorsFound = true;
6487 : }
6488 : }
6489 :
6490 : // A12, \field Diffuse Solar Day Schedule Name
6491 3 : if (ipsc->lAlphaFieldBlanks(12)) {
6492 : // should have entered diffuse schedule
6493 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(12));
6494 0 : ErrorsFound = true;
6495 3 : } else if ((desDayInput.diffuseSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(12))) == nullptr) {
6496 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12));
6497 0 : ErrorsFound = true;
6498 : } else {
6499 3 : std::vector<Real64> const &dayVals = desDayInput.diffuseSolarSched->getDayVals(state);
6500 3 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6501 75 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6502 360 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6503 288 : desDayModsEnvrn(ts + 1, hr + 1).DifSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6504 : }
6505 : }
6506 :
6507 : // units = "[W/m2]";
6508 3 : unitType = Constant::Units::W_m2;
6509 6 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6510 6 : state.dataWeather->spSiteSchedNums.end(),
6511 9 : desDayInput.diffuseSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6512 3 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.diffuseSolarSched->Num);
6513 6 : SetupOutputVariable(state,
6514 : "Sizing Period Site Diffuse Solar Schedule Value",
6515 : unitType,
6516 3 : state.dataWeather->spSiteSchedules(EnvrnNum).DifSolarRad,
6517 : OutputProcessor::TimeStepType::Zone,
6518 : OutputProcessor::StoreType::Average,
6519 3 : ipsc->cAlphaArgs(12));
6520 : }
6521 3 : if (!desDayInput.diffuseSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
6522 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12), Clusive::In, 0.0);
6523 0 : ErrorsFound = true;
6524 : }
6525 : }
6526 :
6527 1674 : } else if (desDayInput.solarModel == DesDaySolarModel::ASHRAE_ClearSky) {
6528 1584 : if (ipsc->lNumericFieldBlanks(14)) {
6529 0 : ShowWarningEmptyField(
6530 0 : state, eoh, ipsc->cNumericFieldNames(14), ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "Zero clear sky (no solar)");
6531 : }
6532 : }
6533 :
6534 : // Validate Design Day Month
6535 :
6536 1677 : switch (desDayInput.Month) {
6537 1654 : case 1:
6538 : case 3:
6539 : case 5:
6540 : case 7:
6541 : case 8:
6542 : case 10:
6543 : case 12: {
6544 1654 : if (desDayInput.DayOfMonth > 31) {
6545 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6546 0 : ShowContinueError(
6547 : state,
6548 0 : format(".. invalid field: {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6549 0 : ErrorsFound = true;
6550 : }
6551 1654 : } break;
6552 21 : case 4:
6553 : case 6:
6554 : case 9:
6555 : case 11: {
6556 21 : if (desDayInput.DayOfMonth > 30) {
6557 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6558 0 : ShowContinueError(
6559 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6560 0 : ErrorsFound = true;
6561 : }
6562 21 : } break;
6563 2 : case 2: {
6564 2 : if (desDayInput.DayOfMonth > 28) {
6565 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6566 0 : ShowContinueError(
6567 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6568 0 : ErrorsFound = true;
6569 : }
6570 2 : } break;
6571 0 : default: {
6572 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6573 0 : ShowContinueError(state, format(".. invalid {} invalid (Month) [{}].", ipsc->cNumericFieldNames(1), desDayInput.Month));
6574 0 : ErrorsFound = true;
6575 0 : } break;
6576 : } // switch (desDayInput.Month)
6577 :
6578 : // A9, \field Daylight Saving Time Indicator
6579 1677 : if (ipsc->lAlphaFieldBlanks(9)) {
6580 6 : desDayInput.DSTIndicator = 0;
6581 1671 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(9)))) != BooleanSwitch::Invalid) {
6582 1671 : desDayInput.DSTIndicator = (int)b;
6583 : } else {
6584 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(9), ipsc->cAlphaArgs(9), "No");
6585 0 : desDayInput.DSTIndicator = 0;
6586 : }
6587 :
6588 : // A2, \field Day Type
6589 1677 : desDayInput.DayType = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
6590 1677 : if (desDayInput.DayType <= 0) {
6591 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
6592 0 : ErrorsFound = true;
6593 : }
6594 :
6595 1677 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
6596 1677 : envCurr.Title = desDayInput.Title;
6597 1677 : envCurr.KindOfEnvrn = Constant::KindOfSim::DesignDay;
6598 1677 : envCurr.DesignDayNum = EnvrnNum;
6599 1677 : envCurr.RunPeriodDesignNum = 0;
6600 1677 : envCurr.TotalDays = 1;
6601 1677 : envCurr.StartMonth = desDayInput.Month;
6602 1677 : envCurr.StartDay = desDayInput.DayOfMonth;
6603 1677 : envCurr.EndMonth = envCurr.StartMonth;
6604 1677 : envCurr.EndDay = envCurr.StartDay;
6605 1677 : envCurr.DayOfWeek = 0;
6606 1677 : envCurr.UseDST = false;
6607 1677 : envCurr.UseHolidays = false;
6608 1677 : envCurr.StartJDay = designDay.DayOfYear;
6609 1677 : envCurr.EndJDay = envCurr.StartJDay;
6610 :
6611 : // create predefined report on design day
6612 1677 : std::string envTitle = desDayInput.Title;
6613 1677 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDmaxDB, envTitle, desDayInput.MaxDryBulb);
6614 1677 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDrange, envTitle, desDayInput.DailyDBRange);
6615 1677 : if (desDayInput.HumIndType != DesDayHumIndType::RelHumSch) {
6616 1674 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, desDayInput.HumIndValue);
6617 : } else {
6618 3 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, "N/A");
6619 : }
6620 3354 : OutputReportPredefined::PreDefTableEntry(
6621 3354 : state, state.dataOutRptPredefined->pdchDDhumTyp, envTitle, DesDayHumIndTypeStringRep[(int)desDayInput.HumIndType]);
6622 1677 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindSp, envTitle, desDayInput.WindSpeed);
6623 1677 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindDr, envTitle, desDayInput.WindDir);
6624 : }
6625 801 : }
6626 :
6627 801 : void GetLocationInfo(EnergyPlusData &state, bool &ErrorsFound)
6628 : {
6629 :
6630 : // SUBROUTINE INFORMATION:
6631 : // AUTHOR Richard Liesen
6632 : // DATE WRITTEN October 1997
6633 :
6634 : // PURPOSE OF THIS SUBROUTINE:
6635 : // This subroutine gets the location info from the IDF file; latitude,
6636 : // longitude and time zone number.
6637 :
6638 801 : auto const &ipsc = state.dataIPShortCut;
6639 801 : ipsc->cCurrentModuleObject = "Site:Location";
6640 801 : int const NumLocations = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6641 :
6642 801 : if (NumLocations > 1) {
6643 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6644 0 : ErrorsFound = true;
6645 : }
6646 :
6647 801 : if (NumLocations == 1) {
6648 : int LocNumAlpha; // Number of alpha names being passed
6649 : int LocNumProp; // Number of properties being passed
6650 : int IOStat; // IO Status when calling get input subroutine
6651 794 : Array1D_string LocAlphas(2); // Temporary array to transfer location info (non-numerics)
6652 794 : Array1D<Real64> LocProps(4); // Temporary array to transfer location info (numerics)
6653 : // Call Input Get routine to retrieve Location information
6654 1588 : state.dataInputProcessing->inputProcessor->getObjectItem(
6655 794 : state, ipsc->cCurrentModuleObject, 1, LocAlphas, LocNumAlpha, LocProps, LocNumProp, IOStat);
6656 :
6657 : // set latitude, longitude, and time zone number variables
6658 794 : state.dataWeather->LocationTitle = LocAlphas(1);
6659 794 : state.dataEnvrn->Latitude = LocProps(1);
6660 794 : state.dataEnvrn->Longitude = LocProps(2);
6661 794 : state.dataEnvrn->TimeZoneNumber = LocProps(3);
6662 794 : state.dataEnvrn->Elevation = LocProps(4);
6663 794 : if (Util::SameString(LocAlphas(2), "Yes")) {
6664 1 : state.dataWeather->keepUserSiteLocationDefinition = true;
6665 : }
6666 794 : state.dataWeather->LocationGathered = true;
6667 794 : }
6668 801 : }
6669 :
6670 801 : void GetWeatherProperties(EnergyPlusData &state, bool &ErrorsFound)
6671 : {
6672 :
6673 : // SUBROUTINE INFORMATION:
6674 : // AUTHOR Linda Lawrie
6675 : // DATE WRITTEN July 2009
6676 :
6677 : // PURPOSE OF THIS SUBROUTINE:
6678 : // Weather properties are an advanced concept for simulation. Primarily, these properties are
6679 : // used in the test suite runs that have specific requirements for certain properties (such as
6680 : // sky temperature).
6681 :
6682 : // REFERENCES:
6683 : // WeatherProperty:SkyTemperature,
6684 : // \memo This object is used to override internal sky temperature calculations.
6685 : // A1, \field Name
6686 : // \reference DesignDays
6687 : // \note leave blank for RunPeriods (until we name them)
6688 : // \note This field references the applicable design day or runperiod(s) if left blank.
6689 : // A2, \field Calculation Type
6690 : // \type choice
6691 : // \key ScheduleValue
6692 : // \key DifferenceScheduleDryBulbValue
6693 : // \key DifferenceScheduleDewPointValue
6694 : // \key AlgorithmA
6695 : // A3; \field Schedule Name
6696 : // \type object-list
6697 : // \object-list DayScheduleNames
6698 : // \object-list ScheduleNames
6699 :
6700 : static constexpr std::string_view routineName = "GetWeatherProperties";
6701 :
6702 : int Found;
6703 : int envFound;
6704 :
6705 801 : auto const &ipsc = state.dataIPShortCut;
6706 801 : ipsc->cCurrentModuleObject = "WeatherProperty:SkyTemperature";
6707 801 : state.dataWeather->NumWPSkyTemperatures = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6708 :
6709 801 : state.dataWeather->WPSkyTemperature.allocate(state.dataWeather->NumWPSkyTemperatures); // by default, not used.
6710 :
6711 803 : for (int i = 1; i <= state.dataWeather->NumWPSkyTemperatures; ++i) {
6712 : int IOStat;
6713 : int NumAlpha;
6714 : int NumNumerics;
6715 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6716 2 : ipsc->cCurrentModuleObject,
6717 : i,
6718 2 : ipsc->cAlphaArgs,
6719 : NumAlpha,
6720 2 : ipsc->rNumericArgs,
6721 : NumNumerics,
6722 : IOStat,
6723 2 : ipsc->lNumericFieldBlanks,
6724 2 : ipsc->lAlphaFieldBlanks,
6725 2 : ipsc->cAlphaFieldNames,
6726 2 : ipsc->cNumericFieldNames);
6727 :
6728 2 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
6729 2 : auto &wpSkyTemp = state.dataWeather->WPSkyTemperature(i);
6730 2 : if (ipsc->cAlphaArgs(1).empty()) {
6731 1 : Found = 0;
6732 14 : for (int j = 1; j <= state.dataWeather->NumOfEnvrn; ++j) {
6733 13 : auto &environJ = state.dataWeather->Environment(j);
6734 13 : if (environJ.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
6735 12 : continue;
6736 : }
6737 1 : if (environJ.WP_Type1 != 0) {
6738 0 : ShowSevereError(state,
6739 0 : format("{}: {}=\"{}\", indicated Environment Name already assigned.",
6740 : routineName,
6741 0 : ipsc->cCurrentModuleObject,
6742 0 : ipsc->cAlphaArgs(1)));
6743 0 : if (!environJ.Title.empty()) {
6744 0 : ShowContinueError(state,
6745 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6746 0 : environJ.Title,
6747 0 : ipsc->cCurrentModuleObject,
6748 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6749 : } else {
6750 0 : ShowContinueError(state,
6751 0 : format("... Runperiod Environment, already using {}=\"{}\".",
6752 0 : ipsc->cCurrentModuleObject,
6753 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6754 : }
6755 0 : ErrorsFound = true;
6756 : } else {
6757 1 : environJ.WP_Type1 = i;
6758 1 : Found = j;
6759 : }
6760 : }
6761 1 : if (Found == 0) {
6762 0 : ShowWarningError(state, "GetWeatherProperties: WeatherProperty:SkyTemperature=blank, no run periods found.");
6763 0 : ShowContinueError(state, "...SkyTemperature will not be applied.");
6764 0 : continue;
6765 : }
6766 : } else { // really a name
6767 1 : Found = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataWeather->Environment, &EnvironmentData::Title);
6768 1 : envFound = Found;
6769 1 : if (Found == 0) {
6770 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
6771 0 : ErrorsFound = true;
6772 0 : continue;
6773 : }
6774 :
6775 1 : auto &envrnFound = state.dataWeather->Environment(Found);
6776 1 : if (envrnFound.WP_Type1 != 0) {
6777 0 : ShowSevereError(state,
6778 0 : format("{}:{}=\"{}\", indicated Environment Name already assigned.",
6779 : routineName,
6780 0 : ipsc->cCurrentModuleObject,
6781 0 : ipsc->cAlphaArgs(1)));
6782 0 : ShowContinueError(state,
6783 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6784 0 : envrnFound.Title,
6785 0 : ipsc->cCurrentModuleObject,
6786 0 : state.dataWeather->WPSkyTemperature(envrnFound.WP_Type1).Name));
6787 0 : ErrorsFound = true;
6788 : } else {
6789 1 : state.dataWeather->Environment(Found).WP_Type1 = i;
6790 : }
6791 : }
6792 :
6793 3 : wpSkyTemp.Name = !ipsc->lAlphaFieldBlanks(1) ? ipsc->cAlphaArgs(1) : "All RunPeriods";
6794 :
6795 : // Validate Calculation Type.
6796 : // std::string units;
6797 : Constant::Units unitType;
6798 2 : wpSkyTemp.skyTempModel = static_cast<SkyTempModel>(getEnumValue(Weather::SkyTempModelNamesUC, ipsc->cAlphaArgs(2)));
6799 :
6800 2 : switch (wpSkyTemp.skyTempModel) {
6801 2 : case SkyTempModel::ScheduleValue: {
6802 2 : wpSkyTemp.IsSchedule = true;
6803 : // units = "[C]";
6804 2 : unitType = Constant::Units::C;
6805 2 : } break;
6806 0 : case SkyTempModel::DryBulbDelta:
6807 : case SkyTempModel::DewPointDelta: {
6808 0 : wpSkyTemp.IsSchedule = true;
6809 : // units = "[deltaC]";
6810 0 : unitType = Constant::Units::deltaC;
6811 0 : } break;
6812 0 : case SkyTempModel::Brunt:
6813 : case SkyTempModel::Idso:
6814 : case SkyTempModel::BerdahlMartin:
6815 : case SkyTempModel::ClarkAllen: {
6816 0 : wpSkyTemp.IsSchedule = false;
6817 0 : } break;
6818 0 : default: {
6819 : // Bad inputs are trapped by input processor
6820 0 : assert(false);
6821 : } break;
6822 : } // switch (skyTempModel)
6823 :
6824 2 : if (wpSkyTemp.IsSchedule) {
6825 3 : if (state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather ||
6826 1 : state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
6827 : // See if it's a schedule.
6828 1 : if ((wpSkyTemp.sched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
6829 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6830 0 : ErrorsFound = true;
6831 : } else {
6832 1 : wpSkyTemp.IsSchedule = true;
6833 : }
6834 : } else { // See if it's a valid schedule.
6835 : // How can a schedule be either a yearly schedule or a day schedule?
6836 1 : if ((wpSkyTemp.sched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
6837 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6838 0 : ErrorsFound = true;
6839 : } else {
6840 1 : if (envFound != 0) {
6841 2 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6842 2 : state.dataWeather->spSiteSchedNums.end(),
6843 3 : wpSkyTemp.sched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6844 1 : state.dataWeather->spSiteSchedNums.emplace_back(wpSkyTemp.sched->Num);
6845 2 : SetupOutputVariable(state,
6846 : "Sizing Period Site Sky Temperature Schedule Value",
6847 : unitType,
6848 1 : state.dataWeather->spSiteSchedules(envFound).SkyTemp,
6849 : OutputProcessor::TimeStepType::Zone,
6850 : OutputProcessor::StoreType::Average,
6851 1 : ipsc->cAlphaArgs(3));
6852 : }
6853 1 : wpSkyTemp.IsSchedule = true;
6854 : }
6855 : }
6856 : }
6857 : }
6858 :
6859 2 : if (!wpSkyTemp.IsSchedule && !ipsc->lAlphaFieldBlanks(4)) {
6860 0 : if (BooleanSwitch b = getYesNoValue(ipsc->cAlphaArgs(4)); b != BooleanSwitch::Invalid) {
6861 0 : wpSkyTemp.UseWeatherFileHorizontalIR = static_cast<bool>(b);
6862 : } else {
6863 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
6864 0 : ErrorsFound = true;
6865 : }
6866 : } else {
6867 2 : wpSkyTemp.UseWeatherFileHorizontalIR = true;
6868 : }
6869 : }
6870 3674 : for (auto &envCurr : state.dataWeather->Environment) {
6871 2873 : if (envCurr.WP_Type1 != 0 && state.dataWeather->NumWPSkyTemperatures >= envCurr.WP_Type1) {
6872 2 : envCurr.skyTempModel = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel;
6873 2 : envCurr.UseWeatherFileHorizontalIR = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).UseWeatherFileHorizontalIR;
6874 : }
6875 : }
6876 801 : }
6877 :
6878 801 : void GetGroundTemps(EnergyPlusData &state)
6879 : {
6880 :
6881 : // SUBROUTINE INFORMATION:
6882 : // AUTHOR Richard Liesen
6883 : // DATE WRITTEN October 1997
6884 :
6885 : // PURPOSE OF THIS SUBROUTINE:
6886 : // This file reads the Ground Temps from the input file and puts them
6887 : // in a new variable.
6888 :
6889 : // Initialize Site:GroundTemperature:BuildingSurface object
6890 1602 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr =
6891 1602 : GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteBuildingSurface, "");
6892 :
6893 : // Initialize Site:GroundTemperature:FCFactorMethod object
6894 1602 : state.dataWeather->siteFCFactorMethodGroundTempsPtr =
6895 1602 : GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteFCFactorMethod, "");
6896 :
6897 : // Initialize Site:GroundTemperature:Shallow object
6898 1602 : state.dataWeather->siteShallowGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteShallow, "");
6899 :
6900 : // Initialize Site:GroundTemperature:Deep object
6901 801 : state.dataWeather->siteDeepGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteDeep, "");
6902 801 : }
6903 :
6904 801 : void GetGroundReflectances(EnergyPlusData &state, bool &ErrorsFound)
6905 : {
6906 :
6907 : // SUBROUTINE INFORMATION:
6908 : // AUTHOR Linda Lawrie
6909 : // DATE WRITTEN March 2002
6910 :
6911 : // PURPOSE OF THIS SUBROUTINE:
6912 : // This file reads the Ground Reflectances from the input file (optional) and
6913 : // places them in the monthly array.
6914 :
6915 801 : auto const &ipsc = state.dataIPShortCut;
6916 801 : ipsc->cCurrentModuleObject = "Site:GroundReflectance";
6917 801 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6918 801 : if (nObjs != 0) {
6919 3 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6920 3 : Array1D<Real64> GndProps(12); // Temporary array to transfer ground reflectances
6921 3 : if (nObjs == 1) {
6922 : int GndNumAlpha; // Number of construction alpha names being passed
6923 : int GndNumProp; // dummy variable for properties being passed
6924 : int IOStat; // IO Status when calling get input subroutine
6925 : // Get the object names for each construction from the input processor
6926 6 : state.dataInputProcessing->inputProcessor->getObjectItem(
6927 3 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6928 :
6929 3 : if (GndNumProp < 12) {
6930 0 : ShowSevereError(state, format("{}: Less than 12 values entered.", ipsc->cCurrentModuleObject));
6931 0 : ErrorsFound = true;
6932 : }
6933 :
6934 : // Assign the ground reflectances to the variable
6935 3 : state.dataWeather->GroundReflectances({1, 12}) = GndProps({1, 12});
6936 :
6937 : } else {
6938 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6939 0 : ErrorsFound = true;
6940 : }
6941 3 : }
6942 :
6943 : // Write Final Ground Reflectance Information to the initialization output file
6944 801 : print(state.files.eio,
6945 : "{}\n",
6946 : "! "
6947 : "<Site:GroundReflectance>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{dimensionless},"
6948 : "May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{dimensionless},Oct{"
6949 : "dimensionless},Nov{dimensionless},Dec{dimensionless}");
6950 :
6951 801 : print(state.files.eio, " Site:GroundReflectance");
6952 10413 : for (int i = 1; i <= 12; ++i) {
6953 9612 : print(state.files.eio, ", {:5.2F}", state.dataWeather->GroundReflectances(i));
6954 : }
6955 801 : print(state.files.eio, "\n");
6956 801 : }
6957 :
6958 801 : void GetSnowGroundRefModifiers(EnergyPlusData &state, bool &ErrorsFound)
6959 : {
6960 :
6961 : // SUBROUTINE INFORMATION:
6962 : // AUTHOR Linda Lawrie
6963 : // DATE WRITTEN March 2002
6964 :
6965 : // PURPOSE OF THIS SUBROUTINE:
6966 : // This file reads the Snow Ground Reflectance Modifiers from the input file (optional) and
6967 : // places them in the variables.
6968 :
6969 801 : auto const &ipsc = state.dataIPShortCut;
6970 801 : ipsc->cCurrentModuleObject = "Site:GroundReflectance:SnowModifier";
6971 801 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6972 801 : if (nObjs != 0) {
6973 2 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6974 2 : Array1D<Real64> GndProps(2); // Temporary array to transfer ground reflectances
6975 2 : if (nObjs == 1) {
6976 : int GndNumAlpha; // Number of construction alpha names being passed
6977 : int GndNumProp; // dummy variable for properties being passed
6978 : int IOStat; // IO Status when calling get input subroutine
6979 : // Get the object names for each construction from the input processor
6980 4 : state.dataInputProcessing->inputProcessor->getObjectItem(
6981 2 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6982 :
6983 : // Assign the ground reflectances to the variable
6984 2 : state.dataWeather->SnowGndRefModifier = GndProps(1);
6985 2 : state.dataWeather->SnowGndRefModifierForDayltg = GndProps(2);
6986 :
6987 : } else {
6988 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6989 0 : ErrorsFound = true;
6990 : }
6991 2 : }
6992 :
6993 : // Write Final Ground Reflectance Modifier Information to the initialization output file
6994 801 : print(state.files.eio, "{}\n", "! <Site:GroundReflectance:SnowModifier>, Normal, Daylighting {dimensionless}");
6995 : static constexpr std::string_view Format_720(" Site:GroundReflectance:SnowModifier, {:7.3F}, {:7.3F}\n");
6996 801 : print(state.files.eio, Format_720, state.dataWeather->SnowGndRefModifier, state.dataWeather->SnowGndRefModifierForDayltg);
6997 :
6998 801 : print(state.files.eio,
6999 : "{}\n",
7000 : "! "
7001 : "<Site:GroundReflectance:Snow>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
7002 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
7003 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
7004 801 : print(state.files.eio, "{}", " Site:GroundReflectance:Snow");
7005 10413 : for (int i = 1; i <= 12; ++i) {
7006 9612 : print(state.files.eio, ", {:5.2F}", max(min(state.dataWeather->GroundReflectances(i) * state.dataWeather->SnowGndRefModifier, 1.0), 0.0));
7007 : }
7008 801 : print(state.files.eio, "\n");
7009 801 : print(state.files.eio,
7010 : "{}\n",
7011 : "! "
7012 : "<Site:GroundReflectance:Snow:Daylighting>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
7013 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
7014 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
7015 801 : print(state.files.eio, " Site:GroundReflectance:Snow:Daylighting");
7016 10413 : for (nObjs = 1; nObjs <= 12; ++nObjs) {
7017 9612 : print(state.files.eio,
7018 : ", {:5.2F}",
7019 19224 : max(min(state.dataWeather->GroundReflectances(nObjs) * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0));
7020 : }
7021 801 : print(state.files.eio, "\n");
7022 801 : }
7023 :
7024 801 : void GetWaterMainsTemperatures(EnergyPlusData &state, bool &ErrorsFound)
7025 : {
7026 :
7027 : // SUBROUTINE INFORMATION:
7028 : // AUTHOR Peter Graham Ellis
7029 : // DATE WRITTEN January 2005
7030 :
7031 : // PURPOSE OF THIS SUBROUTINE:
7032 : // Reads the input data for the WATER MAINS TEMPERATURES object.
7033 :
7034 801 : constexpr std::string_view routineName = "GetWaterMainsTemperatures";
7035 :
7036 801 : auto const &ipsc = state.dataIPShortCut;
7037 801 : ipsc->cCurrentModuleObject = "Site:WaterMainsTemperature";
7038 801 : int NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
7039 :
7040 801 : if (NumObjects == 1) {
7041 : int NumAlphas; // Number of elements in the alpha array
7042 : int NumNums; // Number of elements in the numeric array
7043 : int IOStat; // IO Status when calling get input subroutine
7044 133 : Array1D_string AlphArray(2); // Character string data
7045 133 : Array1D<Real64> NumArray(2); // Numeric data
7046 399 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
7047 133 : ipsc->cCurrentModuleObject,
7048 : 1,
7049 : AlphArray,
7050 : NumAlphas,
7051 : NumArray,
7052 : NumNums,
7053 : IOStat,
7054 133 : ipsc->lNumericFieldBlanks,
7055 133 : ipsc->lAlphaFieldBlanks,
7056 133 : ipsc->cAlphaFieldNames,
7057 133 : ipsc->cNumericFieldNames);
7058 :
7059 133 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
7060 :
7061 266 : state.dataWeather->WaterMainsTempsMethod =
7062 133 : static_cast<Weather::WaterMainsTempCalcMethod>(getEnumValue(waterMainsCalcMethodNamesUC, AlphArray(1)));
7063 :
7064 133 : switch (state.dataWeather->WaterMainsTempsMethod) {
7065 5 : case WaterMainsTempCalcMethod::Schedule: {
7066 5 : if (ipsc->lAlphaFieldBlanks(2)) {
7067 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
7068 0 : ErrorsFound = true;
7069 5 : } else if ((state.dataWeather->waterMainsTempSched = Sched::GetSchedule(state, AlphArray(2))) == nullptr) {
7070 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
7071 0 : ErrorsFound = true;
7072 : }
7073 5 : } break;
7074 :
7075 128 : case WaterMainsTempCalcMethod::Correlation: {
7076 128 : if (NumNums == 0) {
7077 0 : ShowSevereError(state, format("{}: Missing Annual Average and Maximum Difference fields.", ipsc->cCurrentModuleObject));
7078 0 : ErrorsFound = true;
7079 128 : } else if (NumNums == 1) {
7080 0 : ShowSevereError(state, format("{}: Missing Maximum Difference field.", ipsc->cCurrentModuleObject));
7081 0 : ErrorsFound = true;
7082 : } else {
7083 128 : state.dataWeather->WaterMainsTempsAnnualAvgAirTemp = NumArray(1);
7084 128 : state.dataWeather->WaterMainsTempsMaxDiffAirTemp = NumArray(2);
7085 : }
7086 128 : } break;
7087 0 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile: {
7088 : // No action
7089 0 : } break;
7090 0 : default: {
7091 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), AlphArray(1));
7092 0 : ErrorsFound = true;
7093 0 : } break;
7094 : } // switch
7095 :
7096 801 : } else if (NumObjects > 1) {
7097 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
7098 0 : ErrorsFound = true;
7099 : }
7100 801 : }
7101 :
7102 2925340 : void CalcWaterMainsTemp(EnergyPlusData &state)
7103 : {
7104 :
7105 : // SUBROUTINE INFORMATION:
7106 : // AUTHOR Peter Graham Ellis
7107 : // DATE WRITTEN January 2005
7108 : // MODIFIED June 2018, B. Nigusse
7109 :
7110 : // PURPOSE OF THIS SUBROUTINE:
7111 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
7112 :
7113 : // METHODOLOGY EMPLOYED:
7114 : // Water mains temperature is either taken from a schedule or calculated by a correlation. The correlation
7115 : // is fit to Fahrenheit units, so the air temperature values are first convert to F, then mains temperature
7116 : // is calculated and converted back to C.
7117 :
7118 2925340 : switch (state.dataWeather->WaterMainsTempsMethod) {
7119 16008 : case WaterMainsTempCalcMethod::Schedule:
7120 16008 : state.dataEnvrn->WaterMainsTemp = state.dataWeather->waterMainsTempSched->getCurrentVal();
7121 16008 : break;
7122 518326 : case WaterMainsTempCalcMethod::Correlation:
7123 518326 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(
7124 518326 : state, state.dataWeather->WaterMainsTempsAnnualAvgAirTemp, state.dataWeather->WaterMainsTempsMaxDiffAirTemp);
7125 518326 : break;
7126 0 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
7127 0 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
7128 0 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
7129 0 : state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp,
7130 0 : state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff);
7131 : } else {
7132 0 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
7133 : }
7134 0 : break;
7135 2391006 : default:
7136 2391006 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
7137 2391006 : break;
7138 : }
7139 2925340 : }
7140 :
7141 : Real64
7142 518326 : WaterMainsTempFromCorrelation(EnergyPlusData const &state, Real64 const AnnualOAAvgDryBulbTemp, Real64 const MonthlyOAAvgDryBulbTempMaxDiff)
7143 : {
7144 :
7145 : // SUBROUTINE INFORMATION:
7146 : // AUTHOR Peter Graham Ellis
7147 : // DATE WRITTEN January 2005
7148 : // MODIFIED B Nigusse June 2018 (Refactored)
7149 :
7150 : // PURPOSE OF THIS SUBROUTINE:
7151 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
7152 :
7153 : // METHODOLOGY EMPLOYED:
7154 : // Water mains temperature calculated by a correlation. The correlation is fit to Fahrenheit units, so the
7155 : // air temperature values are first convert to F, then mains temperature is calculated and converted back to C.
7156 : // used for Calculated Method: 'Correlation' and 'CorrelationFromWeatherFile'.
7157 :
7158 : // REFERENCES:
7159 : // Correlation developed by Jay Burch and Craig Christensen at NREL, described in:
7160 : // Hendron, R., Anderson, R., Christensen, C., Eastment, M., and Reeves, P. 2004. "Development of an Energy
7161 : // Savings Benchmark for All Residential End-Uses", Proceedings of SimBuild 2004, IBPSA-USA National Conference,
7162 : // Boulder, CO, August 4 - 6, 2004.
7163 :
7164 : // Annual Average Outdoor Air Temperature (F)
7165 518326 : Real64 const Tavg = AnnualOAAvgDryBulbTemp * (9.0 / 5.0) + 32.0;
7166 : // Maximum difference in monthly average outdoor air temperatures (deltaF)
7167 518326 : Real64 const Tdiff = MonthlyOAAvgDryBulbTempMaxDiff * (9.0 / 5.0);
7168 :
7169 518326 : Real64 const Ratio = 0.4 + 0.01 * (Tavg - 44.0);
7170 518326 : Real64 const Lag = 35.0 - 1.0 * (Tavg - 44.0);
7171 518326 : Real64 constexpr Offset = 6.0;
7172 518326 : int const latitude_sign = (state.dataEnvrn->Latitude >= 0) ? 1 : -1;
7173 :
7174 : // calculated water main temp (F)
7175 : Real64 CurrentWaterMainsTemp =
7176 518326 : Tavg + Offset +
7177 518326 : Ratio * (Tdiff / 2.0) * latitude_sign * std::sin((0.986 * (state.dataEnvrn->DayOfYear - 15.0 - Lag) - 90) * Constant::DegToRad);
7178 :
7179 518326 : if (CurrentWaterMainsTemp < 32.0) {
7180 4338 : CurrentWaterMainsTemp = 32.0;
7181 : }
7182 :
7183 : // Convert F to C
7184 518326 : return (CurrentWaterMainsTemp - 32.0) * (5.0 / 9.0);
7185 : }
7186 801 : void GetWeatherStation(EnergyPlusData &state, bool &ErrorsFound)
7187 : {
7188 :
7189 : // SUBROUTINE INFORMATION:
7190 : // AUTHOR Peter Graham Ellis
7191 : // DATE WRITTEN January 2006
7192 :
7193 : // PURPOSE OF THIS SUBROUTINE:
7194 : // Reads the input data for the WEATHER STATION object.
7195 :
7196 801 : auto const &ipsc = state.dataIPShortCut;
7197 801 : ipsc->cCurrentModuleObject = "Site:WeatherStation";
7198 801 : int const NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
7199 :
7200 : // Default conditions for a weather station in an open field at a height of 10 m. (These should match the IDD defaults.)
7201 801 : Real64 WeatherFileWindSensorHeight = 10.0; // Height of the wind sensor at the weather station, i.e., weather file
7202 801 : Real64 WeatherFileWindExp = 0.14; // Exponent for the wind velocity profile at the weather station
7203 801 : Real64 WeatherFileWindBLHeight = 270.0; // Boundary layer height for the wind velocity profile at the weather station (m)
7204 801 : Real64 WeatherFileTempSensorHeight = 1.5; // Height of the air temperature sensor at the weather station (m)
7205 :
7206 801 : if (NumObjects == 1) {
7207 : int NumAlphas; // Number of elements in the alpha array
7208 : int NumNums; // Number of elements in the numeric array
7209 : int IOStat; // IO Status when calling get input subroutine
7210 1 : Array1D_string AlphArray(1); // Character string data
7211 1 : Array1D<Real64> NumArray(4); // Numeric data
7212 2 : state.dataInputProcessing->inputProcessor->getObjectItem(
7213 1 : state, ipsc->cCurrentModuleObject, 1, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
7214 :
7215 1 : if (NumNums > 0) {
7216 1 : WeatherFileWindSensorHeight = NumArray(1);
7217 : }
7218 1 : if (NumNums > 1) {
7219 1 : WeatherFileWindExp = NumArray(2);
7220 : }
7221 1 : if (NumNums > 2) {
7222 1 : WeatherFileWindBLHeight = NumArray(3);
7223 : }
7224 1 : if (NumNums > 3) {
7225 1 : WeatherFileTempSensorHeight = NumArray(4);
7226 : }
7227 :
7228 801 : } else if (NumObjects > 1) {
7229 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
7230 0 : ErrorsFound = true;
7231 : }
7232 :
7233 801 : state.dataEnvrn->WeatherFileWindModCoeff = std::pow(WeatherFileWindBLHeight / WeatherFileWindSensorHeight, WeatherFileWindExp);
7234 1602 : state.dataEnvrn->WeatherFileTempModCoeff = DataEnvironment::AtmosphericTempGradient * DataEnvironment::EarthRadius *
7235 801 : WeatherFileTempSensorHeight / (DataEnvironment::EarthRadius + WeatherFileTempSensorHeight);
7236 :
7237 : // Write to the initialization output file
7238 801 : print(state.files.eio,
7239 : "{}\n",
7240 : "! <Environment:Weather Station>,Wind Sensor Height Above Ground {m},Wind Speed Profile Exponent "
7241 : "{},Wind Speed Profile Boundary Layer Thickness {m},Air Temperature Sensor Height Above Ground {m},Wind "
7242 : "Speed Modifier Coefficient-Internal,Temperature Modifier Coefficient-Internal");
7243 :
7244 : // Formats
7245 : static constexpr std::string_view Format_720("Environment:Weather Station,{:.3R},{:.3R},{:.3R},{:.3R},{:.3R},{:.3R}\n");
7246 801 : print(state.files.eio,
7247 : Format_720,
7248 : WeatherFileWindSensorHeight,
7249 : WeatherFileWindExp,
7250 : WeatherFileWindBLHeight,
7251 : WeatherFileTempSensorHeight,
7252 801 : state.dataEnvrn->WeatherFileWindModCoeff,
7253 801 : state.dataEnvrn->WeatherFileTempModCoeff);
7254 801 : }
7255 :
7256 2925340 : void DayltgCurrentExtHorizIllum(EnergyPlusData &state)
7257 : {
7258 :
7259 : // SUBROUTINE INFORMATION:
7260 : // AUTHOR Fred Winkelmann
7261 : // DATE WRITTEN July 1997
7262 : // MODIFIED Nov98 (FW); Nov 2000 (FW)
7263 :
7264 : // PURPOSE OF THIS SUBROUTINE:
7265 : // CALCULATES EXTERIOR DAYLIGHT ILLUMINANCE AND LUMINOUS EFFICACY
7266 :
7267 : // METHODOLOGY EMPLOYED:
7268 : // CALLED by SetCurrentWeather.
7269 : // CALCULATES THE CURRENT-TIME-STEP
7270 : // ILLUMINANCE ON AN UNOBSTRUCTED HORIZONTAL SURFACE FROM THE
7271 : // THE SKY AND FROM DIRECT SUN.
7272 :
7273 : // REFERENCES:
7274 : // Based on DOE-2.1E subroutine DEXTIL.
7275 :
7276 : // SOLCOS(3), below, is the cosine of the solar zenith angle.
7277 2925340 : if (state.dataEnvrn->SunIsUp) {
7278 : // Exterior horizontal beam irradiance (W/m2)
7279 1462278 : Real64 SDIRH = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z;
7280 : // Exterior horizontal sky diffuse irradiance (W/m2)
7281 1462278 : Real64 SDIFH = state.dataEnvrn->DifSolarRad;
7282 : // Fraction of sky covered by clouds
7283 1462278 : state.dataEnvrn->CloudFraction = pow_2(SDIFH / (SDIRH + SDIFH + 0.0001));
7284 : // Luminous efficacy of sky diffuse solar and beam solar (lumens/W);
7285 : // Horizontal illuminance from sky and horizontal beam illuminance (lux)
7286 : // obtained from solar quantities on weather file and luminous efficacy.
7287 :
7288 1462278 : DayltgLuminousEfficacy(state, state.dataEnvrn->PDIFLW, state.dataEnvrn->PDIRLW);
7289 1462278 : state.dataEnvrn->HISKF = SDIFH * state.dataEnvrn->PDIFLW;
7290 1462278 : state.dataEnvrn->HISUNF = SDIRH * state.dataEnvrn->PDIRLW;
7291 1462278 : state.dataEnvrn->HISUNFnorm = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW;
7292 : } else {
7293 1463062 : state.dataEnvrn->CloudFraction = 0.0;
7294 1463062 : state.dataEnvrn->PDIFLW = 0.0;
7295 1463062 : state.dataEnvrn->PDIRLW = 0.0;
7296 1463062 : state.dataEnvrn->HISKF = 0.0;
7297 1463062 : state.dataEnvrn->HISUNF = 0.0;
7298 1463062 : state.dataEnvrn->HISUNFnorm = 0.0;
7299 1463062 : state.dataEnvrn->SkyClearness = 0.0;
7300 1463062 : state.dataEnvrn->SkyBrightness = 0.0;
7301 : }
7302 2925340 : }
7303 :
7304 1462278 : void DayltgLuminousEfficacy(EnergyPlusData &state,
7305 : Real64 &DiffLumEff, // Luminous efficacy of sky diffuse solar radiation (lum/W)
7306 : Real64 &DirLumEff // Luminous efficacy of beam solar radiation (lum/W)
7307 : )
7308 : {
7309 : // SUBROUTINE INFORMATION:
7310 : // AUTHOR Fred Winkelmann
7311 : // DATE WRITTEN July 1997
7312 : // MODIFIED August 2009, BG fixed upper bound for sky clearness bin 7
7313 :
7314 : // PURPOSE OF THIS SUBROUTINE:
7315 : // Uses diffuse horizontal solar irradiance, direct normal solar
7316 : // irradiance, atmospheric moisture and sun position
7317 : // to determine the luminous efficacy in lumens/watt
7318 : // of sky diffuse solar radiation and direct normal solar radiation.
7319 : // Based on an empirical method described in
7320 : // R. Perez, P. Ineichen, R. Seals, J. Michalsky and R. Stewart,
7321 : // "Modeling daylight availability and irradiance components from direct
7322 : // global irradiance components from direct and global irradiance,"
7323 : // Solar Energy 44 (1990) 271-289.
7324 :
7325 : // Diffuse luminous efficacy coefficients
7326 : static constexpr std::array<Real64, 8> ADiffLumEff = {97.24, 107.22, 104.97, 102.39, 100.71, 106.42, 141.88, 152.23};
7327 : static constexpr std::array<Real64, 8> BDiffLumEff = {-0.46, 1.15, 2.96, 5.59, 5.94, 3.83, 1.90, 0.35};
7328 : static constexpr std::array<Real64, 8> CDiffLumEff = {12.00, 0.59, -5.53, -13.95, -22.75, -36.15, -53.24, -45.27};
7329 : static constexpr std::array<Real64, 8> DDiffLumEff = {-8.91, -3.95, -8.77, -13.90, -23.74, -28.83, -14.03, -7.98};
7330 : // Direct luminous efficacy coefficients
7331 : static constexpr std::array<Real64, 8> ADirLumEff = {57.20, 98.99, 109.83, 110.34, 106.36, 107.19, 105.75, 101.18};
7332 : static constexpr std::array<Real64, 8> BDirLumEff = {-4.55, -3.46, -4.90, -5.84, -3.97, -1.25, 0.77, 1.58};
7333 : static constexpr std::array<Real64, 8> CDirLumEff = {-2.98, -1.21, -1.71, -1.99, -1.75, -1.51, -1.26, -1.10};
7334 : static constexpr std::array<Real64, 8> DDirLumEff = {117.12, 12.38, -8.81, -4.56, -6.16, -26.73, -34.44, -8.29};
7335 : // Monthly exterrestrial direct normal illuminance (lum/m2)
7336 : static constexpr std::array<Real64, 12> ExtraDirNormIll = {
7337 : 131153.0, 130613.0, 128992.0, 126816.0, 124731.0, 123240.0, 122652.0, 123120.0, 124576.0, 126658.0, 128814.0, 130471.0};
7338 :
7339 1462278 : Real64 const SunZenith = std::acos(state.dataEnvrn->SOLCOS.z); // Solar zenith angle (radians)
7340 1462278 : Real64 const SunAltitude = Constant::PiOvr2 - SunZenith; // Solar altitude angle (radians)
7341 1462278 : Real64 const SinSunAltitude = std::sin(SunAltitude);
7342 : // Clearness of sky. SkyClearness close to 1.0 corresponds to an overcast sky.
7343 : // SkyClearness > 6 is a clear sky.
7344 : // DifSolarRad is the diffuse horizontal irradiance.
7345 : // BeamSolarRad is the direct normal irradiance.
7346 1462278 : Real64 const Zeta = 1.041 * pow_3(SunZenith);
7347 2924556 : state.dataEnvrn->SkyClearness =
7348 1462278 : ((state.dataEnvrn->DifSolarRad + state.dataEnvrn->BeamSolarRad) / (state.dataEnvrn->DifSolarRad + 0.0001) + Zeta) / (1.0 + Zeta);
7349 : // Relative optical air mass
7350 : Real64 const relAirMass =
7351 1462278 : (1.0 - 0.1 * state.dataEnvrn->Elevation / 1000.0) / (SinSunAltitude + 0.15 / std::pow(SunAltitude / Constant::DegToRad + 3.885, 1.253));
7352 : // In the following, 93.73 is the extraterrestrial luminous efficacy
7353 1462278 : state.dataEnvrn->SkyBrightness = (state.dataEnvrn->DifSolarRad * 93.73) * relAirMass / ExtraDirNormIll[state.dataEnvrn->Month - 1];
7354 : int ISkyClearness; // Sky clearness bin
7355 1462278 : if (state.dataEnvrn->SkyClearness <= 1.065) {
7356 548429 : ISkyClearness = 0;
7357 913849 : } else if (state.dataEnvrn->SkyClearness <= 1.23) {
7358 11742 : ISkyClearness = 1;
7359 902107 : } else if (state.dataEnvrn->SkyClearness <= 1.50) {
7360 15366 : ISkyClearness = 2;
7361 886741 : } else if (state.dataEnvrn->SkyClearness <= 1.95) {
7362 20863 : ISkyClearness = 3;
7363 865878 : } else if (state.dataEnvrn->SkyClearness <= 2.80) {
7364 99575 : ISkyClearness = 4;
7365 766303 : } else if (state.dataEnvrn->SkyClearness <= 4.50) {
7366 277987 : ISkyClearness = 5;
7367 488316 : } else if (state.dataEnvrn->SkyClearness <= 6.20) {
7368 177322 : ISkyClearness = 6;
7369 : } else {
7370 310994 : ISkyClearness = 7;
7371 : }
7372 :
7373 : // Atmospheric moisture (cm of precipitable water)
7374 1462278 : Real64 const AtmosMoisture = std::exp(0.07 * state.dataEnvrn->OutDewPointTemp - 0.075);
7375 : // Sky diffuse luminous efficacy
7376 1462278 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7377 505048 : DiffLumEff = 0.0;
7378 : } else {
7379 957230 : DiffLumEff = ADiffLumEff[ISkyClearness] + BDiffLumEff[ISkyClearness] * AtmosMoisture +
7380 957230 : CDiffLumEff[ISkyClearness] * state.dataEnvrn->SOLCOS.z +
7381 957230 : DDiffLumEff[ISkyClearness] * std::log(state.dataEnvrn->SkyBrightness);
7382 : }
7383 : // Direct normal luminous efficacy
7384 1462278 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7385 505048 : DirLumEff = 0.0;
7386 : } else {
7387 957230 : DirLumEff =
7388 957230 : max(0.0,
7389 957230 : ADirLumEff[ISkyClearness] + BDirLumEff[ISkyClearness] * AtmosMoisture +
7390 957230 : CDirLumEff[ISkyClearness] * std::exp(5.73 * SunZenith - 5.0) + DDirLumEff[ISkyClearness] * state.dataEnvrn->SkyBrightness);
7391 : }
7392 1462278 : }
7393 :
7394 2145 : Real64 GetSTM(Real64 const Longitude) // Longitude from user input
7395 : {
7396 : // FUNCTION INFORMATION:
7397 : // AUTHOR Linda K. Lawrie
7398 : // DATE WRITTEN August 2003
7399 :
7400 : // PURPOSE OF THIS FUNCTION:
7401 : // This function determines the "standard time meridian" from the input
7402 : // longitude. Calculates the proper Meridian from Longitude. This
7403 : // value is needed for weather calculations so that the sun comes
7404 : // up and goes down at the right times.
7405 :
7406 : Real64 GetSTM;
7407 :
7408 2145 : Array1D<Real64> longl({-12, 12}); // Lower Longitude value for a Time Zone
7409 2145 : Array1D<Real64> longh({-12, 12}); // Upper Longitude value for a Time Zone
7410 :
7411 2145 : GetSTM = 0.0;
7412 :
7413 2145 : longl(0) = -7.5;
7414 2145 : longh(0) = 7.5;
7415 27885 : for (int i = 1; i <= 12; ++i) {
7416 25740 : longl(i) = longl(i - 1) + 15.0;
7417 25740 : longh(i) = longh(i - 1) + 15.0;
7418 : }
7419 27885 : for (int i = 1; i <= 12; ++i) {
7420 25740 : longl(-i) = longl(-i + 1) - 15.0;
7421 25740 : longh(-i) = longh(-i + 1) - 15.0;
7422 : }
7423 2145 : Real64 temp = mod(Longitude, 360.0);
7424 2145 : if (temp > 180.0) {
7425 0 : temp -= 180.0;
7426 : }
7427 : Real64 tz; // resultant tz meridian
7428 13592 : for (int i = -12; i <= 12; ++i) {
7429 13592 : if (temp > longl(i) && temp <= longh(i)) {
7430 2145 : tz = mod(i, 24.0);
7431 2145 : GetSTM = tz;
7432 2145 : break;
7433 : }
7434 : }
7435 :
7436 2145 : return GetSTM;
7437 2145 : }
7438 :
7439 6344 : void ProcessEPWHeader(EnergyPlusData &state, EpwHeaderType const headerType, std::string &Line, bool &ErrorsFound)
7440 : {
7441 :
7442 : // SUBROUTINE INFORMATION:
7443 : // AUTHOR Linda Lawrie
7444 : // DATE WRITTEN December 1999
7445 :
7446 : // PURPOSE OF THIS SUBROUTINE:
7447 : // This subroutine processes each header line in the EPW weather file.
7448 :
7449 : // METHODOLOGY EMPLOYED:
7450 : // File is positioned to the correct line, then backspaced. This routine
7451 : // reads in the line and processes as appropriate.
7452 :
7453 : Weather::DateType dateType;
7454 : int NumHdArgs;
7455 :
7456 : // Strip off Header value from Line
7457 6344 : std::string::size_type Pos = index(Line, ',');
7458 6344 : if ((Pos == std::string::npos) && !((headerType == EpwHeaderType::Comments1) || (headerType == EpwHeaderType::Comments2))) {
7459 0 : ShowSevereError(state, "Invalid Header line in in.epw -- no commas");
7460 0 : ShowContinueError(state, format("Line={}", Line));
7461 0 : ShowFatalError(state, "Previous conditions cause termination.");
7462 : }
7463 6344 : if (Pos != std::string::npos) {
7464 6344 : Line.erase(0, Pos + 1);
7465 : }
7466 :
7467 6344 : switch (headerType) {
7468 793 : case Weather::EpwHeaderType::Location: {
7469 :
7470 : // LOCATION, A1 [City], A2 [State/Province/Region], A3 [Country],
7471 : // A4 [Source], N1 [WMO], N2 [Latitude],
7472 : // N3 [Longitude], N4 [Time Zone], N5 [Elevation {m}]
7473 :
7474 793 : NumHdArgs = 9;
7475 7930 : for (int i = 1; i <= NumHdArgs; ++i) {
7476 7137 : strip(Line);
7477 7137 : Pos = index(Line, ',');
7478 7137 : if (Pos == std::string::npos) {
7479 789 : if (len(Line) == 0) {
7480 0 : while (Pos == std::string::npos) {
7481 0 : Line = state.files.inputWeatherFile.readLine().data;
7482 0 : strip(Line);
7483 0 : uppercase(Line);
7484 0 : Pos = index(Line, ',');
7485 : }
7486 : } else {
7487 789 : Pos = len(Line);
7488 : }
7489 : }
7490 :
7491 7137 : switch (i) {
7492 793 : case 1:
7493 793 : state.dataWeather->EPWHeaderTitle = stripped(Line.substr(0, Pos));
7494 793 : break;
7495 2379 : case 2:
7496 : case 3:
7497 : case 4:
7498 2379 : state.dataWeather->EPWHeaderTitle = strip(state.dataWeather->EPWHeaderTitle) + ' ' + stripped(Line.substr(0, Pos));
7499 2379 : break;
7500 793 : case 5:
7501 793 : state.dataWeather->EPWHeaderTitle += " WMO#=" + stripped(Line.substr(0, Pos));
7502 793 : break;
7503 3172 : case 6:
7504 : case 7:
7505 : case 8:
7506 : case 9: {
7507 : bool errFlag;
7508 3172 : Real64 const Number = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7509 3172 : if (!errFlag) {
7510 3172 : switch (i) {
7511 793 : case 6:
7512 793 : state.dataWeather->WeatherFileLatitude = Number;
7513 793 : break;
7514 793 : case 7:
7515 793 : state.dataWeather->WeatherFileLongitude = Number;
7516 793 : break;
7517 793 : case 8:
7518 793 : state.dataWeather->WeatherFileTimeZone = Number;
7519 793 : break;
7520 793 : case 9:
7521 793 : state.dataWeather->WeatherFileElevation = Number;
7522 793 : break;
7523 0 : default:
7524 0 : break;
7525 : }
7526 : }
7527 3172 : } break;
7528 0 : default:
7529 0 : ShowSevereError(state, format("GetEPWHeader:LOCATION, invalid numeric={}", Line.substr(0, Pos)));
7530 0 : ErrorsFound = true;
7531 0 : break;
7532 : }
7533 7137 : Line.erase(0, Pos + 1);
7534 : }
7535 793 : state.dataEnvrn->WeatherFileLocationTitle = stripped(state.dataWeather->EPWHeaderTitle);
7536 793 : } break;
7537 793 : case Weather::EpwHeaderType::TypicalExtremePeriods: {
7538 793 : strip(Line);
7539 793 : Pos = index(Line, ',');
7540 793 : if (Pos == std::string::npos) {
7541 0 : if (len(Line) == 0) {
7542 0 : while (Pos == std::string::npos && len(Line) == 0) {
7543 0 : Line = state.files.inputWeatherFile.readLine().data;
7544 0 : strip(Line);
7545 0 : Pos = index(Line, ',');
7546 : }
7547 : } else {
7548 0 : Pos = len(Line);
7549 : }
7550 : }
7551 : bool IOStatus;
7552 793 : state.dataWeather->NumEPWTypExtSets = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7553 793 : Line.erase(0, Pos + 1);
7554 793 : state.dataWeather->TypicalExtremePeriods.allocate(state.dataWeather->NumEPWTypExtSets);
7555 793 : int TropExtremeCount = 0;
7556 5422 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7557 4629 : strip(Line);
7558 4629 : Pos = index(Line, ',');
7559 4629 : if (Pos != std::string::npos) {
7560 4629 : state.dataWeather->TypicalExtremePeriods(i).Title = Line.substr(0, Pos);
7561 4629 : Line.erase(0, Pos + 1);
7562 : } else {
7563 0 : ShowWarningError(state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={}", Line.substr(0, Pos)));
7564 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7565 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7566 0 : break;
7567 : }
7568 4629 : Pos = index(Line, ',');
7569 4629 : if (Pos != std::string::npos) {
7570 4629 : state.dataWeather->TypicalExtremePeriods(i).TEType = Line.substr(0, Pos);
7571 4629 : Line.erase(0, Pos + 1);
7572 4629 : if (Util::SameString(state.dataWeather->TypicalExtremePeriods(i).TEType, "EXTREME")) {
7573 1586 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MAX")) {
7574 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7575 1586 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MIN")) {
7576 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7577 1586 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MAX")) {
7578 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMax";
7579 1586 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MIN")) {
7580 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMin";
7581 : // to account for problems earlier in weather files:
7582 1586 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7583 0 : if (TropExtremeCount == 0) {
7584 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Max";
7585 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7586 0 : ++TropExtremeCount;
7587 0 : } else if (TropExtremeCount == 1) {
7588 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Min";
7589 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7590 0 : ++TropExtremeCount;
7591 : }
7592 : } else { // make new short titles
7593 1586 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7594 728 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7595 858 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7596 728 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7597 130 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7598 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7599 65 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7600 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7601 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7602 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7603 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7604 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7605 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7606 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7607 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7608 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7609 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7610 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7611 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7612 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7613 : }
7614 : }
7615 : } else { // not extreme
7616 3043 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7617 728 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7618 2315 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7619 728 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7620 1587 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7621 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7622 1587 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7623 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7624 1587 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7625 728 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7626 859 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7627 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7628 859 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7629 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7630 858 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7631 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7632 793 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7633 65 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7634 728 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7635 728 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7636 : }
7637 : }
7638 : } else {
7639 0 : ShowWarningError(state,
7640 0 : format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={} {}",
7641 0 : state.dataWeather->TypicalExtremePeriods(i).Title,
7642 0 : Line.substr(0, Pos)));
7643 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7644 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7645 0 : break;
7646 : }
7647 : int PMonth;
7648 : int PDay;
7649 : int PWeekDay;
7650 4629 : Pos = index(Line, ',');
7651 4629 : if (Pos != std::string::npos) {
7652 4629 : std::string dateStringUC = Line.substr(0, Pos);
7653 4629 : dateStringUC = uppercase(dateStringUC);
7654 4629 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7655 4629 : if (dateType != DateType::Invalid) {
7656 4629 : if (PMonth != 0 && PDay != 0) {
7657 4629 : state.dataWeather->TypicalExtremePeriods(i).StartMonth = PMonth;
7658 4629 : state.dataWeather->TypicalExtremePeriods(i).StartDay = PDay;
7659 : }
7660 : } else {
7661 0 : ShowSevereError(
7662 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7663 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7664 0 : ErrorsFound = true;
7665 : }
7666 4629 : Line.erase(0, Pos + 1);
7667 4629 : }
7668 4629 : Pos = index(Line, ',');
7669 4629 : if (Pos != std::string::npos) {
7670 3836 : std::string dateStringUC = Line.substr(0, Pos);
7671 3836 : dateStringUC = uppercase(dateStringUC);
7672 3836 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7673 3836 : if (dateType != DateType::Invalid) {
7674 3836 : if (PMonth != 0 && PDay != 0) {
7675 3836 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7676 3836 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7677 : }
7678 : } else {
7679 0 : ShowSevereError(
7680 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7681 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7682 0 : ErrorsFound = true;
7683 : }
7684 3836 : Line.erase(0, Pos + 1);
7685 3836 : } else { // Pos=0, probably last one
7686 793 : std::string const dateStringUC = uppercase(Line);
7687 793 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7688 793 : if (dateType != DateType::Invalid) {
7689 793 : if (PMonth != 0 && PDay != 0) {
7690 793 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7691 793 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7692 : }
7693 : } else {
7694 0 : ShowSevereError(
7695 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7696 0 : ErrorsFound = true;
7697 : }
7698 793 : }
7699 : }
7700 : // Process periods to set up other values.
7701 5422 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7702 4629 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7703 : // JulianDay (Month,Day,LeapYearValue)
7704 4629 : std::string const ExtremePeriodTitle = Util::makeUPPER(typicalExtPer.ShortTitle);
7705 4629 : if (ExtremePeriodTitle == "SUMMER") {
7706 1456 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7707 728 : typicalExtPer.MatchValue = "SummerExtreme";
7708 728 : typicalExtPer.MatchValue1 = "TropicalHot";
7709 728 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7710 : } else {
7711 728 : typicalExtPer.MatchValue = "SummerTypical";
7712 : }
7713 :
7714 3173 : } else if (ExtremePeriodTitle == "WINTER") {
7715 1456 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7716 728 : typicalExtPer.MatchValue = "WinterExtreme";
7717 728 : typicalExtPer.MatchValue1 = "TropicalCold";
7718 728 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7719 : } else {
7720 728 : typicalExtPer.MatchValue = "WinterTypical";
7721 : }
7722 :
7723 1717 : } else if (ExtremePeriodTitle == "AUTUMN") {
7724 728 : typicalExtPer.MatchValue = "AutumnTypical";
7725 :
7726 989 : } else if (ExtremePeriodTitle == "SPRING") {
7727 728 : typicalExtPer.MatchValue = "SpringTypical";
7728 :
7729 261 : } else if (ExtremePeriodTitle == "WETSEASON") {
7730 65 : typicalExtPer.MatchValue = "WetSeason";
7731 :
7732 196 : } else if (ExtremePeriodTitle == "DRYSEASON") {
7733 65 : typicalExtPer.MatchValue = "DrySeason";
7734 :
7735 131 : } else if (ExtremePeriodTitle == "NOWETSEASON") {
7736 1 : typicalExtPer.MatchValue = "NoWetSeason";
7737 :
7738 130 : } else if (ExtremePeriodTitle == "NODRYSEASON") {
7739 0 : typicalExtPer.MatchValue = "NoDrySeason";
7740 :
7741 130 : } else if ((ExtremePeriodTitle == "NODRYSEASONMAX") || (ExtremePeriodTitle == "NOWETSEASONMAX")) {
7742 0 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7743 0 : typicalExtPer.MatchValue1 = "TropicalHot";
7744 0 : typicalExtPer.MatchValue2 = "SummerExtreme";
7745 :
7746 130 : } else if ((ExtremePeriodTitle == "NODRYSEASONMIN") || (ExtremePeriodTitle == "NOWETSEASONMIN")) {
7747 0 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7748 0 : typicalExtPer.MatchValue1 = "TropicalCold";
7749 0 : typicalExtPer.MatchValue2 = "WinterExtreme";
7750 :
7751 130 : } else if (ExtremePeriodTitle == "TROPICALHOT") {
7752 65 : typicalExtPer.MatchValue = "TropicalHot";
7753 65 : typicalExtPer.MatchValue1 = "SummerExtreme";
7754 65 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7755 :
7756 65 : } else if (ExtremePeriodTitle == "TROPICALCOLD") {
7757 65 : typicalExtPer.MatchValue = "TropicalCold";
7758 65 : typicalExtPer.MatchValue1 = "WinterExtreme";
7759 65 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7760 :
7761 : } else {
7762 0 : typicalExtPer.MatchValue = "Invalid - no match";
7763 : }
7764 4629 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, 0);
7765 4629 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, 0);
7766 4629 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7767 4622 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7768 : } else {
7769 7 : typicalExtPer.TotalDays =
7770 7 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7771 : }
7772 4629 : }
7773 793 : } break;
7774 793 : case Weather::EpwHeaderType::GroundTemperatures: {
7775 : // Added for ground surfaces defined with F or c factor method. TH 7/2009
7776 : // Assume the 0.5 m set of ground temperatures
7777 : // or first set on a weather file, if any.
7778 793 : Pos = index(Line, ',');
7779 793 : if (Pos != std::string::npos) {
7780 : bool errFlag;
7781 793 : int NumGrndTemps = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7782 793 : if (!errFlag && NumGrndTemps >= 1) {
7783 793 : Line.erase(0, Pos + 1);
7784 : // skip depth, soil conductivity, soil density, soil specific heat
7785 3965 : for (int i = 1; i <= 4; ++i) {
7786 3172 : Pos = index(Line, ',');
7787 3172 : if (Pos == std::string::npos) {
7788 0 : Line.clear();
7789 0 : break;
7790 : }
7791 3172 : Line.erase(0, Pos + 1);
7792 : }
7793 793 : state.dataWeather->GroundTempsFCFromEPWHeader = 0.0;
7794 793 : int actcount = 0;
7795 10309 : for (int i = 1; i <= 12; ++i) { // take the first set of ground temperatures.
7796 9516 : Pos = index(Line, ',');
7797 9516 : if (Pos != std::string::npos) {
7798 9516 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7799 9516 : ++actcount;
7800 : } else {
7801 0 : if (len(Line) > 0) {
7802 0 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7803 0 : ++actcount;
7804 : }
7805 0 : break;
7806 : }
7807 9516 : Line.erase(0, Pos + 1);
7808 : }
7809 793 : if (actcount == 12) {
7810 793 : state.dataWeather->wthFCGroundTemps = true;
7811 : }
7812 : }
7813 : }
7814 793 : } break;
7815 793 : case Weather::EpwHeaderType::HolidaysDST: {
7816 : // A1, \field LeapYear Observed
7817 : // \type choice
7818 : // \key Yes
7819 : // \key No
7820 : // \note Yes if Leap Year will be observed for this file
7821 : // \note No if Leap Year days (29 Feb) should be ignored in this file
7822 : // A2, \field Daylight Saving Start Day
7823 : // A3, \field Daylight Saving End Day
7824 : // N1, \field Number of Holidays
7825 : // A4, \field Holiday 1 Name
7826 : // A5, \field Holiday 1 Day
7827 : // etc.
7828 : // Start with Minimum number of NumHdArgs
7829 793 : uppercase(Line);
7830 793 : NumHdArgs = 4;
7831 793 : int CurCount = 0;
7832 3987 : for (int i = 1; i <= NumHdArgs; ++i) {
7833 3194 : strip(Line);
7834 3194 : Pos = index(Line, ',');
7835 3194 : if (Pos == std::string::npos) {
7836 789 : if (len(Line) == 0) {
7837 0 : while (Pos == std::string::npos) {
7838 0 : Line = state.files.inputWeatherFile.readLine().data;
7839 0 : strip(Line);
7840 0 : uppercase(Line);
7841 0 : Pos = index(Line, ',');
7842 : }
7843 : } else {
7844 789 : Pos = len(Line);
7845 : }
7846 : }
7847 :
7848 : int PMonth;
7849 : int PDay;
7850 : int PWeekDay;
7851 : bool IOStatus;
7852 3194 : if (i == 1) {
7853 793 : state.dataWeather->WFAllowsLeapYears = (Line[0] == 'Y');
7854 2401 : } else if (i == 2) {
7855 : // In this section, we call ProcessDateString, and if that fails, we can recover from it
7856 : // by setting DST to false, so we don't affect ErrorsFound
7857 :
7858 : // call ProcessDateString with local bool (unused)
7859 : bool errflag1;
7860 793 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, errflag1);
7861 793 : if (dateType != DateType::Invalid) {
7862 : // ErrorsFound is still false after ProcessDateString
7863 793 : if (PMonth == 0 && PDay == 0) {
7864 792 : state.dataWeather->EPWDaylightSaving = false;
7865 : } else {
7866 1 : state.dataWeather->EPWDaylightSaving = true;
7867 1 : state.dataWeather->EPWDST.StDateType = dateType;
7868 1 : state.dataWeather->EPWDST.StMon = PMonth;
7869 1 : state.dataWeather->EPWDST.StDay = PDay;
7870 1 : state.dataWeather->EPWDST.StWeekDay = PWeekDay;
7871 : }
7872 : } else {
7873 : // ErrorsFound is untouched
7874 0 : ShowContinueError(
7875 0 : state, format("ProcessEPWHeader: Invalid Daylight Saving Period Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7876 0 : ShowContinueError(state, format("...invalid header={}", epwHeaders[static_cast<int>(headerType)]));
7877 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7878 0 : state.dataWeather->EPWDaylightSaving = false;
7879 : }
7880 :
7881 1608 : } else if (i == 3) {
7882 793 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7883 793 : if (state.dataWeather->EPWDaylightSaving) {
7884 1 : if (dateType != DateType::Invalid) {
7885 1 : state.dataWeather->EPWDST.EnDateType = dateType;
7886 1 : state.dataWeather->EPWDST.EnMon = PMonth;
7887 1 : state.dataWeather->EPWDST.EnDay = PDay;
7888 1 : state.dataWeather->EPWDST.EnWeekDay = PWeekDay;
7889 : } else {
7890 0 : ShowWarningError(
7891 : state,
7892 0 : format("ProcessEPWHeader: Invalid Daylight Saving Period End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7893 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7894 0 : state.dataWeather->EPWDaylightSaving = false;
7895 : }
7896 1 : state.dataWeather->DST = state.dataWeather->EPWDST;
7897 : }
7898 :
7899 815 : } else if (i == 4) {
7900 793 : int NumEPWHolidays = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7901 1586 : state.dataWeather->NumSpecialDays =
7902 793 : NumEPWHolidays + state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriodControl:SpecialDays");
7903 793 : state.dataWeather->SpecialDays.allocate(state.dataWeather->NumSpecialDays);
7904 793 : NumHdArgs = 4 + NumEPWHolidays * 2;
7905 :
7906 22 : } else if ((i >= 5)) {
7907 22 : if (mod(i, 2) != 0) {
7908 11 : ++CurCount;
7909 11 : if (CurCount > state.dataWeather->NumSpecialDays) {
7910 0 : ShowSevereError(state, "Too many SpecialDays");
7911 0 : ErrorsFound = true;
7912 : } else {
7913 11 : state.dataWeather->SpecialDays(CurCount).Name = Line.substr(0, Pos);
7914 : }
7915 : // Process name
7916 : } else {
7917 11 : if (CurCount <= state.dataWeather->NumSpecialDays) {
7918 11 : auto &specialDay = state.dataWeather->SpecialDays(CurCount);
7919 : // Process date
7920 11 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7921 11 : if (dateType == DateType::MonthDay) {
7922 6 : specialDay.dateType = dateType;
7923 6 : specialDay.Month = PMonth;
7924 6 : specialDay.Day = PDay;
7925 6 : specialDay.WeekDay = 0;
7926 6 : specialDay.CompDate = PMonth * 32 + PDay;
7927 6 : specialDay.Duration = 1;
7928 6 : specialDay.DayType = 1;
7929 6 : specialDay.WthrFile = true;
7930 5 : } else if (dateType != DateType::Invalid) {
7931 5 : specialDay.dateType = dateType;
7932 5 : specialDay.Month = PMonth;
7933 5 : specialDay.Day = PDay;
7934 5 : specialDay.WeekDay = PWeekDay;
7935 5 : specialDay.CompDate = 0;
7936 5 : specialDay.Duration = 1;
7937 5 : specialDay.DayType = 1;
7938 5 : specialDay.WthrFile = true;
7939 : } else {
7940 0 : ShowSevereError(state, format("Invalid SpecialDay Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7941 0 : ErrorsFound = true;
7942 : }
7943 : }
7944 : }
7945 : }
7946 3194 : Line.erase(0, Pos + 1);
7947 : }
7948 5422 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7949 : // General::OrdinalDay (Month,Day,LeapYearValue)
7950 4629 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7951 4629 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, state.dataWeather->LeapYearAdd);
7952 4629 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, state.dataWeather->LeapYearAdd);
7953 4629 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7954 4622 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7955 : } else {
7956 7 : typicalExtPer.TotalDays =
7957 7 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7958 : }
7959 : }
7960 793 : } break;
7961 2379 : case Weather::EpwHeaderType::Comments1:
7962 : case Weather::EpwHeaderType::Comments2:
7963 : case Weather::EpwHeaderType::DesignConditions: {
7964 : // no action
7965 2379 : } break;
7966 793 : case Weather::EpwHeaderType::DataPeriods: {
7967 : // N1, \field Number of Data Periods
7968 : // N2, \field Number of Records per hour
7969 : // A1, \field Data Period 1 Name/Description
7970 : // A2, \field Data Period 1 Start Day of Week
7971 : // \type choice
7972 : // \key Sunday
7973 : // \key Monday
7974 : // \key Tuesday
7975 : // \key Wednesday
7976 : // \key Thursday
7977 : // \key Friday
7978 : // \key Saturday
7979 : // A3, \field Data Period 1 Start Day
7980 : // A4, \field Data Period 1 End Day
7981 793 : uppercase(Line);
7982 793 : NumHdArgs = 2;
7983 793 : int CurCount = 0;
7984 5551 : for (int i = 1; i <= NumHdArgs; ++i) {
7985 4758 : strip(Line);
7986 4758 : Pos = index(Line, ',');
7987 4758 : if (Pos == std::string::npos) {
7988 793 : if (len(Line) == 0) {
7989 0 : while (Pos == std::string::npos) {
7990 0 : Line = state.files.inputWeatherFile.readLine().data;
7991 0 : strip(Line);
7992 0 : uppercase(Line);
7993 0 : Pos = index(Line, ',');
7994 : }
7995 : } else {
7996 793 : Pos = len(Line);
7997 : }
7998 : }
7999 :
8000 : bool IOStatus;
8001 4758 : if (i == 1) {
8002 793 : state.dataWeather->NumDataPeriods = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
8003 793 : state.dataWeather->DataPeriods.allocate(state.dataWeather->NumDataPeriods);
8004 793 : NumHdArgs += 4 * state.dataWeather->NumDataPeriods;
8005 793 : if (state.dataWeather->NumDataPeriods > 0) {
8006 1586 : for (auto &e : state.dataWeather->DataPeriods) {
8007 793 : e.NumDays = 0;
8008 793 : }
8009 : }
8010 :
8011 3965 : } else if (i == 2) {
8012 793 : state.dataWeather->NumIntervalsPerHour = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
8013 3172 : } else if (i >= 3) {
8014 3172 : int const CurOne = mod(i - 3, 4);
8015 : int PMonth;
8016 : int PDay;
8017 : int PWeekDay;
8018 : int PYear;
8019 3172 : if (CurOne == 0) {
8020 : // Description of Data Period
8021 793 : ++CurCount;
8022 793 : if (CurCount > state.dataWeather->NumDataPeriods) {
8023 0 : ShowSevereError(state, "Too many data periods");
8024 0 : ErrorsFound = true;
8025 : } else {
8026 793 : state.dataWeather->DataPeriods(CurCount).Name = Line.substr(0, Pos);
8027 : }
8028 :
8029 2379 : } else if (CurOne == 1) {
8030 : // Start Day of Week
8031 793 : if (CurCount <= state.dataWeather->NumDataPeriods) {
8032 793 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
8033 793 : dataPeriod.DayOfWeek = Line.substr(0, Pos);
8034 793 : dataPeriod.WeekDay = getEnumValue(Sched::dayTypeNamesUC, dataPeriod.DayOfWeek);
8035 793 : if (dataPeriod.WeekDay < 1 || dataPeriod.WeekDay > 7) {
8036 0 : ShowSevereError(state,
8037 0 : fmt::format("Weather File -- Invalid Start Day of Week for Data Period #{}, Invalid day={}",
8038 : CurCount,
8039 0 : dataPeriod.DayOfWeek));
8040 0 : ErrorsFound = true;
8041 : }
8042 : }
8043 :
8044 1586 : } else if (CurOne == 2) {
8045 : // DataPeriod Start Day
8046 793 : if (CurCount <= state.dataWeather->NumDataPeriods) {
8047 793 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
8048 793 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
8049 793 : if (dateType == DateType::MonthDay) {
8050 793 : dataPeriod.StMon = PMonth;
8051 793 : dataPeriod.StDay = PDay;
8052 793 : dataPeriod.StYear = PYear;
8053 793 : if (PYear != 0) {
8054 0 : dataPeriod.HasYearData = true;
8055 : }
8056 : } else {
8057 0 : ShowSevereError(state,
8058 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day> (WeatherFile), found={}",
8059 0 : Line.substr(0, Pos)));
8060 0 : ErrorsFound = true;
8061 : }
8062 : }
8063 :
8064 793 : } else if (CurOne == 3) {
8065 793 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
8066 793 : if (CurCount <= state.dataWeather->NumDataPeriods) {
8067 793 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
8068 793 : if (dateType == DateType::MonthDay) {
8069 793 : dataPeriod.EnMon = PMonth;
8070 793 : dataPeriod.EnDay = PDay;
8071 793 : dataPeriod.EnYear = PYear;
8072 793 : if (PYear == 0 && dataPeriod.HasYearData) {
8073 0 : ShowWarningError(state, "Data Period (WeatherFile) - Start Date contains year. End Date does not.");
8074 0 : ShowContinueError(state, "...Assuming same year as Start Date for this data.");
8075 0 : dataPeriod.EnYear = dataPeriod.StYear;
8076 : }
8077 : } else {
8078 0 : ShowSevereError(state,
8079 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day>, (WeatherFile) found={}",
8080 0 : Line.substr(0, Pos)));
8081 0 : ErrorsFound = true;
8082 : }
8083 : }
8084 793 : if (dataPeriod.StYear == 0 || dataPeriod.EnYear == 0) {
8085 793 : dataPeriod.DataStJDay = General::OrdinalDay(dataPeriod.StMon, dataPeriod.StDay, state.dataWeather->LeapYearAdd);
8086 793 : dataPeriod.DataEnJDay = General::OrdinalDay(dataPeriod.EnMon, dataPeriod.EnDay, state.dataWeather->LeapYearAdd);
8087 793 : if (dataPeriod.DataStJDay <= dataPeriod.DataEnJDay) {
8088 793 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
8089 : } else {
8090 0 : dataPeriod.NumDays = (365 - dataPeriod.DataStJDay + 1) + (dataPeriod.DataEnJDay - 1 + 1);
8091 : }
8092 : } else { // weather file has actual year(s)
8093 0 : dataPeriod.DataStJDay = computeJulianDate(dataPeriod.StYear, dataPeriod.StMon, dataPeriod.StDay);
8094 0 : dataPeriod.DataEnJDay = computeJulianDate(dataPeriod.EnYear, dataPeriod.EnMon, dataPeriod.EnDay);
8095 0 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
8096 : }
8097 : // Have processed the last item for this, can set up Weekdays for months
8098 793 : dataPeriod.MonWeekDay = 0;
8099 793 : if (!ErrorsFound) {
8100 2379 : SetupWeekDaysByMonth(state,
8101 793 : state.dataWeather->DataPeriods(CurCount).StMon,
8102 793 : state.dataWeather->DataPeriods(CurCount).StDay,
8103 793 : state.dataWeather->DataPeriods(CurCount).WeekDay,
8104 793 : state.dataWeather->DataPeriods(CurCount).MonWeekDay);
8105 : }
8106 : }
8107 : }
8108 4758 : Line.erase(0, Pos + 1);
8109 : }
8110 793 : } break;
8111 0 : default: {
8112 : // Invalid header type
8113 0 : assert(false);
8114 : } break;
8115 : }
8116 6344 : }
8117 :
8118 5009 : void SkipEPlusWFHeader(EnergyPlusData &state)
8119 : {
8120 :
8121 : // SUBROUTINE INFORMATION:
8122 : // AUTHOR Linda K. Lawrie
8123 : // DATE WRITTEN August 2000
8124 :
8125 : // PURPOSE OF THIS SUBROUTINE:
8126 : // This subroutine skips the initial header records on the EnergyPlus Weather File (in.epw).
8127 :
8128 : static constexpr std::string_view Header("DATA PERIODS");
8129 :
8130 : // Read in Header Information
8131 10018 : InputFile::ReadResult<std::string> Line{"", true, false};
8132 :
8133 : // Headers should come in order
8134 : while (true) {
8135 40072 : Line = state.files.inputWeatherFile.readLine();
8136 40072 : if (Line.eof) {
8137 0 : ShowFatalError(state,
8138 0 : format("Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}", Header),
8139 0 : OptionalOutputFileRef{state.files.eso});
8140 : }
8141 40072 : uppercase(Line.data);
8142 40072 : if (has(Line.data, Header)) {
8143 5009 : break;
8144 : }
8145 : }
8146 :
8147 : // Dummy process Data Periods line
8148 : // 'DATA PERIODS'
8149 : // N1, \field Number of Data Periods
8150 : // N2, \field Number of Records per hour
8151 : // A1, \field Data Period 1 Name/Description
8152 : // A2, \field Data Period 1 Start Day of Week
8153 : // \type choice
8154 : // \key Sunday
8155 : // \key Monday
8156 : // \key Tuesday
8157 : // \key Wednesday
8158 : // \key Thursday
8159 : // \key Friday
8160 : // \key Saturday
8161 : // A3, \field Data Period 1 Start Day
8162 : // A4, \field Data Period 1 End Day
8163 :
8164 5009 : int NumHdArgs = 2;
8165 5009 : int CurCount = 0;
8166 15027 : for (int i = 1; i <= NumHdArgs; ++i) {
8167 10018 : strip(Line.data);
8168 10018 : std::string::size_type Pos = index(Line.data, ',');
8169 10018 : if (Pos == std::string::npos) {
8170 0 : if (len(Line.data) == 0) {
8171 0 : while (Pos == std::string::npos) {
8172 0 : Line = state.files.inputWeatherFile.readLine();
8173 0 : strip(Line.data);
8174 0 : uppercase(Line.data);
8175 0 : Pos = index(Line.data, ',');
8176 : }
8177 : } else {
8178 0 : Pos = len(Line.data);
8179 : }
8180 : }
8181 :
8182 10018 : if (i == 1) {
8183 : bool IOStatus;
8184 5009 : int const NumPeriods = Util::ProcessNumber(Line.data.substr(0, Pos), IOStatus);
8185 5009 : NumHdArgs += 4 * NumPeriods;
8186 5009 : } else if ((i >= 3)) {
8187 0 : if (mod(i - 3, 4) == 0) {
8188 0 : ++CurCount;
8189 : }
8190 : }
8191 10018 : Line.data.erase(0, Pos + 1);
8192 : }
8193 5009 : }
8194 :
8195 1233 : void ReportMissing_RangeData(EnergyPlusData &state)
8196 : {
8197 :
8198 : // SUBROUTINE INFORMATION:
8199 : // AUTHOR Linda Lawrie
8200 : // DATE WRITTEN January 2002
8201 :
8202 : // PURPOSE OF THIS SUBROUTINE:
8203 : // This subroutine reports the counts of missing/out of range data
8204 : // for weather file environments.
8205 :
8206 : static constexpr std::string_view MissString("Missing Data Found on Weather Data File");
8207 : static constexpr std::string_view msFmt("Missing {}, Number of items={:5}");
8208 : static constexpr std::string_view InvString("Invalid Data Found on Weather Data File");
8209 : static constexpr std::string_view ivFmt("Invalid {}, Number of items={:5}");
8210 : static constexpr std::string_view RangeString("Out of Range Data Found on Weather Data File");
8211 : static constexpr std::string_view rgFmt("Out of Range {} [{},{}], Number of items={:5}");
8212 :
8213 1233 : if (!state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
8214 1233 : return;
8215 : }
8216 :
8217 0 : bool MissedHeader = false;
8218 0 : auto missedHeaderCheck = [&](Real64 const value, std::string const &description) {
8219 0 : if (value > 0) {
8220 0 : if (!MissedHeader) {
8221 0 : ShowWarningError(state, std::string{MissString});
8222 0 : MissedHeader = true;
8223 : }
8224 0 : ShowMessage(state, format(msFmt, "\"" + description + "\"", value));
8225 : }
8226 0 : };
8227 :
8228 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDryBulbTemp, "Dry Bulb Temperature");
8229 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutBaroPress, "Atmospheric Pressure");
8230 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutRelHum, "Relative Humidity");
8231 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDewPointTemp, "Dew Point Temperatures");
8232 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindSpeed, "Wind Speed");
8233 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindDir, "Wind Direction");
8234 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.BeamSolarRad, "Direct Radiation");
8235 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.DifSolarRad, "Diffuse Radiation");
8236 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.TotalSkyCover, "Total Sky Cover");
8237 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OpaqueSkyCover, "Opaque Sky Cover");
8238 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.SnowDepth, "Snow Depth");
8239 0 : if (state.dataWeather->wvarsMissedCounts.WeathCodes > 0) {
8240 0 : ShowWarningError(state, std::string{InvString});
8241 0 : ShowMessage(state, format(ivFmt, "\"Weather Codes\" (not equal 9 digits)", state.dataWeather->wvarsMissedCounts.WeathCodes));
8242 : }
8243 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.LiquidPrecip, "Liquid Precipitation Depth");
8244 :
8245 0 : bool OutOfRangeHeader = false;
8246 : auto outOfRangeHeaderCheck = // (AUTO_OK_LAMBDA)
8247 0 : [&](Real64 const value, std::string_view description, std::string_view rangeLow, std::string_view rangeHigh, std::string_view extraMsg) {
8248 0 : if (value > 0) {
8249 0 : if (!OutOfRangeHeader) {
8250 0 : ShowWarningError(state, std::string{RangeString});
8251 0 : OutOfRangeHeader = true;
8252 : }
8253 0 : ShowMessage(state, EnergyPlus::format(rgFmt, description, rangeLow, rangeHigh, value));
8254 0 : if (!extraMsg.empty()) {
8255 0 : ShowMessage(state, std::string{extraMsg});
8256 : }
8257 : }
8258 0 : };
8259 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp, "Dry Bulb Temperatures", ">=-90", "<=70", "");
8260 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress,
8261 : "Atmospheric Pressure",
8262 : ">31000",
8263 : "<=120000",
8264 : "Out of Range values set to last good value");
8265 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutRelHum, "Relative Humidity", ">=0", "<=110", "");
8266 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp, "Dew Point Temperatures", ">=-90", "<=70", "");
8267 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindSpeed, "Wind Speed", ">=0", "<=40", "");
8268 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindDir, "Wind Direction", ">=0", "<=360", "");
8269 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad, "Direct Radiation", ">=0", "NoLimit", "");
8270 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad, "Diffuse Radiation", ">=0", "NoLimit", "");
8271 : }
8272 :
8273 801 : void SetupInterpolationValues(EnergyPlusData &state)
8274 : {
8275 :
8276 : // SUBROUTINE INFORMATION:
8277 : // AUTHOR Linda Lawrie
8278 : // DATE WRITTEN November 2002
8279 :
8280 : // PURPOSE OF THIS SUBROUTINE:
8281 : // This subroutine creates the "interpolation" values / weights that are used for
8282 : // interpolating weather data from hourly down to the time step level.
8283 :
8284 : // METHODOLOGY EMPLOYED:
8285 : // Create arrays (InterpolationValues, SolarInterpolationValues) dependent on
8286 : // Number of Time Steps in Hour. This will be used in the "SetCurrentWeather" procedure.
8287 :
8288 801 : state.dataWeather->Interpolation.allocate(state.dataGlobal->TimeStepsInHour);
8289 801 : state.dataWeather->SolarInterpolation.allocate(state.dataGlobal->TimeStepsInHour);
8290 801 : state.dataWeather->Interpolation = 0.0;
8291 801 : state.dataWeather->SolarInterpolation = 0.0;
8292 :
8293 5390 : for (int tloop = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop) {
8294 4589 : state.dataWeather->Interpolation(tloop) =
8295 4589 : (state.dataGlobal->TimeStepsInHour == 1) ? 1.0 : min(1.0, (double(tloop) / double(state.dataGlobal->TimeStepsInHour)));
8296 : }
8297 :
8298 801 : if (mod(state.dataGlobal->TimeStepsInHour, 2) == 0) {
8299 : // even number of time steps.
8300 786 : int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
8301 :
8302 786 : state.dataWeather->SolarInterpolation(halfpoint) = 1.0;
8303 786 : Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
8304 3073 : for (int tloop = halfpoint + 1, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
8305 2287 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8306 : }
8307 2287 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8308 1501 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8309 : }
8310 : } else { // odd number of time steps
8311 15 : if (state.dataGlobal->TimeStepsInHour == 1) {
8312 15 : state.dataWeather->SolarInterpolation(1) = 0.5;
8313 0 : } else if (state.dataGlobal->TimeStepsInHour == 3) {
8314 0 : state.dataWeather->SolarInterpolation(1) = 5.0 / 6.0;
8315 0 : state.dataWeather->SolarInterpolation(2) = 5.0 / 6.0;
8316 0 : state.dataWeather->SolarInterpolation(3) = 0.5;
8317 : } else {
8318 0 : Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
8319 0 : int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
8320 0 : Real64 tweight1 = 1.0 - tweight / 2.0;
8321 0 : state.dataWeather->SolarInterpolation(halfpoint) = tweight1;
8322 0 : state.dataWeather->SolarInterpolation(halfpoint + 1) = tweight1;
8323 0 : for (int tloop = halfpoint + 2, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
8324 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8325 : }
8326 0 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8327 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8328 : }
8329 : }
8330 : }
8331 801 : }
8332 :
8333 802 : void SetupEnvironmentTypes(EnergyPlusData &state)
8334 : {
8335 :
8336 : // SUBROUTINE INFORMATION:
8337 : // AUTHOR Linda Lawrie
8338 : // DATE WRITTEN October 2010
8339 :
8340 : // PURPOSE OF THIS SUBROUTINE:
8341 : // Make sure Environment derived type is set prior to getting
8342 : // Weather Properties
8343 :
8344 : // Transfer weather file information to the Environment derived type
8345 802 : state.dataWeather->Envrn = state.dataEnvrn->TotDesDays + 1;
8346 :
8347 : // Sizing Periods from Weather File
8348 807 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunDesPers; ++iRunPer, ++state.dataWeather->Envrn) {
8349 5 : auto const &runPer = state.dataWeather->RunPeriodDesignInput(iRunPer);
8350 5 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8351 :
8352 5 : envCurr.StartMonth = runPer.startMonth;
8353 5 : envCurr.StartDay = runPer.startDay;
8354 5 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, state.dataWeather->LeapYearAdd);
8355 5 : envCurr.TotalDays = runPer.totalDays;
8356 5 : envCurr.EndMonth = runPer.endMonth;
8357 5 : envCurr.EndDay = runPer.endDay;
8358 5 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, state.dataWeather->LeapYearAdd);
8359 5 : envCurr.NumSimYears = runPer.numSimYears;
8360 5 : if (envCurr.StartJDay <= envCurr.EndJDay) {
8361 5 : envCurr.TotalDays = (envCurr.EndJDay - envCurr.StartJDay + 1) * envCurr.NumSimYears;
8362 : } else {
8363 0 : envCurr.TotalDays =
8364 0 : (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - envCurr.StartJDay + 1 + envCurr.EndJDay) * envCurr.NumSimYears;
8365 : }
8366 5 : state.dataEnvrn->TotRunDesPersDays += envCurr.TotalDays;
8367 5 : envCurr.UseDST = runPer.useDST;
8368 5 : envCurr.UseHolidays = runPer.useHolidays;
8369 5 : envCurr.Title = runPer.title;
8370 5 : envCurr.cKindOfEnvrn = runPer.periodType;
8371 5 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
8372 5 : envCurr.DesignDayNum = 0;
8373 5 : envCurr.RunPeriodDesignNum = iRunPer;
8374 5 : envCurr.DayOfWeek = runPer.dayOfWeek;
8375 5 : envCurr.MonWeekDay = runPer.monWeekDay;
8376 5 : envCurr.SetWeekDays = false;
8377 5 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8378 5 : envCurr.UseRain = runPer.useRain;
8379 5 : envCurr.UseSnow = runPer.useSnow;
8380 5 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // this will just the default
8381 : }
8382 :
8383 : // RunPeriods from weather file
8384 1995 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunPers; ++iRunPer, ++state.dataWeather->Envrn) {
8385 1193 : auto const &runPer = state.dataWeather->RunPeriodInput(iRunPer);
8386 1193 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8387 :
8388 1193 : envCurr.StartMonth = runPer.startMonth;
8389 1193 : envCurr.StartDay = runPer.startDay;
8390 1193 : envCurr.StartYear = runPer.startYear;
8391 1193 : envCurr.EndMonth = runPer.endMonth;
8392 1193 : envCurr.EndDay = runPer.endDay;
8393 1193 : envCurr.EndYear = runPer.endYear;
8394 1193 : envCurr.NumSimYears = runPer.numSimYears;
8395 1193 : envCurr.CurrentYear = runPer.startYear;
8396 1193 : envCurr.IsLeapYear = runPer.isLeapYear;
8397 1193 : envCurr.TreatYearsAsConsecutive = true;
8398 1193 : if (runPer.actualWeather) {
8399 : // This will require leap years to be present, thus Julian days can be used for all the calculations
8400 0 : envCurr.StartJDay = envCurr.StartDate = runPer.startJulianDate;
8401 0 : envCurr.EndJDay = envCurr.EndDate = runPer.endJulianDate;
8402 0 : envCurr.TotalDays = envCurr.EndDate - envCurr.StartDate + 1;
8403 0 : envCurr.RawSimDays = envCurr.EndDate - envCurr.StartDate + 1;
8404 0 : envCurr.MatchYear = true;
8405 0 : envCurr.ActualWeather = true;
8406 : } else { // std RunPeriod
8407 1193 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8408 1193 : if (envCurr.StartYear == envCurr.EndYear) {
8409 : // Short-circuit all the calculations, we're in a single year
8410 :
8411 1187 : envCurr.IsLeapYear = isLeapYear(envCurr.StartYear) && state.dataWeather->WFAllowsLeapYears;
8412 1187 : int LocalLeapYearAdd = (int)envCurr.IsLeapYear;
8413 :
8414 1187 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, LocalLeapYearAdd);
8415 1187 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, LocalLeapYearAdd);
8416 1187 : envCurr.RawSimDays = (envCurr.EndJDay - envCurr.StartJDay + 1);
8417 1187 : envCurr.TotalDays = envCurr.RawSimDays;
8418 : } else {
8419 : // Environment crosses year boundaries
8420 6 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8421 6 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, (int)runPer.isLeapYear);
8422 12 : envCurr.EndJDay = General::OrdinalDay(
8423 6 : runPer.endMonth, runPer.endDay, (int)(isLeapYear(runPer.endYear) && state.dataWeather->WFAllowsLeapYears));
8424 6 : envCurr.TotalDays = 366 - envCurr.StartJDay + envCurr.EndJDay + 365 * std::max(envCurr.NumSimYears - 2, 0);
8425 6 : if (state.dataWeather->WFAllowsLeapYears) {
8426 : // First year
8427 0 : if (envCurr.StartJDay < 59) {
8428 0 : if (isLeapYear(envCurr.StartYear)) {
8429 0 : ++envCurr.TotalDays;
8430 : }
8431 : }
8432 : // Middle years
8433 0 : for (int yr = envCurr.StartYear + 1; yr < envCurr.EndYear; ++yr) {
8434 0 : if (isLeapYear(yr)) {
8435 0 : ++envCurr.TotalDays;
8436 : }
8437 : }
8438 : // Last year not needed, the end ordinal date will take this into account
8439 : }
8440 6 : envCurr.RawSimDays = envCurr.TotalDays;
8441 : }
8442 : }
8443 1193 : envCurr.UseDST = runPer.useDST;
8444 1193 : envCurr.UseHolidays = runPer.useHolidays;
8445 1193 : if (runPer.title.empty()) {
8446 1 : envCurr.Title = state.dataEnvrn->WeatherFileLocationTitle;
8447 : } else {
8448 1192 : envCurr.Title = runPer.title;
8449 : }
8450 1193 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::ReadAllWeatherData) {
8451 1 : envCurr.cKindOfEnvrn = "ReadAllWeatherDataRunPeriod";
8452 : } else {
8453 1192 : envCurr.cKindOfEnvrn = "WeatherFileRunPeriod";
8454 1192 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
8455 : }
8456 1193 : envCurr.DayOfWeek = runPer.dayOfWeek;
8457 1193 : envCurr.MonWeekDay = runPer.monWeekDay;
8458 1193 : envCurr.SetWeekDays = false;
8459 1193 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8460 1193 : envCurr.UseRain = runPer.useRain;
8461 1193 : envCurr.UseSnow = runPer.useSnow;
8462 1193 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // first hour interpolation choice
8463 : } // for (i)
8464 802 : }
8465 :
8466 2384 : bool isLeapYear(int const Year)
8467 : {
8468 : // true if it's a leap year, false if not.
8469 :
8470 2384 : if (mod(Year, 4) == 0) { // Potential Leap Year
8471 2 : if (!(mod(Year, 100) == 0 && mod(Year, 400) != 0)) {
8472 2 : return true;
8473 : }
8474 : }
8475 2382 : return false;
8476 : }
8477 :
8478 9317 : int computeJulianDate(int const gyyyy, // input/output gregorian year, should be specified as 4 digits
8479 : int const gmm, // input/output gregorian month
8480 : int const gdd // input/output gregorian day
8481 : )
8482 : {
8483 : // SUBROUTINE INFORMATION:
8484 : // AUTHOR Jason DeGraw
8485 : // DATE WRITTEN 10/25/2017
8486 :
8487 : // PURPOSE OF THIS SUBROUTINE:
8488 : // Split the former JGDate function in two. Convert a gregorian
8489 : // date to actual julian date. the advantage of storing a julian date
8490 : // in the jdate format rather than a 5 digit format is that any
8491 : // number of days can be add or subtracted to jdate and
8492 : // that result is a proper julian date.
8493 :
8494 : // REFERENCES:
8495 : // for discussion of this algorithm,
8496 : // see cacm, vol 11, no 10, oct 1968, page 657
8497 :
8498 9317 : int tyyyy = gyyyy;
8499 9317 : int tmm = gmm;
8500 9317 : int tdd = gdd;
8501 9317 : int l = (tmm - 14) / 12;
8502 9317 : return tdd - 32075 + 1461 * (tyyyy + 4800 + l) / 4 + 367 * (tmm - 2 - l * 12) / 12 - 3 * ((tyyyy + 4900 + l) / 100) / 4;
8503 : }
8504 :
8505 0 : int computeJulianDate(GregorianDate const &gdate)
8506 : {
8507 0 : return computeJulianDate(gdate.year, gdate.month, gdate.day);
8508 : }
8509 :
8510 0 : GregorianDate computeGregorianDate(int const jdate)
8511 : {
8512 0 : int tdate = jdate;
8513 0 : int l = tdate + 68569;
8514 0 : int n = 4 * l / 146097;
8515 0 : l -= (146097 * n + 3) / 4;
8516 0 : int tyyyy = 4000 * (l + 1) / 1461001;
8517 0 : l = l - 1461 * tyyyy / 4 + 31;
8518 0 : int tmm = 80 * l / 2447;
8519 0 : int tdd = l - 2447 * tmm / 80;
8520 0 : l = tmm / 11;
8521 0 : tmm += 2 - 12 * l;
8522 0 : tyyyy += 100 * (n - 49) + l;
8523 0 : return {tyyyy, tmm, tdd};
8524 : }
8525 :
8526 154 : Sched::DayType calculateDayOfWeek(EnergyPlusData &state, int const year, int const month, int const day)
8527 : {
8528 :
8529 : // FUNCTION INFORMATION:
8530 : // AUTHOR Linda Lawrie
8531 : // DATE WRITTEN March 2012
8532 : // MODIFIED October 2017, Jason DeGraw
8533 :
8534 : // PURPOSE OF THIS FUNCTION:
8535 : // Calculate the correct day of week.
8536 :
8537 : // METHODOLOGY EMPLOYED:
8538 : // Zeller's algorithm.
8539 :
8540 : // REFERENCES:
8541 : // http://en.wikipedia.org/wiki/Zeller%27s_congruence
8542 : // and other references around the web.
8543 :
8544 154 : int Gyyyy(year); // Gregorian yyyy
8545 154 : int Gmm(month); // Gregorian mm
8546 :
8547 : // Jan, Feb are 13, 14 months of previous year
8548 154 : if (Gmm < 3) {
8549 91 : Gmm += 12;
8550 91 : --Gyyyy;
8551 : }
8552 :
8553 154 : state.dataEnvrn->DayOfWeek = mod(day + (13 * (Gmm + 1) / 5) + Gyyyy + (Gyyyy / 4) + 6 * (Gyyyy / 100) + (Gyyyy / 400), 7);
8554 154 : if (state.dataEnvrn->DayOfWeek == 0) {
8555 43 : state.dataEnvrn->DayOfWeek = 7;
8556 : }
8557 :
8558 154 : return static_cast<Sched::DayType>(state.dataEnvrn->DayOfWeek);
8559 : }
8560 :
8561 1037 : int calculateDayOfYear(int const Month, int const Day, bool const leapYear)
8562 : {
8563 :
8564 : // FUNCTION INFORMATION:
8565 : // AUTHOR Jason DeGraw
8566 : // DATE WRITTEN October 10, 2017
8567 :
8568 : // PURPOSE OF THIS FUNCTION:
8569 : // Compute the day of the year for leap and non-leap years.
8570 :
8571 : static std::array<int, 12> const daysbefore{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}};
8572 : static std::array<int, 12> const daysbeforeleap{{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
8573 :
8574 : // Could probably do some bounds checking here, but for now assume the month is in [1, 12]
8575 1037 : if (leapYear) {
8576 0 : return daysbeforeleap[Month - 1] + Day;
8577 : } else {
8578 1037 : return daysbefore[Month - 1] + Day;
8579 : }
8580 : }
8581 :
8582 2382 : bool validMonthDay(int const month, int const day, int const leapYearAdd)
8583 : {
8584 :
8585 : // FUNCTION INFORMATION:
8586 : // AUTHOR Jason DeGraw
8587 : // DATE WRITTEN October 31, 2017
8588 :
8589 : // PURPOSE OF THIS FUNCTION:
8590 : // Determine if a month/day+leapyear combination is valid.
8591 :
8592 2382 : switch (month) {
8593 2222 : case 1:
8594 : case 3:
8595 : case 5:
8596 : case 7:
8597 : case 8:
8598 : case 10:
8599 : case 12:
8600 2222 : if (day > 31) {
8601 0 : return false;
8602 : }
8603 2222 : break;
8604 119 : case 4:
8605 : case 6:
8606 : case 9:
8607 : case 11:
8608 119 : if (day > 30) {
8609 0 : return false;
8610 : }
8611 119 : break;
8612 41 : case 2:
8613 41 : if (day > 28 + leapYearAdd) {
8614 0 : return false;
8615 : }
8616 41 : break;
8617 0 : default:
8618 0 : return false;
8619 : }
8620 2382 : return true;
8621 : }
8622 :
8623 0 : void AnnualMonthlyDryBulbWeatherData::CalcAnnualAndMonthlyDryBulbTemp(EnergyPlusData &state)
8624 : {
8625 :
8626 : // PURPOSE OF THIS SUBROUTINE:
8627 : // Calculates monthly daily average outdoor air drybulb temperature from
8628 : // either weather (*.EPW) file or reads monthly daily average outdoor air
8629 : // drybulb temperature from STAT (*.stat) for use to autosize main water
8630 : // temperature.
8631 :
8632 0 : Real64 MonthlyDailyDryBulbMin(200.0); // monthly-daily minimum outside air dry-bulb temperature
8633 0 : Real64 MonthlyDailyDryBulbMax(-200.0); // monthly-daily maximum outside air dry-bulb temperature
8634 0 : Real64 AnnualDailyAverageDryBulbTempSum(0.0); // annual sum of daily average outside air dry-bulb temperature
8635 0 : Array1D<Real64> MonthlyAverageDryBulbTemp(12, 0.0); // monthly-daily average outside air temperature
8636 :
8637 0 : if (!this->OADryBulbWeatherDataProcessed) {
8638 0 : const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
8639 0 : const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
8640 0 : if (statFileExists) {
8641 0 : auto statFile = state.files.inStatFilePath.try_open();
8642 0 : if (!statFile.good()) {
8643 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", statFile.filePath));
8644 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8645 0 : return;
8646 : }
8647 :
8648 0 : std::string lineAvg;
8649 0 : while (statFile.good()) {
8650 0 : auto lineIn = statFile.readLine();
8651 0 : if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
8652 0 : for (int i = 1; i <= 7; ++i) {
8653 0 : lineIn = statFile.readLine();
8654 : }
8655 0 : lineIn = statFile.readLine();
8656 0 : lineAvg = lineIn.data;
8657 0 : break;
8658 : }
8659 0 : }
8660 0 : if (lineAvg.empty()) {
8661 0 : ShowSevereError(
8662 : state,
8663 0 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have Monthly Statistics for Dry Bulb temperatures.",
8664 : statFile.filePath));
8665 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8666 0 : return;
8667 0 : } else if (lineAvg.find("Daily Avg") == std::string::npos) {
8668 0 : ShowSevereError(state,
8669 0 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have the 'Daily Avg' line in the Monthly "
8670 : "Statistics for Dry Bulb temperatures.",
8671 : statFile.filePath));
8672 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8673 0 : return;
8674 : } else {
8675 0 : int AnnualNumberOfDays = 0;
8676 0 : for (int i = 1; i <= 12; ++i) {
8677 0 : MonthlyAverageDryBulbTemp(i) = OutputReportTabular::StrToReal(OutputReportTabular::GetColumnUsingTabs(lineAvg, i + 2));
8678 0 : AnnualDailyAverageDryBulbTempSum += MonthlyAverageDryBulbTemp(i) * state.dataWeather->EndDayOfMonth(i);
8679 0 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyAverageDryBulbTemp(i));
8680 0 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyAverageDryBulbTemp(i));
8681 0 : AnnualNumberOfDays += state.dataWeather->EndDayOfMonth(i);
8682 : }
8683 0 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8684 0 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8685 0 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8686 0 : this->OADryBulbWeatherDataProcessed = true;
8687 : }
8688 0 : } else if (epwFileExists) {
8689 0 : auto epwFile = state.files.inputWeatherFilePath.try_open();
8690 0 : bool epwHasLeapYear(false);
8691 0 : if (!epwFile.good()) {
8692 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", epwFile.filePath));
8693 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8694 0 : return;
8695 : }
8696 0 : for (int i = 1; i <= 8; ++i) { // Headers
8697 0 : auto epwLine = epwFile.readLine();
8698 :
8699 0 : if (i == 5) {
8700 : // HOLIDAYS/DAYLIGHT SAVINGS,Yes,0,0,0
8701 0 : std::string::size_type pos = index(epwLine.data, ',');
8702 0 : epwLine.data.erase(0, pos + 1);
8703 0 : pos = index(epwLine.data, ',');
8704 0 : std::string LeapYear = Util::makeUPPER(epwLine.data.substr(0, pos));
8705 0 : if (LeapYear[0] == 'Y') {
8706 0 : epwHasLeapYear = true;
8707 : }
8708 0 : }
8709 0 : }
8710 0 : Array1D<int> EndDayOfMonthLocal;
8711 0 : EndDayOfMonthLocal = state.dataWeather->EndDayOfMonth;
8712 0 : if (epwHasLeapYear) {
8713 : // increase number of days for february by one day if weather data has leap year
8714 0 : EndDayOfMonthLocal(2) = EndDayOfMonthLocal(2) + 1;
8715 : }
8716 0 : for (int i = 1; i <= 12; ++i) {
8717 0 : Real64 MonthlyDailyDryBulbAvg = 0.0;
8718 0 : int DaysCountOfMonth = EndDayOfMonthLocal(i);
8719 0 : for (int DayNum = 1; DayNum <= DaysCountOfMonth; ++DayNum) {
8720 0 : Real64 DailyAverageDryBulbTemp = 0.0;
8721 : std::string::size_type pos;
8722 0 : for (int j = 1; j <= 24; ++j) {
8723 0 : auto epwLine = epwFile.readLine();
8724 0 : for (int ind = 1; ind <= 6; ++ind) {
8725 0 : pos = index(epwLine.data, ',');
8726 0 : epwLine.data.erase(0, pos + 1);
8727 : }
8728 0 : pos = index(epwLine.data, ',');
8729 0 : Real64 HourlyDryBulbTemp = OutputReportTabular::StrToReal(epwLine.data.substr(0, pos));
8730 0 : DailyAverageDryBulbTemp += (HourlyDryBulbTemp / 24.0);
8731 0 : }
8732 0 : AnnualDailyAverageDryBulbTempSum += DailyAverageDryBulbTemp;
8733 0 : MonthlyDailyDryBulbAvg += (DailyAverageDryBulbTemp / DaysCountOfMonth);
8734 : }
8735 0 : MonthlyAverageDryBulbTemp(i) = MonthlyDailyDryBulbAvg;
8736 0 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyDailyDryBulbAvg);
8737 0 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyDailyDryBulbAvg);
8738 : }
8739 : // calculate annual average outdoor air dry-bulb temperature and monthly daily average
8740 : // outdoor air temperature maximum difference
8741 0 : int AnnualNumberOfDays = 365;
8742 0 : if (epwHasLeapYear) {
8743 0 : AnnualNumberOfDays++;
8744 : }
8745 0 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8746 0 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8747 0 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8748 0 : this->OADryBulbWeatherDataProcessed = true;
8749 0 : } else {
8750 0 : ShowSevereError(state, "CalcAnnualAndMonthlyDryBulbTemp: weather file or stat file does not exist.");
8751 0 : ShowContinueError(state, format("Weather file: {}.", state.files.inputWeatherFilePath.filePath));
8752 0 : ShowContinueError(state, format("Stat file: {}.", state.files.inStatFilePath.filePath));
8753 0 : ShowContinueError(state, "Water Mains Monthly Temperature cannot be calculated using CorrelationFromWeatherFile method.");
8754 0 : ShowContinueError(state, "Instead a fixed default value of 10.0 C will be used.");
8755 : }
8756 : }
8757 0 : }
8758 :
8759 801 : void ReportWaterMainsTempParameters(EnergyPlusData &state)
8760 : {
8761 : // PURPOSE OF THIS SUBROUTINE:
8762 : // report site water mains temperature object user inputs and/or parameters calculated
8763 : // from weather or stat file
8764 :
8765 801 : if (!state.files.eio.good()) {
8766 0 : return;
8767 : }
8768 :
8769 801 : std::stringstream ss;
8770 801 : auto *eiostream = &ss;
8771 :
8772 : // Write annual average OA temperature and maximum difference in monthly-daily average outdoor air temperature
8773 : *eiostream << "! <Site Water Mains Temperature Information>"
8774 : ",Calculation Method{}"
8775 : ",Water Mains Temperature Schedule Name{}"
8776 : ",Annual Average Outdoor Air Temperature{C}"
8777 : ",Maximum Difference In Monthly Average Outdoor Air Temperatures{deltaC}"
8778 801 : ",Fixed Default Water Mains Temperature{C}\n";
8779 :
8780 801 : switch (state.dataWeather->WaterMainsTempsMethod) {
8781 5 : case WaterMainsTempCalcMethod::Schedule:
8782 5 : *eiostream << "Site Water Mains Temperature Information,";
8783 5 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8784 10 : << state.dataWeather->waterMainsTempSched->Name << ",";
8785 5 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8786 10 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8787 5 : *eiostream << "NA\n";
8788 5 : break;
8789 128 : case WaterMainsTempCalcMethod::Correlation:
8790 128 : *eiostream << "Site Water Mains Temperature Information,";
8791 128 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << "," << "NA" << ",";
8792 128 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8793 256 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8794 128 : *eiostream << "NA\n";
8795 128 : break;
8796 0 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
8797 0 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
8798 0 : *eiostream << "Site Water Mains Temperature Information,";
8799 0 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << "," << "NA" << ",";
8800 0 : *eiostream << format("{:.2R}", state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp) << ","
8801 0 : << format("{:.2R}", state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff) << "," << "NA\n";
8802 : } else {
8803 0 : *eiostream << "Site Water Mains Temperature Information,";
8804 0 : *eiostream << "FixedDefault" << "," << "NA" << "," << "NA" << "," << "NA" << "," << format("{:.1R}", 10.0) << '\n';
8805 : }
8806 0 : break;
8807 668 : default:
8808 668 : *eiostream << "Site Water Mains Temperature Information,";
8809 668 : *eiostream << "FixedDefault" << "," << "NA" << "," << "NA" << "," << "NA" << "," << format("{:.1R}", 10.0) << '\n';
8810 668 : break;
8811 : }
8812 :
8813 801 : print(state.files.eio, "{}", ss.str());
8814 801 : }
8815 :
8816 518592 : void calcSky(EnergyPlusData &state,
8817 : Real64 &HorizIRSky,
8818 : Real64 &SkyTemp,
8819 : Real64 OpaqueSkyCover,
8820 : Real64 DryBulb,
8821 : Real64 DewPoint,
8822 : Real64 RelHum,
8823 : Real64 IRHoriz)
8824 : {
8825 518592 : if (IRHoriz <= 0.0) {
8826 0 : IRHoriz = 9999.0;
8827 : }
8828 :
8829 518592 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8830 518592 : if (!envCurr.UseWeatherFileHorizontalIR || IRHoriz >= 9999.0) {
8831 : // Missing or user defined to not use IRHoriz from weather, using sky cover and clear sky emissivity
8832 0 : Real64 ESky = CalcSkyEmissivity(state, envCurr.skyTempModel, OpaqueSkyCover, DryBulb, DewPoint, RelHum);
8833 0 : HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
8834 0 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8835 0 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8836 0 : SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
8837 : } else {
8838 0 : SkyTemp = 0.0; // dealt with later
8839 : }
8840 0 : } else {
8841 : // Valid IR from weather files
8842 518592 : HorizIRSky = IRHoriz;
8843 518592 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8844 518592 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8845 518592 : SkyTemp = root_4(IRHoriz / Constant::StefanBoltzmann) - Constant::Kelvin;
8846 : } else {
8847 0 : SkyTemp = 0.0; // dealt with later
8848 : }
8849 : }
8850 518592 : }
8851 :
8852 : } // namespace Weather
8853 :
8854 : } // namespace EnergyPlus
|