Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <array>
50 : #include <cmath>
51 : #include <cstdio>
52 : #include <map>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array.functions.hh>
56 : #include <ObjexxFCL/ArrayS.functions.hh>
57 : #include <ObjexxFCL/string.functions.hh>
58 : #include <ObjexxFCL/time.hh>
59 :
60 : // EnergyPlus Headers
61 : #include <EnergyPlus/Data/EnergyPlusData.hh>
62 : #include <EnergyPlus/DataEnvironment.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataPrecisionGlobals.hh>
66 : #include <EnergyPlus/DataReportingFlags.hh>
67 : #include <EnergyPlus/DataSurfaces.hh>
68 : #include <EnergyPlus/DataSystemVariables.hh>
69 : #include <EnergyPlus/DataWater.hh>
70 : #include <EnergyPlus/DisplayRoutines.hh>
71 : #include <EnergyPlus/EMSManager.hh>
72 : #include <EnergyPlus/FileSystem.hh>
73 : #include <EnergyPlus/General.hh>
74 : #include <EnergyPlus/GlobalNames.hh>
75 : #include <EnergyPlus/GroundTemperatureModeling/BaseGroundTemperatureModel.hh>
76 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
77 : #include <EnergyPlus/OutputProcessor.hh>
78 : #include <EnergyPlus/OutputReportPredefined.hh>
79 : #include <EnergyPlus/OutputReportTabular.hh>
80 : #include <EnergyPlus/Psychrometrics.hh>
81 : #include <EnergyPlus/ScheduleManager.hh>
82 : #include <EnergyPlus/StringUtilities.hh>
83 : #include <EnergyPlus/SurfaceGeometry.hh>
84 : #include <EnergyPlus/ThermalComfort.hh>
85 : #include <EnergyPlus/UtilityRoutines.hh>
86 : #include <EnergyPlus/Vectors.hh>
87 : #include <EnergyPlus/WaterManager.hh>
88 : #include <EnergyPlus/WeatherManager.hh>
89 :
90 : namespace EnergyPlus {
91 :
92 : namespace Weather {
93 :
94 : // MODULE INFORMATION:
95 : // AUTHOR Rick Strand
96 : // DATE WRITTEN May 1997
97 : // MODIFIED December 1998, FW; December 1999, LKL.
98 :
99 : // PURPOSE OF THIS MODULE:
100 : // This module contains all of the weather handling routines for
101 : // EnergyPlus. That includes getting user input, defining design day
102 : // weather, retrieving data from weather files, and supplying the
103 : // outdoor environment for each time step.
104 :
105 : constexpr std::array<std::string_view, (int)EpwHeaderType::Num> epwHeaders = {"LOCATION",
106 : "DESIGN CONDITIONS",
107 : "TYPICAL/EXTREME PERIODS",
108 : "GROUND TEMPERATURES",
109 : "HOLIDAYS/DAYLIGHT SAVING",
110 : "COMMENTS 1",
111 : "COMMENTS 2",
112 : "DATA PERIODS"};
113 :
114 : static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNames{
115 : "Schedule", "Correlation", "CorrelationFromWeatherFile", "FixedDefault"};
116 :
117 : static constexpr std::array<std::string_view, (int)WaterMainsTempCalcMethod::Num> waterMainsCalcMethodNamesUC{
118 : "SCHEDULE", "CORRELATION", "CORRELATIONFROMWEATHERFILE", "FIXEDDEFAULT"};
119 :
120 : static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNamesUC{
121 : "CLARKALLEN", "SCHEDULEVALUE", "DIFFERENCESCHEDULEDRYBULBVALUE", "DIFFERENCESCHEDULEDEWPOINTVALUE", "BRUNT", "IDSO", "BERDAHLMARTIN"};
122 :
123 : static constexpr std::array<std::string_view, (int)SkyTempModel::Num> SkyTempModelNames{"Clark and Allen",
124 : "Schedule Value",
125 : "DryBulb Difference Schedule Value",
126 : "Dewpoint Difference Schedule Value",
127 : "Brunt",
128 : "Idso",
129 : "Berdahl and Martin"};
130 :
131 326115 : void ManageWeather(EnergyPlusData &state)
132 : {
133 :
134 : // SUBROUTINE INFORMATION:
135 : // AUTHOR Rick Strand
136 : // DATE WRITTEN May 1997
137 : // MODIFIED June 1997 (general clean-up)
138 :
139 : // PURPOSE OF THIS SUBROUTINE:
140 : // This subroutine is the main driver of the weather manager module.
141 : // It controls the assignment of weather related global variables as
142 : // well as the reads and writes for weather information.
143 :
144 326115 : InitializeWeather(state, state.dataWeather->PrintEnvrnStamp);
145 :
146 : // Cannot call this during sizing, because EMS will not initialize properly until after simulation kickoff
147 326115 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
148 255311 : bool anyEMSRan = false;
149 255311 : EMSManager::ManageEMS(state,
150 : EMSManager::EMSCallFrom::BeginZoneTimestepBeforeSetCurrentWeather,
151 : anyEMSRan,
152 510622 : ObjexxFCL::Optional_int_const()); // calling point
153 : }
154 326115 : SetCurrentWeather(state);
155 :
156 326115 : ReportWeatherAndTimeInformation(state, state.dataWeather->PrintEnvrnStamp);
157 326115 : }
158 :
159 292 : void ResetEnvironmentCounter(EnergyPlusData &state)
160 : {
161 292 : state.dataWeather->Envrn = 0;
162 292 : }
163 :
164 67 : bool CheckIfAnyUnderwaterBoundaries(EnergyPlusData &state)
165 : {
166 67 : bool errorsFound = false;
167 67 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
168 :
169 67 : constexpr std::string_view routineName = "CheckIfAnyUnderwaterBoundaries";
170 :
171 67 : auto const &ipsc = state.dataIPShortCut;
172 67 : ipsc->cCurrentModuleObject = "SurfaceProperty:Underwater";
173 67 : int Num = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
174 69 : for (int i = 1; i <= Num; i++) {
175 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
176 2 : ipsc->cCurrentModuleObject,
177 : i,
178 2 : ipsc->cAlphaArgs,
179 : NumAlpha,
180 2 : ipsc->rNumericArgs,
181 : NumNumber,
182 : IOStat,
183 2 : ipsc->lNumericFieldBlanks,
184 2 : ipsc->lAlphaFieldBlanks,
185 2 : ipsc->cAlphaFieldNames,
186 2 : ipsc->cNumericFieldNames);
187 2 : state.dataWeather->underwaterBoundaries.emplace_back();
188 2 : auto &underwaterBoundary = state.dataWeather->underwaterBoundaries[i - 1];
189 2 : underwaterBoundary.Name = ipsc->cAlphaArgs(1);
190 :
191 2 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, underwaterBoundary.Name};
192 :
193 2 : underwaterBoundary.distanceFromLeadingEdge = ipsc->rNumericArgs(1);
194 2 : underwaterBoundary.OSCMIndex = Util::FindItemInList(underwaterBoundary.Name, state.dataSurface->OSCM);
195 2 : if (underwaterBoundary.OSCMIndex <= 0) {
196 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
197 0 : errorsFound = true;
198 : }
199 :
200 2 : if (ipsc->lAlphaFieldBlanks(2)) {
201 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
202 0 : errorsFound = true;
203 2 : } else if ((underwaterBoundary.waterTempSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
204 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
205 0 : errorsFound = true;
206 : }
207 :
208 2 : if (ipsc->lAlphaFieldBlanks(3)) {
209 : // that's OK, we can have a blank schedule, the water will just have no free stream velocity
210 1 : } else if ((underwaterBoundary.velocitySched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
211 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
212 0 : errorsFound = true;
213 : }
214 2 : if (errorsFound) {
215 0 : break;
216 : }
217 : }
218 67 : if (errorsFound) {
219 0 : ShowFatalError(state, "Previous input problems cause program termination");
220 : }
221 67 : return (Num > 0);
222 : }
223 :
224 : Real64
225 5 : calculateWaterBoundaryConvectionCoefficient(Real64 const curWaterTemp, Real64 const freeStreamVelocity, Real64 const distanceFromLeadingEdge)
226 : {
227 5 : Real64 constexpr waterKinematicViscosity = 1e-6; // m2/s
228 5 : Real64 constexpr waterPrandtlNumber = 6; // -
229 5 : Real64 constexpr waterThermalConductivity = 0.6; // W/mK
230 : // do some calculation for forced convection from the leading edge of the ship
231 5 : Real64 const localReynoldsNumber = freeStreamVelocity * distanceFromLeadingEdge / waterKinematicViscosity;
232 5 : Real64 const localNusseltNumber = 0.0296 * pow(localReynoldsNumber, 0.8) * pow(waterPrandtlNumber, 1.0 / 3.0);
233 5 : Real64 const localConvectionCoeff = localNusseltNumber * waterThermalConductivity / distanceFromLeadingEdge;
234 :
235 : // do some calculations for natural convection from the bottom of the ship
236 5 : Real64 constexpr distanceFromBottomOfHull = 12; // meters, assumed for now
237 : // this Prandtl correction is from Incropera & Dewitt, Intro to HT, eq 9.20
238 5 : Real64 const prandtlCorrection =
239 : (0.75 * pow(waterPrandtlNumber, 0.5)) / pow(0.609 + 1.221 * pow(waterPrandtlNumber, 0.5) + 1.238 * waterPrandtlNumber, 0.25);
240 : // calculate the Grashof number
241 5 : Real64 constexpr gravity = 9.81; // m/s2
242 5 : Real64 constexpr beta = 0.000214; // water thermal expansion coefficient, from engineeringtoolbox.com, 1/C
243 5 : Real64 constexpr assumedSurfaceTemp = 25; // Grashof requires a surface temp, this should suffice
244 : Real64 const localGrashofNumber =
245 5 : (gravity * beta * std::abs(assumedSurfaceTemp - curWaterTemp) * pow(distanceFromBottomOfHull, 3)) / pow(waterKinematicViscosity, 2);
246 5 : Real64 const localNusseltFreeConvection = pow(localGrashofNumber / 4, 0.25) * prandtlCorrection;
247 5 : Real64 const localConvectionCoeffFreeConv = localNusseltFreeConvection * waterThermalConductivity / distanceFromBottomOfHull;
248 5 : return max(localConvectionCoeff, localConvectionCoeffFreeConv);
249 : }
250 :
251 0 : void UpdateUnderwaterBoundaries(EnergyPlusData &state)
252 : {
253 0 : for (auto &thisBoundary : state.dataWeather->underwaterBoundaries) {
254 0 : Real64 const curWaterTemp = thisBoundary.waterTempSched->getCurrentVal(); // C
255 0 : Real64 freeStreamVelocity = 0;
256 0 : if (thisBoundary.velocitySched != nullptr) {
257 0 : freeStreamVelocity = thisBoundary.velocitySched->getCurrentVal(); // m/s
258 : }
259 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TConv = curWaterTemp;
260 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HConv =
261 0 : Weather::calculateWaterBoundaryConvectionCoefficient(curWaterTemp, freeStreamVelocity, thisBoundary.distanceFromLeadingEdge);
262 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TRad = curWaterTemp;
263 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HRad = 0.0;
264 0 : }
265 0 : }
266 :
267 73 : void ReadVariableLocationOrientation(EnergyPlusData &state)
268 : {
269 : static constexpr std::string_view routineName = "ReadVariableLocationOrientation";
270 :
271 73 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
272 73 : auto const &ipsc = state.dataIPShortCut;
273 :
274 73 : ipsc->cCurrentModuleObject = "Site:VariableLocation";
275 73 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject) == 0) {
276 73 : return;
277 : }
278 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
279 0 : ipsc->cCurrentModuleObject,
280 : 1,
281 0 : ipsc->cAlphaArgs,
282 : NumAlpha,
283 0 : ipsc->rNumericArgs,
284 : NumNumber,
285 : IOStat,
286 0 : ipsc->lNumericFieldBlanks,
287 0 : ipsc->lAlphaFieldBlanks,
288 0 : ipsc->cAlphaFieldNames,
289 0 : ipsc->cNumericFieldNames);
290 :
291 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
292 :
293 0 : if (ipsc->lAlphaFieldBlanks(1)) {
294 0 : } else if ((state.dataEnvrn->varyingLocationLatSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(1))) == nullptr) {
295 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
296 : }
297 :
298 0 : if (ipsc->lAlphaFieldBlanks(2)) {
299 0 : } else if ((state.dataEnvrn->varyingLocationLongSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
300 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
301 : }
302 :
303 0 : if (ipsc->lAlphaFieldBlanks(3)) {
304 0 : } else if ((state.dataEnvrn->varyingOrientationSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
305 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
306 : }
307 : }
308 :
309 0 : void UpdateLocationAndOrientation(EnergyPlusData &state)
310 : {
311 0 : if (state.dataEnvrn->varyingLocationLatSched != nullptr) {
312 0 : state.dataEnvrn->Latitude = state.dataEnvrn->varyingLocationLatSched->getCurrentVal();
313 : }
314 0 : if (state.dataEnvrn->varyingLocationLongSched != nullptr) {
315 0 : state.dataEnvrn->Longitude = state.dataEnvrn->varyingLocationLongSched->getCurrentVal();
316 : }
317 :
318 0 : CheckLocationValidity(state);
319 0 : if (state.dataEnvrn->varyingOrientationSched != nullptr) {
320 0 : state.dataHeatBal->BuildingAzimuth = mod(state.dataEnvrn->varyingOrientationSched->getCurrentVal(), 360.0);
321 0 : state.dataSurfaceGeometry->CosBldgRelNorth =
322 0 : std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
323 0 : state.dataSurfaceGeometry->SinBldgRelNorth =
324 0 : std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
325 0 : for (size_t SurfNum = 1; SurfNum < state.dataSurface->Surface.size(); ++SurfNum) {
326 0 : auto &surf = state.dataSurface->Surface(SurfNum);
327 0 : for (int n = 1; n <= surf.Sides; ++n) {
328 0 : Real64 Xb = surf.Vertex(n).x;
329 0 : Real64 Yb = surf.Vertex(n).y;
330 0 : surf.NewVertex(n).x = Xb * state.dataSurfaceGeometry->CosBldgRelNorth - Yb * state.dataSurfaceGeometry->SinBldgRelNorth;
331 0 : surf.NewVertex(n).y = Xb * state.dataSurfaceGeometry->SinBldgRelNorth + Yb * state.dataSurfaceGeometry->CosBldgRelNorth;
332 0 : surf.NewVertex(n).z = surf.Vertex(n).z;
333 : }
334 0 : Vectors::CreateNewellSurfaceNormalVector(surf.NewVertex, surf.Sides, surf.NewellSurfaceNormalVector);
335 0 : Real64 SurfWorldAz = 0.0;
336 0 : Real64 SurfTilt = 0.0;
337 0 : Vectors::DetermineAzimuthAndTilt(
338 0 : surf.NewVertex, SurfWorldAz, SurfTilt, surf.lcsx, surf.lcsy, surf.lcsz, surf.NewellSurfaceNormalVector);
339 0 : surf.Azimuth = SurfWorldAz;
340 0 : surf.SinAzim = std::sin(SurfWorldAz * Constant::DegToRad);
341 0 : surf.CosAzim = std::cos(SurfWorldAz * Constant::DegToRad);
342 0 : surf.OutNormVec = surf.NewellSurfaceNormalVector;
343 : }
344 : }
345 0 : }
346 :
347 851 : bool GetNextEnvironment(EnergyPlusData &state, bool &Available, bool &ErrorsFound)
348 : {
349 :
350 : // SUBROUTINE INFORMATION:
351 : // AUTHOR Linda Lawrie
352 : // DATE WRITTEN August 2000
353 :
354 : // PURPOSE OF THIS SUBROUTINE:
355 : // This subroutine is called from the outer simulation manager and determines
356 : // if another environment is available in the "run list" or if the end has been
357 : // reached.
358 :
359 : static constexpr std::string_view RoutineName("GetNextEnvironment: ");
360 : static constexpr std::string_view EnvNameFormat("Environment,{},{},{},{},{},{},{},{},{},{},{},{},{}\n");
361 : static constexpr std::string_view EnvDSTNFormat("Environment:Daylight Saving,No,{}\n");
362 : static constexpr std::string_view EnvDSTYFormat("Environment:Daylight Saving,Yes,{},{},{}\n");
363 : static constexpr std::string_view DateFormat("{:02}/{:02}");
364 : static constexpr std::string_view DateFormatWithYear("{:02}/{:02}/{:04}");
365 :
366 851 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->GetEnvironmentFirstCall) {
367 :
368 107 : state.dataReportFlag->PrintEndDataDictionary = true;
369 :
370 107 : ReportOutputFileHeaders(state); // Write the output file header information
371 :
372 : // Setup Output Variables, CurrentModuleObject='All Simulations'
373 :
374 428 : SetupOutputVariable(state,
375 : "Site Outdoor Air Drybulb Temperature",
376 : Constant::Units::C,
377 107 : state.dataEnvrn->OutDryBulbTemp,
378 : OutputProcessor::TimeStepType::Zone,
379 : OutputProcessor::StoreType::Average,
380 : "Environment");
381 428 : SetupOutputVariable(state,
382 : "Site Outdoor Air Dewpoint Temperature",
383 : Constant::Units::C,
384 107 : state.dataEnvrn->OutDewPointTemp,
385 : OutputProcessor::TimeStepType::Zone,
386 : OutputProcessor::StoreType::Average,
387 : "Environment");
388 428 : SetupOutputVariable(state,
389 : "Site Outdoor Air Wetbulb Temperature",
390 : Constant::Units::C,
391 107 : state.dataEnvrn->OutWetBulbTemp,
392 : OutputProcessor::TimeStepType::Zone,
393 : OutputProcessor::StoreType::Average,
394 : "Environment");
395 428 : SetupOutputVariable(state,
396 : "Site Outdoor Air Humidity Ratio",
397 : Constant::Units::kgWater_kgDryAir,
398 107 : state.dataEnvrn->OutHumRat,
399 : OutputProcessor::TimeStepType::Zone,
400 : OutputProcessor::StoreType::Average,
401 : "Environment");
402 428 : SetupOutputVariable(state,
403 : "Site Outdoor Air Relative Humidity",
404 : Constant::Units::Perc,
405 107 : state.dataEnvrn->OutRelHum,
406 : OutputProcessor::TimeStepType::Zone,
407 : OutputProcessor::StoreType::Average,
408 : "Environment");
409 428 : SetupOutputVariable(state,
410 : "Site Outdoor Air Barometric Pressure",
411 : Constant::Units::Pa,
412 107 : state.dataEnvrn->OutBaroPress,
413 : OutputProcessor::TimeStepType::Zone,
414 : OutputProcessor::StoreType::Average,
415 : "Environment");
416 428 : SetupOutputVariable(state,
417 : "Site Wind Speed",
418 : Constant::Units::m_s,
419 107 : state.dataEnvrn->WindSpeed,
420 : OutputProcessor::TimeStepType::Zone,
421 : OutputProcessor::StoreType::Average,
422 : "Environment");
423 428 : SetupOutputVariable(state,
424 : "Site Wind Direction",
425 : Constant::Units::deg,
426 107 : state.dataEnvrn->WindDir,
427 : OutputProcessor::TimeStepType::Zone,
428 : OutputProcessor::StoreType::Average,
429 : "Environment");
430 428 : SetupOutputVariable(state,
431 : "Site Sky Temperature",
432 : Constant::Units::C,
433 107 : state.dataEnvrn->SkyTemp,
434 : OutputProcessor::TimeStepType::Zone,
435 : OutputProcessor::StoreType::Average,
436 : "Environment");
437 428 : SetupOutputVariable(state,
438 : "Site Horizontal Infrared Radiation Rate per Area",
439 : Constant::Units::W_m2,
440 107 : state.dataWeather->HorizIRSky,
441 : OutputProcessor::TimeStepType::Zone,
442 : OutputProcessor::StoreType::Average,
443 : "Environment");
444 428 : SetupOutputVariable(state,
445 : "Site Diffuse Solar Radiation Rate per Area",
446 : Constant::Units::W_m2,
447 107 : state.dataEnvrn->DifSolarRad,
448 : OutputProcessor::TimeStepType::Zone,
449 : OutputProcessor::StoreType::Average,
450 : "Environment");
451 428 : SetupOutputVariable(state,
452 : "Site Direct Solar Radiation Rate per Area",
453 : Constant::Units::W_m2,
454 107 : state.dataEnvrn->BeamSolarRad,
455 : OutputProcessor::TimeStepType::Zone,
456 : OutputProcessor::StoreType::Average,
457 : "Environment");
458 428 : SetupOutputVariable(state,
459 : "Liquid Precipitation Depth",
460 : Constant::Units::m,
461 107 : state.dataEnvrn->LiquidPrecipitation,
462 : OutputProcessor::TimeStepType::Zone,
463 : OutputProcessor::StoreType::Sum,
464 : "Environment");
465 428 : SetupOutputVariable(state,
466 : "Site Precipitation Rate",
467 : Constant::Units::m_s,
468 107 : state.dataWaterData->RainFall.CurrentRate,
469 : OutputProcessor::TimeStepType::Zone,
470 : OutputProcessor::StoreType::Average,
471 : "Environment");
472 428 : SetupOutputVariable(state,
473 : "Site Precipitation Depth",
474 : Constant::Units::m,
475 107 : state.dataWaterData->RainFall.CurrentAmount,
476 : OutputProcessor::TimeStepType::Zone,
477 : OutputProcessor::StoreType::Sum,
478 : "Environment");
479 428 : SetupOutputVariable(state,
480 : "Site Ground Reflected Solar Radiation Rate per Area",
481 : Constant::Units::W_m2,
482 107 : state.dataEnvrn->GndSolarRad,
483 : OutputProcessor::TimeStepType::Zone,
484 : OutputProcessor::StoreType::Average,
485 : "Environment");
486 428 : SetupOutputVariable(state,
487 : "Site Ground Temperature",
488 : Constant::Units::C,
489 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface],
490 : OutputProcessor::TimeStepType::Zone,
491 : OutputProcessor::StoreType::Average,
492 : "Environment");
493 428 : SetupOutputVariable(state,
494 : "Site Surface Ground Temperature",
495 : Constant::Units::C,
496 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow],
497 : OutputProcessor::TimeStepType::Zone,
498 : OutputProcessor::StoreType::Average,
499 : "Environment");
500 428 : SetupOutputVariable(state,
501 : "Site Deep Ground Temperature",
502 : Constant::Units::C,
503 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep],
504 : OutputProcessor::TimeStepType::Zone,
505 : OutputProcessor::StoreType::Average,
506 : "Environment");
507 428 : SetupOutputVariable(state,
508 : "Site Simple Factor Model Ground Temperature",
509 : Constant::Units::C,
510 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod],
511 : OutputProcessor::TimeStepType::Zone,
512 : OutputProcessor::StoreType::Average,
513 : "Environment");
514 428 : SetupOutputVariable(state,
515 : "Site Total Sky Cover",
516 : Constant::Units::None,
517 107 : state.dataEnvrn->TotalCloudCover,
518 : OutputProcessor::TimeStepType::Zone,
519 : OutputProcessor::StoreType::Average,
520 : "Environment");
521 428 : SetupOutputVariable(state,
522 : "Site Opaque Sky Cover",
523 : Constant::Units::None,
524 107 : state.dataEnvrn->OpaqueCloudCover,
525 : OutputProcessor::TimeStepType::Zone,
526 : OutputProcessor::StoreType::Average,
527 : "Environment");
528 428 : SetupOutputVariable(state,
529 : "Site Outdoor Air Enthalpy",
530 : Constant::Units::J_kg,
531 107 : state.dataEnvrn->OutEnthalpy,
532 : OutputProcessor::TimeStepType::Zone,
533 : OutputProcessor::StoreType::Average,
534 : "Environment");
535 428 : SetupOutputVariable(state,
536 : "Site Outdoor Air Density",
537 : Constant::Units::kg_m3,
538 107 : state.dataEnvrn->OutAirDensity,
539 : OutputProcessor::TimeStepType::Zone,
540 : OutputProcessor::StoreType::Average,
541 : "Environment");
542 428 : SetupOutputVariable(state,
543 : "Site Solar Azimuth Angle",
544 : Constant::Units::deg,
545 107 : state.dataWeather->SolarAzimuthAngle,
546 : OutputProcessor::TimeStepType::Zone,
547 : OutputProcessor::StoreType::Average,
548 : "Environment");
549 428 : SetupOutputVariable(state,
550 : "Site Solar Altitude Angle",
551 : Constant::Units::deg,
552 107 : state.dataWeather->SolarAltitudeAngle,
553 : OutputProcessor::TimeStepType::Zone,
554 : OutputProcessor::StoreType::Average,
555 : "Environment");
556 428 : SetupOutputVariable(state,
557 : "Site Solar Hour Angle",
558 : Constant::Units::deg,
559 107 : state.dataWeather->HrAngle,
560 : OutputProcessor::TimeStepType::Zone,
561 : OutputProcessor::StoreType::Average,
562 : "Environment");
563 321 : SetupOutputVariable(state,
564 : "Site Rain Status",
565 : Constant::Units::None,
566 107 : state.dataWeather->RptIsRain,
567 : OutputProcessor::TimeStepType::Zone,
568 : OutputProcessor::StoreType::Average,
569 : "Environment");
570 214 : SetupOutputVariable(state,
571 : "Site Snow on Ground Status",
572 : Constant::Units::None,
573 107 : state.dataWeather->RptIsSnow,
574 : OutputProcessor::TimeStepType::Zone,
575 : OutputProcessor::StoreType::Average,
576 : "Environment");
577 428 : SetupOutputVariable(state,
578 : "Site Exterior Horizontal Sky Illuminance",
579 : Constant::Units::lux,
580 107 : state.dataEnvrn->HISKF,
581 : OutputProcessor::TimeStepType::Zone,
582 : OutputProcessor::StoreType::Average,
583 : "Environment");
584 428 : SetupOutputVariable(state,
585 : "Site Exterior Horizontal Beam Illuminance",
586 : Constant::Units::lux,
587 107 : state.dataEnvrn->HISUNF,
588 : OutputProcessor::TimeStepType::Zone,
589 : OutputProcessor::StoreType::Average,
590 : "Environment");
591 428 : SetupOutputVariable(state,
592 : "Site Exterior Beam Normal Illuminance",
593 : Constant::Units::lux,
594 107 : state.dataEnvrn->HISUNFnorm,
595 : OutputProcessor::TimeStepType::Zone,
596 : OutputProcessor::StoreType::Average,
597 : "Environment");
598 428 : SetupOutputVariable(state,
599 : "Site Sky Diffuse Solar Radiation Luminous Efficacy",
600 : Constant::Units::lum_W,
601 107 : state.dataEnvrn->PDIFLW,
602 : OutputProcessor::TimeStepType::Zone,
603 : OutputProcessor::StoreType::Average,
604 : "Environment");
605 428 : SetupOutputVariable(state,
606 : "Site Beam Solar Radiation Luminous Efficacy",
607 : Constant::Units::lum_W,
608 107 : state.dataEnvrn->PDIRLW,
609 : OutputProcessor::TimeStepType::Zone,
610 : OutputProcessor::StoreType::Average,
611 : "Environment");
612 428 : SetupOutputVariable(state,
613 : "Site Daylighting Model Sky Clearness",
614 : Constant::Units::None,
615 107 : state.dataEnvrn->SkyClearness,
616 : OutputProcessor::TimeStepType::Zone,
617 : OutputProcessor::StoreType::Average,
618 : "Environment");
619 428 : SetupOutputVariable(state,
620 : "Site Daylighting Model Sky Brightness",
621 : Constant::Units::None,
622 107 : state.dataEnvrn->SkyBrightness,
623 : OutputProcessor::TimeStepType::Zone,
624 : OutputProcessor::StoreType::Average,
625 : "Environment");
626 321 : SetupOutputVariable(state,
627 : "Site Daylight Saving Time Status",
628 : Constant::Units::None,
629 107 : state.dataEnvrn->DSTIndicator,
630 : OutputProcessor::TimeStepType::Zone,
631 : OutputProcessor::StoreType::Average,
632 : "Environment");
633 214 : SetupOutputVariable(state,
634 : "Site Day Type Index",
635 : Constant::Units::None,
636 107 : state.dataWeather->RptDayType,
637 : OutputProcessor::TimeStepType::Zone,
638 : OutputProcessor::StoreType::Average,
639 : "Environment");
640 428 : SetupOutputVariable(state,
641 : "Site Mains Water Temperature",
642 : Constant::Units::C,
643 107 : state.dataEnvrn->WaterMainsTemp,
644 : OutputProcessor::TimeStepType::Zone,
645 : OutputProcessor::StoreType::Average,
646 : "Environment");
647 :
648 107 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
649 27 : SetupEMSActuator(state,
650 : "Weather Data",
651 : "Environment",
652 : "Outdoor Dry Bulb",
653 : "[C]",
654 27 : state.dataEnvrn->EMSOutDryBulbOverrideOn,
655 27 : state.dataEnvrn->EMSOutDryBulbOverrideValue);
656 27 : SetupEMSActuator(state,
657 : "Weather Data",
658 : "Environment",
659 : "Outdoor Dew Point",
660 : "[C]",
661 27 : state.dataEnvrn->EMSOutDewPointTempOverrideOn,
662 27 : state.dataEnvrn->EMSOutDewPointTempOverrideValue);
663 27 : SetupEMSActuator(state,
664 : "Weather Data",
665 : "Environment",
666 : "Outdoor Relative Humidity",
667 : "[%]",
668 27 : state.dataEnvrn->EMSOutRelHumOverrideOn,
669 27 : state.dataEnvrn->EMSOutRelHumOverrideValue);
670 27 : SetupEMSActuator(state,
671 : "Weather Data",
672 : "Environment",
673 : "Diffuse Solar",
674 : "[W/m2]",
675 27 : state.dataEnvrn->EMSDifSolarRadOverrideOn,
676 27 : state.dataEnvrn->EMSDifSolarRadOverrideValue);
677 27 : SetupEMSActuator(state,
678 : "Weather Data",
679 : "Environment",
680 : "Direct Solar",
681 : "[W/m2]",
682 27 : state.dataEnvrn->EMSBeamSolarRadOverrideOn,
683 27 : state.dataEnvrn->EMSBeamSolarRadOverrideValue);
684 27 : SetupEMSActuator(state,
685 : "Weather Data",
686 : "Environment",
687 : "Wind Speed",
688 : "[m/s]",
689 27 : state.dataEnvrn->EMSWindSpeedOverrideOn,
690 27 : state.dataEnvrn->EMSWindSpeedOverrideValue);
691 27 : SetupEMSActuator(state,
692 : "Weather Data",
693 : "Environment",
694 : "Wind Direction",
695 : "[deg]",
696 27 : state.dataEnvrn->EMSWindDirOverrideOn,
697 27 : state.dataEnvrn->EMSWindDirOverrideValue);
698 : }
699 107 : state.dataWeather->GetEnvironmentFirstCall = false;
700 :
701 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
702 :
703 851 : if (state.dataWeather->GetBranchInputOneTimeFlag) {
704 :
705 111 : SetupInterpolationValues(state);
706 111 : state.dataWeather->TimeStepFraction = 1.0 / double(state.dataGlobal->TimeStepsInHour);
707 111 : state.dataEnvrn->rhoAirSTP = Psychrometrics::PsyRhoAirFnPbTdbW(
708 : state, DataEnvironment::StdPressureSeaLevel, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
709 111 : OpenWeatherFile(state, ErrorsFound); // moved here because of possibility of special days on EPW file
710 111 : CloseWeatherFile(state);
711 111 : ReadUserWeatherInput(state);
712 111 : AllocateWeatherData(state);
713 111 : if (state.dataWeather->NumIntervalsPerHour != 1) {
714 0 : if (state.dataWeather->NumIntervalsPerHour != state.dataGlobal->TimeStepsInHour) {
715 0 : ShowSevereError(
716 : state,
717 0 : format("{}Number of intervals per hour on Weather file does not match specified number of Time Steps Per Hour", RoutineName));
718 0 : ErrorsFound = true;
719 : }
720 : }
721 111 : state.dataWeather->GetBranchInputOneTimeFlag = false;
722 111 : state.dataWeather->Envrn = 0;
723 111 : if (state.dataWeather->NumOfEnvrn > 0) {
724 110 : ResolveLocationInformation(state, ErrorsFound); // Obtain weather related info from input file
725 110 : CheckLocationValidity(state);
726 160 : if ((state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
727 50 : (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
728 50 : CheckWeatherFileValidity(state);
729 : }
730 109 : if (ErrorsFound) {
731 1 : ShowSevereError(state, format("{}No location specified, program will terminate.", RoutineName));
732 : }
733 : } else {
734 1 : ErrorsFound = true;
735 1 : ShowSevereError(state, format("{}No Design Days or Run Period(s) specified, program will terminate.", RoutineName));
736 : }
737 110 : if (state.dataSysVars->DDOnly && state.dataEnvrn->TotDesDays == 0) {
738 1 : ErrorsFound = true;
739 2 : ShowSevereError(
740 : state,
741 2 : format("{}Requested Design Days only (DataSystemVariables::DDOnly) but no Design Days specified, program will terminate.",
742 : RoutineName));
743 : }
744 110 : if (state.dataSysVars->ReverseDD && state.dataEnvrn->TotDesDays == 1) {
745 0 : ErrorsFound = true;
746 0 : ShowSevereError(
747 : state,
748 0 : format(
749 : "{}Requested Reverse Design Days (DataSystemVariables::ReverseDD) but only 1 Design Day specified, program will terminate.",
750 : RoutineName));
751 : }
752 :
753 : // Throw a Fatal now that we have said it'll terminalte
754 110 : if (ErrorsFound) {
755 2 : CloseWeatherFile(state); // will only close if opened.
756 4 : ShowFatalError(state, format("{}Errors found in Weather Data Input. Program terminates.", RoutineName));
757 : }
758 :
759 108 : state.dataEnvrn->CurrentOverallSimDay = 0;
760 108 : state.dataEnvrn->TotalOverallSimDays = 0;
761 108 : state.dataEnvrn->MaxNumberSimYears = 1;
762 336 : for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
763 228 : state.dataEnvrn->TotalOverallSimDays += state.dataWeather->Environment(i).TotalDays;
764 228 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
765 52 : state.dataEnvrn->MaxNumberSimYears = max(state.dataEnvrn->MaxNumberSimYears, state.dataWeather->Environment(i).NumSimYears);
766 : }
767 : }
768 108 : DisplaySimDaysProgress(state, state.dataEnvrn->CurrentOverallSimDay, state.dataEnvrn->TotalOverallSimDays);
769 : }
770 :
771 848 : CloseWeatherFile(state); // will only close if opened.
772 848 : ++state.dataWeather->Envrn;
773 848 : state.dataWeather->DatesShouldBeReset = false;
774 848 : if (state.dataWeather->Envrn > state.dataWeather->NumOfEnvrn) {
775 208 : Available = false;
776 208 : state.dataWeather->Envrn = 0;
777 208 : state.dataEnvrn->CurEnvirNum = 0;
778 : } else {
779 640 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
780 640 : state.dataGlobal->KindOfSim = envCurr.KindOfEnvrn;
781 640 : state.dataEnvrn->DayOfYear = envCurr.StartJDay;
782 640 : state.dataEnvrn->DayOfMonth = envCurr.StartDay;
783 640 : state.dataGlobal->CalendarYear = envCurr.StartYear;
784 640 : state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
785 640 : state.dataEnvrn->Month = envCurr.StartMonth;
786 640 : state.dataGlobal->NumOfDayInEnvrn = envCurr.TotalDays; // Set day loop maximum from DataGlobals
787 :
788 865 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
789 225 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
790 0 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) {
791 0 : if (state.dataGlobal->DoDesDaySim) {
792 0 : ShowWarningError(state, format("{}Adaptive Comfort being reported during design day.", RoutineName));
793 0 : Real64 GrossApproxAvgDryBulb = (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb +
794 0 : (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb -
795 0 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).DailyDBRange)) /
796 0 : 2.0;
797 0 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
798 0 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, false, GrossApproxAvgDryBulb);
799 : }
800 0 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
801 0 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, false, GrossApproxAvgDryBulb);
802 : }
803 : }
804 : } else {
805 0 : if (state.dataGlobal->DoWeathSim || state.dataGlobal->DoDesDaySim) {
806 0 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
807 0 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, true, 0.0);
808 : }
809 0 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
810 0 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, true, 0.0);
811 : }
812 : }
813 : }
814 : }
815 :
816 640 : if (state.dataWeather->Envrn > state.dataEnvrn->TotDesDays && state.dataWeather->WeatherFileExists) {
817 44 : OpenEPlusWeatherFile(state, ErrorsFound, false);
818 : }
819 640 : Available = true;
820 762 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
821 122 : (!state.dataWeather->WeatherFileExists && state.dataGlobal->DoWeathSim)) {
822 0 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
823 0 : ShowSevereError(state, "Weather Simulation requested, but no weather file attached.");
824 0 : ErrorsFound = true;
825 : }
826 0 : if (!state.dataGlobal->DoingHVACSizingSimulations) {
827 0 : state.dataWeather->Envrn = 0;
828 : }
829 0 : Available = false;
830 762 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
831 122 : (!state.dataWeather->WeatherFileExists && !state.dataGlobal->DoWeathSim)) {
832 80 : Available = false;
833 80 : if (!state.dataGlobal->DoingHVACSizingSimulations) {
834 80 : state.dataWeather->Envrn = 0;
835 : }
836 560 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoingSizing) {
837 0 : Available = false;
838 0 : state.dataWeather->Envrn = 0;
839 : }
840 :
841 640 : if (!ErrorsFound && Available && state.dataWeather->Envrn > 0) {
842 560 : state.dataEnvrn->EnvironmentName = envCurr.Title;
843 560 : state.dataEnvrn->CurEnvirNum = state.dataWeather->Envrn;
844 560 : state.dataEnvrn->RunPeriodStartDayOfWeek = 0;
845 665 : if ((state.dataGlobal->DoDesDaySim && (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather)) ||
846 105 : ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoWeathSim)) {
847 458 : if (state.dataWeather->PrntEnvHeaders && state.dataReportFlag->DoWeatherInitReporting) {
848 : static constexpr std::string_view EnvironFormat(
849 : "! <Environment>,Environment Name,Environment Type, Start Date, End Date, Start DayOfWeek, Duration {#days}, "
850 : "Source:Start DayOfWeek, Use Daylight Saving, Use Holidays, Apply Weekend Holiday Rule, Use Rain Values, Use Snow "
851 : "Values, Sky Temperature Model\n! <Environment:Special Days>, Special Day Name, Special Day Type, Source, Start Date, "
852 : "Duration {#days}\n! "
853 : "<Environment:Daylight Saving>, Daylight Saving Indicator, Source, Start Date, End Date\n! <Environment:WarmupDays>, "
854 : "NumberofWarmupDays");
855 65 : print(state.files.eio, "{}\n", EnvironFormat);
856 65 : state.dataWeather->PrntEnvHeaders = false;
857 : }
858 :
859 458 : std::string StDate;
860 458 : std::string EnDate;
861 913 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) ||
862 455 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodDesign)) {
863 3 : int DSTActStMon = 0;
864 3 : int DSTActStDay = 0;
865 3 : int DSTActEnMon = 0;
866 3 : int DSTActEnDay = 0;
867 3 : std::string kindOfRunPeriod = envCurr.cKindOfEnvrn;
868 3 : state.dataEnvrn->RunPeriodEnvironment = state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather;
869 3 : state.dataEnvrn->CurrentYearIsLeapYear = state.dataWeather->Environment(state.dataWeather->Envrn).IsLeapYear;
870 3 : if (state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears) {
871 0 : state.dataWeather->LeapYearAdd = 1;
872 : } else {
873 3 : state.dataWeather->LeapYearAdd = 0;
874 : }
875 3 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
876 0 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
877 : }
878 3 : state.dataWeather->UseDaylightSaving = envCurr.UseDST;
879 3 : state.dataWeather->UseSpecialDays = envCurr.UseHolidays;
880 3 : state.dataWeather->UseRainValues = envCurr.UseRain;
881 3 : state.dataWeather->UseSnowValues = envCurr.UseSnow;
882 :
883 3 : bool missingLeap(false); // Defer acting on anything found here until after the other range checks (see below)
884 :
885 3 : if (envCurr.ActualWeather && !state.dataWeather->WFAllowsLeapYears) {
886 0 : for (int year = envCurr.StartYear; year <= envCurr.EndYear; year++) {
887 0 : if (!isLeapYear(year)) {
888 0 : continue;
889 : }
890 :
891 0 : ShowSevereError(
892 : state,
893 0 : format("{}Weatherfile does not support leap years but runperiod includes a leap year ({})", RoutineName, year));
894 0 : missingLeap = true;
895 : }
896 : }
897 :
898 3 : bool OkRun = false;
899 :
900 3 : if (envCurr.ActualWeather) {
901 : // Actual weather
902 0 : for (auto const &dataperiod : state.dataWeather->DataPeriods) {
903 0 : int runStartJulian = dataperiod.DataStJDay;
904 0 : int runEndJulian = dataperiod.DataEnJDay;
905 0 : if (!dataperiod.HasYearData) {
906 0 : ShowSevereError(state,
907 0 : format("{}Actual weather runperiod has been entered but weatherfile DATA PERIOD does not have "
908 : "year included in start/end date.",
909 : RoutineName));
910 0 : ShowContinueError(state, "...to match the RunPeriod, the DATA PERIOD should be mm/dd/yyyy for both, or");
911 0 : ShowContinueError(state, "(...set \"Treat Weather as Actual\" to \"No\".)");
912 : }
913 0 : if (!General::BetweenDates(envCurr.StartDate, runStartJulian, runEndJulian)) {
914 0 : continue;
915 : }
916 0 : if (!General::BetweenDates(envCurr.EndDate, runStartJulian, runEndJulian)) {
917 0 : continue;
918 : }
919 0 : OkRun = true;
920 0 : break;
921 0 : }
922 : } else {
923 : // Typical (or just non-actual) weather
924 3 : for (auto &dataperiod : state.dataWeather->DataPeriods) {
925 : // Since this is not actual weather, there may be issues with this calculation
926 : // Assume the weather data starts the same year as the simulation, so LeapYearAdd is what
927 : // should be used.
928 3 : int runStartOrdinal = General::OrdinalDay(dataperiod.StMon, dataperiod.StDay, state.dataWeather->LeapYearAdd);
929 : // This one is harder, leave as is for now. What about multiple years of data?
930 3 : int runEndOrdinal = General::OrdinalDay(dataperiod.EnMon, dataperiod.EnDay, state.dataWeather->LeapYearAdd);
931 3 : if (runStartOrdinal == 1 && (runEndOrdinal == 366 || runEndOrdinal == 365)) {
932 : // Complete year(s) of weather data, will wrap around
933 3 : OkRun = true;
934 3 : break;
935 : }
936 0 : if (!General::BetweenDates(envCurr.StartJDay, runStartOrdinal, runEndOrdinal)) {
937 0 : continue;
938 : }
939 0 : if (!General::BetweenDates(envCurr.EndJDay, runStartOrdinal, runEndOrdinal)) {
940 0 : continue;
941 : }
942 0 : OkRun = true;
943 3 : }
944 : }
945 :
946 3 : if (!OkRun) {
947 0 : if (!envCurr.ActualWeather) {
948 0 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
949 0 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
950 0 : ShowSevereError(state,
951 0 : format("{}Runperiod [mm/dd] (Start={},End={}) requested not within Data Period(s) from Weather File",
952 : RoutineName,
953 : StDate,
954 : EnDate));
955 : } else {
956 0 : StDate = format(DateFormatWithYear, envCurr.StartMonth, envCurr.StartDay, envCurr.StartYear);
957 0 : EnDate = format(DateFormatWithYear, envCurr.EndMonth, envCurr.EndDay, envCurr.EndYear);
958 0 : ShowSevereError(
959 : state,
960 0 : format("{}Runperiod [mm/dd/yyyy] (Start={},End={}) requested not within Data Period(s) from Weather File",
961 : RoutineName,
962 : StDate,
963 : EnDate));
964 : }
965 :
966 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
967 0 : StDate = format(DateFormat, dataPeriod1.StMon, dataPeriod1.StDay);
968 0 : EnDate = format(DateFormat, dataPeriod1.EnMon, dataPeriod1.EnDay);
969 0 : if (dataPeriod1.StYear > 0) {
970 0 : StDate += format("/{}", dataPeriod1.StYear);
971 : } else {
972 0 : StDate += "/<noyear>";
973 : }
974 0 : if (dataPeriod1.EnYear > 0) {
975 0 : EnDate += format("/{}", dataPeriod1.EnYear);
976 : } else {
977 0 : EnDate += "/<noyear>";
978 : }
979 0 : if (state.dataWeather->NumDataPeriods == 1) {
980 0 : ShowContinueError(state, format("Weather Data Period (Start={},End={})", StDate, EnDate));
981 : } else {
982 0 : ShowContinueError(state, format("Multiple Weather Data Periods 1st (Start={},End={})", StDate, EnDate));
983 : }
984 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
985 : }
986 :
987 3 : if (missingLeap) {
988 : // Bail out now if we still need to
989 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
990 : }
991 :
992 : // Following builds Environment start/end for ASHRAE 55 warnings
993 3 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
994 3 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
995 3 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
996 3 : StDate += format("/{}", envCurr.StartYear);
997 3 : EnDate += format("/{}", envCurr.EndYear);
998 : }
999 3 : state.dataEnvrn->EnvironmentStartEnd = StDate + " - " + EnDate;
1000 3 : state.dataEnvrn->StartYear = envCurr.StartYear;
1001 3 : state.dataEnvrn->EndYear = envCurr.EndYear;
1002 :
1003 3 : int TWeekDay = (envCurr.DayOfWeek == 0) ? 1 : envCurr.DayOfWeek;
1004 3 : auto const &MonWeekDay = envCurr.MonWeekDay;
1005 :
1006 3 : if (state.dataReportFlag->DoWeatherInitReporting) {
1007 0 : std::string_view const AlpUseDST = (envCurr.UseDST) ? "Yes" : "No";
1008 0 : std::string_view const AlpUseSpec = (envCurr.UseHolidays) ? "Yes" : "No";
1009 0 : std::string_view const ApWkRule = (envCurr.ApplyWeekendRule) ? "Yes" : "No";
1010 0 : std::string_view const AlpUseRain = (envCurr.UseRain) ? "Yes" : "No";
1011 0 : std::string_view const AlpUseSnow = (envCurr.UseSnow) ? "Yes" : "No";
1012 :
1013 0 : print(state.files.eio,
1014 : EnvNameFormat,
1015 0 : envCurr.Title,
1016 : kindOfRunPeriod,
1017 : StDate,
1018 : EnDate,
1019 0 : Sched::dayTypeNames[TWeekDay],
1020 0 : fmt::to_string(envCurr.TotalDays),
1021 : "Use RunPeriod Specified Day",
1022 : AlpUseDST,
1023 : AlpUseSpec,
1024 : ApWkRule,
1025 : AlpUseRain,
1026 : AlpUseSnow,
1027 0 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1028 : }
1029 :
1030 6 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
1031 9 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather && state.dataGlobal->DoWeathSim) &&
1032 3 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
1033 0 : if (state.dataWeather->WFAllowsLeapYears) {
1034 0 : ShowSevereError(
1035 : state,
1036 0 : format("{}AdaptiveComfort Reporting does not work correctly with leap years in weather files.", RoutineName));
1037 0 : ErrorsFound = true;
1038 : }
1039 0 : if (state.dataWeather->NumDataPeriods != 1) {
1040 0 : ShowSevereError(
1041 : state,
1042 0 : format("{}AdaptiveComfort Reporting does not work correctly with multiple dataperiods in weather files.",
1043 : RoutineName));
1044 0 : ErrorsFound = true;
1045 : }
1046 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
1047 0 : if (dataPeriod1.StMon == 1 && dataPeriod1.StDay == 1) {
1048 0 : int RunStJDay = General::OrdinalDay(dataPeriod1.StMon, dataPeriod1.StDay, state.dataWeather->LeapYearAdd);
1049 0 : int RunEnJDay = General::OrdinalDay(dataPeriod1.EnMon, dataPeriod1.EnDay, state.dataWeather->LeapYearAdd);
1050 0 : if (RunEnJDay - RunStJDay + 1 != 365) {
1051 0 : ShowSevereError(state,
1052 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do "
1053 : "not contain 365 days.",
1054 : RoutineName));
1055 0 : ErrorsFound = true;
1056 : }
1057 0 : } else {
1058 0 : ShowSevereError(state,
1059 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do not "
1060 : "start on 1 January.",
1061 : RoutineName));
1062 0 : ErrorsFound = true;
1063 : }
1064 0 : if (state.dataWeather->NumIntervalsPerHour != 1) {
1065 0 : ShowSevereError(state,
1066 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that have "
1067 : "multiple interval records per hour.",
1068 : RoutineName));
1069 0 : ErrorsFound = true;
1070 : }
1071 : } // if
1072 :
1073 : // Only need to set Week days for Run Days
1074 3 : state.dataEnvrn->RunPeriodStartDayOfWeek = TWeekDay;
1075 3 : state.dataWeather->WeekDayTypes = 0;
1076 3 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1077 3 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1078 :
1079 3 : state.dataWeather->curSimDayForEndOfRunPeriod = envCurr.TotalDays;
1080 :
1081 3 : int i = JDay5Start;
1082 : while (true) {
1083 426 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1084 426 : TWeekDay = mod(TWeekDay, 7) + 1;
1085 426 : ++i;
1086 426 : if (i > 366) {
1087 1 : i = 1;
1088 : }
1089 426 : if (i == JDay5End) {
1090 3 : break;
1091 : }
1092 : }
1093 :
1094 3 : state.dataWeather->DaylightSavingIsActive =
1095 3 : (state.dataWeather->UseDaylightSaving && state.dataWeather->EPWDaylightSaving) || state.dataWeather->IDFDaylightSaving;
1096 :
1097 3 : envCurr.SetWeekDays = false;
1098 :
1099 3 : if (state.dataWeather->DaylightSavingIsActive) {
1100 0 : SetDSTDateRanges(state, MonWeekDay, state.dataWeather->DSTIndex, DSTActStMon, DSTActStDay, DSTActEnMon, DSTActEnDay);
1101 : }
1102 :
1103 3 : SetSpecialDayDates(state, MonWeekDay);
1104 :
1105 3 : if (envCurr.StartMonth != 1 || envCurr.StartDay != 1) {
1106 1 : state.dataWeather->StartDatesCycleShouldBeReset = true;
1107 1 : state.dataWeather->Jan1DatesShouldBeReset = true;
1108 : }
1109 :
1110 3 : if (envCurr.StartMonth == 1 && envCurr.StartDay == 1) {
1111 2 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1112 2 : state.dataWeather->Jan1DatesShouldBeReset = true;
1113 : }
1114 :
1115 3 : if (envCurr.ActualWeather) {
1116 0 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1117 0 : state.dataWeather->Jan1DatesShouldBeReset = true;
1118 : }
1119 :
1120 : // Report Actual Dates for Daylight Saving and Special Days
1121 3 : if (!state.dataGlobal->KickOffSimulation) {
1122 3 : std::string Source;
1123 3 : if (state.dataWeather->UseDaylightSaving) {
1124 3 : if (state.dataWeather->EPWDaylightSaving) {
1125 0 : Source = "WeatherFile";
1126 : }
1127 : } else {
1128 0 : Source = "RunPeriod Object";
1129 : }
1130 3 : if (state.dataWeather->IDFDaylightSaving) {
1131 0 : Source = "InputFile";
1132 : }
1133 3 : if (state.dataWeather->DaylightSavingIsActive && state.dataReportFlag->DoWeatherInitReporting) {
1134 0 : StDate = format(DateFormat, DSTActStMon, DSTActStDay);
1135 0 : EnDate = format(DateFormat, DSTActEnMon, DSTActEnDay);
1136 0 : print(state.files.eio, EnvDSTYFormat, Source, StDate, EnDate);
1137 3 : } else if (state.dataGlobal->DoOutputReporting) {
1138 0 : print(state.files.eio, EnvDSTNFormat, Source);
1139 : }
1140 3 : for (int k = 1; k <= state.dataWeather->NumSpecialDays; ++k) {
1141 0 : auto &specialDay = state.dataWeather->SpecialDays(k);
1142 : static constexpr std::string_view EnvSpDyFormat("Environment:Special Days,{},{},{},{},{:3}\n");
1143 0 : if (specialDay.WthrFile && state.dataWeather->UseSpecialDays && state.dataReportFlag->DoWeatherInitReporting) {
1144 0 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1145 0 : print(state.files.eio,
1146 : EnvSpDyFormat,
1147 0 : specialDay.Name,
1148 0 : Sched::dayTypeNames[specialDay.DayType],
1149 : "WeatherFile",
1150 : StDate,
1151 0 : specialDay.Duration);
1152 : }
1153 0 : if (!specialDay.WthrFile && state.dataReportFlag->DoWeatherInitReporting) {
1154 0 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1155 0 : print(state.files.eio,
1156 : EnvSpDyFormat,
1157 0 : specialDay.Name,
1158 0 : Sched::dayTypeNames[specialDay.DayType],
1159 : "InputFile",
1160 : StDate,
1161 0 : specialDay.Duration);
1162 : }
1163 : }
1164 3 : }
1165 :
1166 460 : } else if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay ||
1167 2 : state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay) { // Design Day
1168 453 : auto const &desDayInput = state.dataWeather->DesDayInput(envCurr.DesignDayNum);
1169 453 : state.dataEnvrn->RunPeriodEnvironment = false;
1170 453 : StDate = format(DateFormat, desDayInput.Month, desDayInput.DayOfMonth);
1171 453 : EnDate = StDate;
1172 453 : if (state.dataReportFlag->DoWeatherInitReporting) {
1173 112 : print(state.files.eio,
1174 : EnvNameFormat,
1175 112 : envCurr.Title,
1176 : "SizingPeriod:DesignDay",
1177 : StDate,
1178 : EnDate,
1179 112 : Sched::dayTypeNames[desDayInput.DayType],
1180 : "1",
1181 : "N/A",
1182 : "N/A",
1183 : "N/A",
1184 : "N/A",
1185 : "N/A",
1186 : "N/A",
1187 112 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1188 : }
1189 453 : if (desDayInput.DSTIndicator == 0 && state.dataReportFlag->DoWeatherInitReporting) {
1190 112 : print(state.files.eio, EnvDSTNFormat, "SizingPeriod:DesignDay");
1191 341 : } else if (state.dataReportFlag->DoWeatherInitReporting) {
1192 0 : print(state.files.eio, EnvDSTYFormat, "SizingPeriod:DesignDay", StDate, EnDate);
1193 : }
1194 : }
1195 458 : }
1196 : } // ErrorsFound
1197 : }
1198 :
1199 848 : if (ErrorsFound && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
1200 0 : ShowSevereError(state, format("{}Errors found in getting a new environment", RoutineName));
1201 0 : Available = false;
1202 848 : } else if (ErrorsFound) {
1203 0 : Available = false;
1204 : }
1205 848 : return Available && !ErrorsFound;
1206 : }
1207 :
1208 4 : void AddDesignSetToEnvironmentStruct(EnergyPlusData &state, int const HVACSizingIterCount)
1209 : {
1210 4 : int OrigNumOfEnvrn = state.dataWeather->NumOfEnvrn;
1211 :
1212 14 : for (int i = 1; i <= OrigNumOfEnvrn; ++i) {
1213 : // Gotcha: references may no longer be valid after a redimension! Cannot declare reference to Environment(i) here.
1214 10 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1215 8 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1216 8 : auto &envBase = state.dataWeather->Environment(i);
1217 8 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1218 8 : envNew = envBase; // copy over seed data from current array element
1219 8 : envNew.SeedEnvrnNum = i;
1220 8 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeDesignDay;
1221 8 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1222 8 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1223 2 : } else if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
1224 2 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1225 2 : auto &envBase = state.dataWeather->Environment(i);
1226 2 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1227 2 : envNew = envBase; // copy over seed data
1228 2 : envNew.SeedEnvrnNum = i;
1229 2 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeRunPeriodDesign;
1230 2 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1231 2 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1232 : }
1233 : } // for each loop over Environment data strucure
1234 4 : }
1235 :
1236 93 : void SetupWeekDaysByMonth(EnergyPlusData &state, int const StMon, int const StDay, int const StWeekDay, Array1D_int &WeekDays)
1237 : {
1238 :
1239 : // SUBROUTINE INFORMATION:
1240 : // AUTHOR Linda Lawrie
1241 : // DATE WRITTEN August 2000
1242 :
1243 : // PURPOSE OF THIS SUBROUTINE:
1244 : // This subroutine calculates the weekday for each month based on the start date and
1245 : // weekday specified for that date.
1246 :
1247 : // Argument array dimensioning
1248 93 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1249 :
1250 : // Set 1st day of Start Month
1251 93 : int CurWeekDay{StWeekDay};
1252 256 : for (int i = 1; i <= StDay - 1; ++i) {
1253 163 : --CurWeekDay;
1254 163 : if (CurWeekDay == 0) {
1255 23 : CurWeekDay = 7;
1256 : }
1257 : }
1258 :
1259 93 : WeekDays(StMon) = CurWeekDay;
1260 1080 : for (int i = StMon + 1; i <= 12; ++i) {
1261 :
1262 987 : if (i == 2) {
1263 84 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1264 424 : while (CurWeekDay > 7) {
1265 340 : CurWeekDay -= 7;
1266 : }
1267 84 : WeekDays(i) = CurWeekDay;
1268 903 : } else if (i == 3) {
1269 88 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + state.dataWeather->LeapYearAdd;
1270 440 : while (CurWeekDay > 7) {
1271 352 : CurWeekDay -= 7;
1272 : }
1273 88 : WeekDays(i) = CurWeekDay;
1274 : } else {
1275 815 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1276 4391 : while (CurWeekDay > 7) {
1277 3576 : CurWeekDay -= 7;
1278 : }
1279 815 : WeekDays(i) = CurWeekDay;
1280 : }
1281 : }
1282 :
1283 93 : if (any_eq(WeekDays, 0)) {
1284 : // need to start at StMon and go backwards.
1285 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1286 9 : CurWeekDay = StWeekDay;
1287 146 : for (int i = 1; i <= StDay - 1; ++i) {
1288 137 : --CurWeekDay;
1289 137 : if (CurWeekDay == 0) {
1290 19 : CurWeekDay = 7;
1291 : }
1292 : }
1293 :
1294 45 : for (int i = StMon - 1; i >= 1; --i) {
1295 :
1296 36 : if (i == 1) {
1297 9 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1298 49 : while (CurWeekDay <= 0) {
1299 40 : CurWeekDay += 7;
1300 : }
1301 9 : WeekDays(i) = CurWeekDay;
1302 27 : } else if (i == 2) {
1303 5 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
1304 25 : while (CurWeekDay <= 0) {
1305 20 : CurWeekDay += 7;
1306 : }
1307 5 : WeekDays(i) = CurWeekDay;
1308 22 : } else if ((i >= 3) && (i <= 12)) {
1309 22 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1310 119 : while (CurWeekDay <= 0) {
1311 97 : CurWeekDay += 7;
1312 : }
1313 22 : WeekDays(i) = CurWeekDay;
1314 : }
1315 : }
1316 : }
1317 93 : }
1318 : #pragma clang diagnostic pop
1319 :
1320 0 : void ResetWeekDaysByMonth(EnergyPlusData &state,
1321 : Array1D_int &WeekDays,
1322 : int const AddLeapYear,
1323 : int const StartMonth,
1324 : int const StartMonthDay,
1325 : int const EndMonth,
1326 : int const EndMonthDay,
1327 : bool const Rollover,
1328 : bool const MidSimReset)
1329 : {
1330 :
1331 : // SUBROUTINE INFORMATION:
1332 : // AUTHOR Linda Lawrie
1333 : // DATE WRITTEN March 2012
1334 :
1335 : // PURPOSE OF THIS SUBROUTINE:
1336 : // This subroutine resets the weekday for each month based on the current weekday
1337 : // and previous weekdays per month.
1338 :
1339 0 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1340 :
1341 0 : Array1D_int WeekDaysCopy(12);
1342 : int CurWeekDay;
1343 :
1344 0 : WeekDaysCopy = WeekDays;
1345 0 : if (!MidSimReset) {
1346 0 : if (Rollover) {
1347 0 : if (StartMonth == 1) {
1348 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1349 : } else {
1350 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1351 : }
1352 : } else { // restart at same as before
1353 0 : CurWeekDay = WeekDays(StartMonth);
1354 : }
1355 0 : while (CurWeekDay > 7) {
1356 0 : CurWeekDay -= 7;
1357 : }
1358 :
1359 0 : WeekDays = 0;
1360 0 : WeekDays(StartMonth) = CurWeekDay;
1361 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1362 0 : if (i == 2) {
1363 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1364 0 : while (CurWeekDay > 7) {
1365 0 : CurWeekDay -= 7;
1366 : }
1367 0 : WeekDays(i) = CurWeekDay;
1368 0 : } else if (i == 3) {
1369 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1370 0 : while (CurWeekDay > 7) {
1371 0 : CurWeekDay -= 7;
1372 : }
1373 0 : WeekDays(i) = CurWeekDay;
1374 : } else {
1375 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1376 0 : while (CurWeekDay > 7) {
1377 0 : CurWeekDay -= 7;
1378 : }
1379 0 : WeekDays(i) = CurWeekDay;
1380 : }
1381 : }
1382 :
1383 0 : if (any_eq(WeekDays, 0)) {
1384 : // need to start at StMon and go backwards.
1385 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1386 0 : CurWeekDay = WeekDays(StartMonth);
1387 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1388 0 : --CurWeekDay;
1389 0 : if (CurWeekDay == 0) {
1390 0 : CurWeekDay = 7;
1391 : }
1392 : }
1393 :
1394 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1395 :
1396 0 : if (i == 1) {
1397 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1398 0 : while (CurWeekDay <= 0) {
1399 0 : CurWeekDay += 7;
1400 : }
1401 0 : WeekDays(i) = CurWeekDay;
1402 0 : } else if (i == 2) {
1403 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1404 0 : while (CurWeekDay <= 0) {
1405 0 : CurWeekDay += 7;
1406 : }
1407 0 : WeekDays(i) = CurWeekDay;
1408 0 : } else if ((i >= 3) && (i <= 12)) {
1409 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1410 0 : while (CurWeekDay <= 0) {
1411 0 : CurWeekDay += 7;
1412 : }
1413 0 : WeekDays(i) = CurWeekDay;
1414 : }
1415 : }
1416 : }
1417 :
1418 : } else {
1419 0 : if (Rollover) {
1420 0 : if (StartMonth == 1) {
1421 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1422 : } else {
1423 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1424 : }
1425 : } else { // restart at same as before
1426 0 : CurWeekDay = WeekDays(StartMonth);
1427 : }
1428 0 : while (CurWeekDay > 7) {
1429 0 : CurWeekDay -= 7;
1430 : }
1431 0 : WeekDays = 0;
1432 0 : if (StartMonth != 1) {
1433 0 : CurWeekDay = WeekDaysCopy(12) + state.dataWeather->EndDayOfMonth(12);
1434 0 : while (CurWeekDay > 7) {
1435 0 : CurWeekDay -= 7;
1436 : }
1437 0 : WeekDays(1) = CurWeekDay;
1438 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1439 0 : while (CurWeekDay > 7) {
1440 0 : CurWeekDay -= 7;
1441 : }
1442 0 : WeekDays(2) = CurWeekDay;
1443 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1444 0 : while (CurWeekDay > 7) {
1445 0 : CurWeekDay -= 7;
1446 : }
1447 0 : WeekDays(3) = CurWeekDay;
1448 0 : for (int i = 4; i <= 12; ++i) {
1449 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1450 0 : while (CurWeekDay > 7) {
1451 0 : CurWeekDay -= 7;
1452 : }
1453 0 : WeekDays(i) = CurWeekDay;
1454 : }
1455 : } else {
1456 0 : WeekDays = 0;
1457 0 : WeekDays(StartMonth) = CurWeekDay;
1458 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1459 0 : if (i == 2) {
1460 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1461 0 : while (CurWeekDay > 7) {
1462 0 : CurWeekDay -= 7;
1463 : }
1464 0 : WeekDays(i) = CurWeekDay;
1465 0 : } else if (i == 3) {
1466 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1467 0 : while (CurWeekDay > 7) {
1468 0 : CurWeekDay -= 7;
1469 : }
1470 0 : WeekDays(i) = CurWeekDay;
1471 : } else {
1472 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1473 0 : while (CurWeekDay > 7) {
1474 0 : CurWeekDay -= 7;
1475 : }
1476 0 : WeekDays(i) = CurWeekDay;
1477 : }
1478 : }
1479 :
1480 0 : if (any_eq(WeekDays, 0)) {
1481 : // need to start at StMon and go backwards.
1482 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1483 0 : CurWeekDay = WeekDays(StartMonth);
1484 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1485 0 : --CurWeekDay;
1486 0 : if (CurWeekDay == 0) {
1487 0 : CurWeekDay = 7;
1488 : }
1489 : }
1490 :
1491 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1492 :
1493 0 : if (i == 1) {
1494 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1495 0 : while (CurWeekDay <= 0) {
1496 0 : CurWeekDay += 7;
1497 : }
1498 0 : WeekDays(i) = CurWeekDay;
1499 0 : } else if (i == 2) {
1500 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1501 0 : while (CurWeekDay <= 0) {
1502 0 : CurWeekDay += 7;
1503 : }
1504 0 : WeekDays(i) = CurWeekDay;
1505 0 : } else if ((i >= 3) && (i <= 12)) {
1506 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1507 0 : while (CurWeekDay <= 0) {
1508 0 : CurWeekDay += 7;
1509 : }
1510 0 : WeekDays(i) = CurWeekDay;
1511 : }
1512 : }
1513 : }
1514 : }
1515 : }
1516 0 : }
1517 :
1518 0 : void SetDSTDateRanges(EnergyPlusData &state,
1519 : Array1D_int const &MonWeekDay, // Weekday of each day 1 of month
1520 : Array1D_int &DSTIdx, // DST Index for each julian day (1:366)
1521 : ObjexxFCL::Optional_int DSTActStMon,
1522 : ObjexxFCL::Optional_int DSTActStDay,
1523 : ObjexxFCL::Optional_int DSTActEnMon,
1524 : ObjexxFCL::Optional_int DSTActEnDay)
1525 : {
1526 :
1527 : // SUBROUTINE INFORMATION:
1528 : // AUTHOR Linda Lawrie
1529 : // DATE WRITTEN March 2012
1530 :
1531 : // PURPOSE OF THIS SUBROUTINE:
1532 : // With multiple year weather files (or repeating weather files that rollover day),
1533 : // need to set DST (Daylight Saving Time) dates at start of environment or year.
1534 : // DST is only projected for one year.
1535 :
1536 : static constexpr std::string_view RoutineName("SetDSTDateRanges: ");
1537 :
1538 : int ActStartMonth; // Actual Start Month
1539 : int ActStartDay; // Actual Start Day of Month
1540 : int ActEndMonth; // Actual End Month
1541 : int ActEndDay; // Actual End Day of Month
1542 :
1543 0 : bool ErrorsFound = false;
1544 0 : if (state.dataWeather->DST.StDateType == DateType::MonthDay) {
1545 0 : ActStartMonth = state.dataWeather->DST.StMon;
1546 0 : ActStartDay = state.dataWeather->DST.StDay;
1547 0 : } else if (state.dataWeather->DST.StDateType == DateType::NthDayInMonth) {
1548 0 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1549 0 : while (ThisDay <= 0) {
1550 0 : ThisDay += 7;
1551 : }
1552 0 : ThisDay += 7 * (state.dataWeather->DST.StDay - 1);
1553 0 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1554 0 : ShowSevereError(state, format("{}Determining DST: DST Start Date, Nth Day of Month, not enough Nths", RoutineName));
1555 0 : ErrorsFound = true;
1556 : } else {
1557 0 : ActStartMonth = state.dataWeather->DST.StMon;
1558 0 : ActStartDay = ThisDay;
1559 : }
1560 : } else { // LastWeekDayInMonth
1561 0 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1562 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1563 0 : ThisDay += 7;
1564 : }
1565 0 : ActStartMonth = state.dataWeather->DST.StMon;
1566 0 : ActStartDay = ThisDay;
1567 : }
1568 :
1569 0 : if (state.dataWeather->DST.EnDateType == DateType::MonthDay) {
1570 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1571 0 : ActEndDay = state.dataWeather->DST.EnDay;
1572 0 : } else if (state.dataWeather->DST.EnDateType == DateType::NthDayInMonth) {
1573 0 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1574 0 : while (ThisDay <= 0) {
1575 0 : ThisDay += 7;
1576 : }
1577 0 : ThisDay += 7 * (state.dataWeather->DST.EnDay - 1);
1578 0 : if (ThisDay >> state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1579 0 : ActEndMonth = 0; // Suppress uninitialized warning
1580 0 : ActEndDay = 0; // Suppress uninitialized warning
1581 0 : ShowSevereError(state, format("{}Determining DST: DST End Date, Nth Day of Month, not enough Nths", RoutineName));
1582 0 : ErrorsFound = true;
1583 : } else {
1584 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1585 0 : ActEndDay = ThisDay;
1586 : }
1587 : } else { // LastWeekDayInMonth
1588 0 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1589 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1590 0 : ThisDay += 7;
1591 : }
1592 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1593 0 : ActEndDay = ThisDay;
1594 : }
1595 :
1596 0 : if (ErrorsFound) {
1597 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1598 : }
1599 :
1600 0 : if (present(DSTActStMon)) {
1601 0 : DSTActStMon = ActStartMonth;
1602 0 : DSTActStDay = ActStartDay;
1603 0 : DSTActEnMon = ActEndMonth;
1604 0 : DSTActEnDay = ActEndDay;
1605 : }
1606 :
1607 0 : DSTIdx = 0;
1608 0 : int JDay = General::OrdinalDay(ActStartMonth, ActStartDay, state.dataWeather->LeapYearAdd);
1609 0 : int JDay1 = General::OrdinalDay(ActEndMonth, ActEndDay, state.dataWeather->LeapYearAdd);
1610 0 : if (JDay1 >= JDay) {
1611 0 : DSTIdx({JDay, JDay1}) = 1;
1612 : } else {
1613 0 : DSTIdx({JDay, 366}) = 1;
1614 0 : DSTIdx({1, JDay1}) = 1;
1615 : }
1616 0 : }
1617 :
1618 9 : void SetSpecialDayDates(EnergyPlusData &state, Array1D_int const &MonWeekDay) // Weekday of each day 1 of month
1619 : {
1620 :
1621 : // SUBROUTINE INFORMATION:
1622 : // AUTHOR Linda Lawrie
1623 : // DATE WRITTEN March 2012
1624 :
1625 : // PURPOSE OF THIS SUBROUTINE:
1626 : // With multiple year weather files (or repeating weather files that rollover day),
1627 : // need to set Special Day dates at start of environment or year.
1628 : // Special Days are only projected for one year.
1629 :
1630 : static constexpr std::string_view RoutineName("SetSpecialDayDates: ");
1631 :
1632 : int JDay;
1633 :
1634 9 : bool ErrorsFound = false;
1635 9 : state.dataWeather->SpecialDayTypes = 0;
1636 9 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1637 0 : auto &specialDay = state.dataWeather->SpecialDays(i);
1638 0 : if (specialDay.WthrFile && !state.dataWeather->UseSpecialDays) {
1639 0 : continue;
1640 : }
1641 0 : if (specialDay.dateType <= DateType::MonthDay) {
1642 0 : JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd);
1643 0 : if (specialDay.Duration == 1 && state.dataWeather->Environment(state.dataWeather->Envrn).ApplyWeekendRule) {
1644 0 : if (state.dataWeather->WeekDayTypes(JDay) == static_cast<int>(Sched::DayType::Sunday)) {
1645 : // Sunday, must go to Monday
1646 0 : ++JDay;
1647 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
1648 0 : JDay = 1;
1649 : }
1650 0 : } else if (state.dataWeather->WeekDayTypes(JDay) == (int)Sched::DayType::Saturday) {
1651 0 : ++JDay;
1652 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
1653 0 : JDay = 1;
1654 : }
1655 0 : ++JDay;
1656 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) {
1657 0 : JDay = 1;
1658 : }
1659 : }
1660 : }
1661 0 : General::InvOrdinalDay(JDay, specialDay.ActStMon, specialDay.ActStDay, state.dataWeather->LeapYearAdd);
1662 0 : } else if (specialDay.dateType == DateType::NthDayInMonth) {
1663 0 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1664 0 : if (specialDay.WeekDay < MonWeekDay(specialDay.Month)) {
1665 0 : ThisDay += 7;
1666 : }
1667 0 : ThisDay += 7 * (specialDay.Day - 1);
1668 0 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1669 0 : ShowSevereError(state,
1670 0 : format("{}Special Day Date, Nth Day of Month, not enough Nths, for SpecialDay={}", RoutineName, specialDay.Name));
1671 0 : ErrorsFound = true;
1672 0 : continue;
1673 : }
1674 0 : specialDay.ActStMon = specialDay.Month;
1675 0 : specialDay.ActStDay = ThisDay;
1676 0 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1677 : } else { // LastWeekDayInMonth
1678 0 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1679 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1680 0 : ThisDay += 7;
1681 : }
1682 0 : specialDay.ActStMon = specialDay.Month;
1683 0 : specialDay.ActStDay = ThisDay;
1684 0 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1685 : }
1686 0 : if (state.dataWeather->SpecialDayTypes(JDay) != 0) {
1687 0 : ShowWarningError(
1688 : state,
1689 0 : format("{}Special Day definition ({}) is overwriting previously entered special day period", RoutineName, specialDay.Name));
1690 0 : if (state.dataWeather->UseSpecialDays) {
1691 0 : ShowContinueError(state, "...This could be caused by definitions on the Weather File.");
1692 : }
1693 0 : ShowContinueError(state, "...This could be caused by duplicate definitions in the Input File.");
1694 : }
1695 0 : int JDay1 = JDay - 1;
1696 0 : for (int j = 0; j <= specialDay.Duration - 1; ++j) {
1697 0 : ++JDay1;
1698 0 : if (JDay1 == 366 && state.dataWeather->LeapYearAdd == 0) {
1699 0 : JDay1 = 1;
1700 : }
1701 0 : if (JDay1 == 367) {
1702 0 : JDay1 = 1;
1703 : }
1704 0 : state.dataWeather->SpecialDayTypes(JDay1) = specialDay.DayType;
1705 : }
1706 : }
1707 :
1708 9 : if (ErrorsFound) {
1709 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1710 : }
1711 9 : }
1712 :
1713 326115 : void InitializeWeather(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
1714 : {
1715 :
1716 : // SUBROUTINE INFORMATION:
1717 : // AUTHOR Rick Strand
1718 : // DATE WRITTEN June 1997
1719 :
1720 : // PURPOSE OF THIS SUBROUTINE:
1721 : // This subroutine is the main driver of the weather initializations.
1722 : // Most of the weather handling can be described as "initializations"
1723 : // so most of the work is done via this subroutine.
1724 :
1725 326115 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->FirstCall) {
1726 :
1727 103 : state.dataWeather->FirstCall = false;
1728 103 : state.dataEnvrn->EndMonthFlag = false;
1729 :
1730 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
1731 :
1732 326115 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
1733 326115 : if (state.dataGlobal->BeginEnvrnFlag) {
1734 :
1735 : // Call and setup the Design Day environment
1736 538 : if (envCurr.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
1737 518 : if (envCurr.DesignDayNum > 0) {
1738 516 : SetUpDesignDay(state, envCurr.DesignDayNum);
1739 516 : state.dataEnvrn->EnvironmentName = envCurr.Title;
1740 : }
1741 : }
1742 :
1743 : // Only used in Weather file environments
1744 : // Start over missing values with each environment
1745 538 : state.dataWeather->wvarsMissing.OutBaroPress = state.dataEnvrn->StdBaroPress; // Initial "missing" value
1746 538 : state.dataWeather->wvarsMissing.OutDryBulbTemp = 6.0; // Initial "missing" value
1747 538 : state.dataWeather->wvarsMissing.OutDewPointTemp = 3.0; // Initial "missing" value
1748 538 : state.dataWeather->wvarsMissing.OutRelHum = 50.0; // Initial "missing" value
1749 538 : state.dataWeather->wvarsMissing.WindSpeed = 2.5; // Initial "missing" value
1750 538 : state.dataWeather->wvarsMissing.WindDir = 180; // Initial "missing" value
1751 538 : state.dataWeather->wvarsMissing.TotalSkyCover = 5; // Initial "missing" value
1752 538 : state.dataWeather->wvarsMissing.OpaqueSkyCover = 5; // Initial "missing" value
1753 538 : state.dataWeather->wvarsMissing.Visibility = 777.7; // Initial "missing" value
1754 538 : state.dataWeather->wvarsMissing.Ceiling = 77777; // Initial "missing" value
1755 538 : state.dataWeather->wvarsMissing.AerOptDepth = 0.0; // Initial "missing" value
1756 538 : state.dataWeather->wvarsMissing.SnowDepth = 0; // Initial "missing" value
1757 538 : state.dataWeather->wvarsMissing.DaysLastSnow = 88; // Initial "missing" value
1758 538 : state.dataWeather->wvarsMissing.Albedo = 0.0; // Initial "missing" value
1759 538 : state.dataWeather->wvarsMissing.LiquidPrecip = 0.0; // Initial "missing" value
1760 : // Counts set to 0 for each environment
1761 538 : state.dataWeather->wvarsMissedCounts = Weather::WeatherVarCounts();
1762 :
1763 : // Counts set to 0 for each environment
1764 538 : state.dataWeather->wvarsOutOfRangeCounts = Weather::WeatherVarCounts();
1765 :
1766 538 : state.dataWeather->IsRainThreshold = 0.8 / double(state.dataGlobal->TimeStepsInHour); // [mm]
1767 :
1768 538 : if (!state.dataWeather->RPReadAllWeatherData) {
1769 536 : printEnvrnStamp = true; // Set this to true so that on first non-warmup day (only) the environment header will print out
1770 : }
1771 :
1772 538 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1773 0 : state.dataWeather->SpecialDays(i).Used = false;
1774 : }
1775 :
1776 560 : if ((state.dataGlobal->KindOfSim != Constant::KindOfSim::DesignDay) &&
1777 22 : (state.dataGlobal->KindOfSim != Constant::KindOfSim::HVACSizeDesignDay)) {
1778 22 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read first day's weather
1779 : } else {
1780 516 : state.dataWeather->TomorrowVariables = state.dataWeather->DesignDay(envCurr.DesignDayNum);
1781 : }
1782 :
1783 : } // ... end of DataGlobals::BeginEnvrnFlag IF-THEN block.
1784 :
1785 326115 : if (state.dataGlobal->BeginDayFlag) {
1786 :
1787 : // Check Holidays, Daylight Saving Time, Ground Temperatures, etc.
1788 :
1789 3083 : UpdateWeatherData(state); // Update daily weather info
1790 :
1791 : // Read tomorrow's weather only if necessary. This means that the
1792 : // simulation is out of warmup, is using a weather tape for this
1793 : // environment, and is not on the last day (day after last day is
1794 : // assumed to be equal to last day).
1795 :
1796 : // Following code checks whether the present day of simulation matches the start month and start day.
1797 : // In a multi year simulation with run period less than 365, we need to position the weather line
1798 : // appropriately.
1799 :
1800 4084 : if ((!state.dataGlobal->WarmupFlag) &&
1801 1001 : ((envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) && (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay))) {
1802 730 : if (state.dataGlobal->DayOfSim < state.dataGlobal->NumOfDayInEnvrn) {
1803 728 : if (state.dataGlobal->DayOfSim == state.dataWeather->curSimDayForEndOfRunPeriod) {
1804 1 : state.dataWeather->curSimDayForEndOfRunPeriod += envCurr.RawSimDays;
1805 1 : if (state.dataWeather->StartDatesCycleShouldBeReset) {
1806 0 : ResetWeekDaysByMonth(state,
1807 0 : envCurr.MonWeekDay,
1808 0 : state.dataWeather->LeapYearAdd,
1809 : envCurr.StartMonth,
1810 : envCurr.StartDay,
1811 : envCurr.EndMonth,
1812 : envCurr.EndDay,
1813 0 : envCurr.RollDayTypeOnRepeat);
1814 0 : if (state.dataWeather->DaylightSavingIsActive) {
1815 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1816 : }
1817 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1818 : }
1819 1 : ++state.dataWeather->YearOfSim;
1820 1 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1821 : } else {
1822 727 : ReadWeatherForDay(state, state.dataGlobal->DayOfSim + 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1823 : }
1824 : }
1825 : }
1826 :
1827 3083 : state.dataEnvrn->EndYearFlag = false;
1828 3083 : if (state.dataEnvrn->DayOfMonth == state.dataWeather->EndDayOfMonthWithLeapDay(state.dataEnvrn->Month)) {
1829 24 : state.dataEnvrn->EndMonthFlag = true;
1830 24 : state.dataEnvrn->EndYearFlag = (state.dataEnvrn->Month == 12);
1831 : }
1832 :
1833 : // Set Tomorrow's date data
1834 3083 : state.dataEnvrn->MonthTomorrow = state.dataWeather->TomorrowVariables.Month;
1835 3083 : state.dataEnvrn->DayOfMonthTomorrow = state.dataWeather->TomorrowVariables.DayOfMonth;
1836 3083 : state.dataEnvrn->DayOfWeekTomorrow = state.dataWeather->TomorrowVariables.DayOfWeek;
1837 3083 : state.dataEnvrn->HolidayIndexTomorrow = state.dataWeather->TomorrowVariables.HolidayIndex;
1838 3083 : state.dataEnvrn->YearTomorrow = state.dataWeather->TomorrowVariables.Year;
1839 :
1840 3083 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1841 20 : if (state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1 && envCurr.ActualWeather) {
1842 0 : if (state.dataWeather->DatesShouldBeReset) {
1843 0 : if (envCurr.TreatYearsAsConsecutive) {
1844 0 : ++envCurr.CurrentYear;
1845 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1846 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1847 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1848 :
1849 : // need to reset MonWeekDay and WeekDayTypes
1850 0 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1851 0 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1852 0 : if (!envCurr.ActualWeather) {
1853 0 : state.dataWeather->curSimDayForEndOfRunPeriod =
1854 0 : state.dataGlobal->DayOfSim + envCurr.RawSimDays + state.dataWeather->LeapYearAdd - 1;
1855 : }
1856 :
1857 : {
1858 0 : int i = JDay5Start;
1859 0 : int TWeekDay = state.dataEnvrn->DayOfWeek;
1860 : while (true) {
1861 0 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1862 0 : TWeekDay = mod(TWeekDay, 7) + 1;
1863 0 : ++i;
1864 0 : if (i > 366) {
1865 0 : i = 1;
1866 : }
1867 0 : if (i == JDay5End) {
1868 0 : break;
1869 : }
1870 : }
1871 : }
1872 0 : ResetWeekDaysByMonth(state,
1873 0 : envCurr.MonWeekDay,
1874 0 : state.dataWeather->LeapYearAdd,
1875 : envCurr.StartMonth,
1876 : envCurr.StartDay,
1877 : envCurr.EndMonth,
1878 : envCurr.EndDay,
1879 0 : envCurr.RollDayTypeOnRepeat);
1880 0 : if (state.dataWeather->DaylightSavingIsActive) {
1881 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1882 : }
1883 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1884 : }
1885 : }
1886 20 : } else if ((state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1) && state.dataWeather->DatesShouldBeReset &&
1887 0 : (state.dataWeather->Jan1DatesShouldBeReset)) {
1888 0 : if (envCurr.TreatYearsAsConsecutive) {
1889 0 : ++envCurr.CurrentYear;
1890 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1891 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1892 0 : if (state.dataEnvrn->CurrentYearIsLeapYear && !state.dataWeather->WFAllowsLeapYears) {
1893 0 : state.dataEnvrn->CurrentYearIsLeapYear = false;
1894 : }
1895 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod && state.dataEnvrn->CurrentYearIsLeapYear) {
1896 0 : ++state.dataWeather->curSimDayForEndOfRunPeriod;
1897 : }
1898 : }
1899 :
1900 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1901 :
1902 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod) {
1903 0 : ResetWeekDaysByMonth(state,
1904 0 : envCurr.MonWeekDay,
1905 0 : state.dataWeather->LeapYearAdd,
1906 : envCurr.StartMonth,
1907 : envCurr.StartDay,
1908 : envCurr.EndMonth,
1909 : envCurr.EndDay,
1910 0 : envCurr.RollDayTypeOnRepeat,
1911 0 : envCurr.RollDayTypeOnRepeat || state.dataEnvrn->CurrentYearIsLeapYear);
1912 0 : if (state.dataWeather->DaylightSavingIsActive) {
1913 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1914 : }
1915 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1916 : }
1917 : }
1918 : }
1919 :
1920 : // at the end of each day find the min/max weather used for DOAS sizing
1921 3083 : if (state.dataGlobal->AirLoopHVACDOASUsedInSim) {
1922 68 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign || envCurr.KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1923 1700 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
1924 11424 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
1925 9792 : Real64 Tdb = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDryBulbTemp;
1926 9792 : Real64 Tdp = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDewPointTemp;
1927 9792 : if (Tdb > envCurr.maxCoolingOATSizing) {
1928 84 : envCurr.maxCoolingOATSizing = Tdb;
1929 84 : envCurr.maxCoolingOADPSizing = Tdp;
1930 : }
1931 9792 : if (Tdb < envCurr.minHeatingOATSizing) {
1932 62 : envCurr.minHeatingOATSizing = Tdb;
1933 62 : envCurr.minHeatingOADPSizing = Tdp;
1934 : }
1935 : } // for (iTS)
1936 : } // for (iHr)
1937 : }
1938 : }
1939 :
1940 : } // ... end of DataGlobals::BeginDayFlag IF-THEN block.
1941 :
1942 649147 : if (!state.dataGlobal->BeginDayFlag && !state.dataGlobal->WarmupFlag &&
1943 103763 : (state.dataEnvrn->Month != envCurr.StartMonth || state.dataEnvrn->DayOfMonth != envCurr.StartDay) &&
1944 649147 : !state.dataWeather->DatesShouldBeReset && envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1945 0 : state.dataWeather->DatesShouldBeReset = true;
1946 : }
1947 :
1948 326137 : if (state.dataGlobal->EndEnvrnFlag && (envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
1949 22 : (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
1950 22 : state.files.inputWeatherFile.rewind();
1951 22 : SkipEPlusWFHeader(state);
1952 22 : ReportMissing_RangeData(state);
1953 : }
1954 :
1955 : // set the EndDesignDayEnvrnsFlag (dataGlobal)
1956 : // True at the end of the last design day environment (last time step of last hour of last day of environ which is a design day)
1957 326115 : state.dataGlobal->EndDesignDayEnvrnsFlag = false;
1958 326115 : if (state.dataGlobal->EndEnvrnFlag) {
1959 535 : if (state.dataWeather->Envrn < state.dataWeather->NumOfEnvrn) {
1960 341 : if (envCurr.KindOfEnvrn != state.dataWeather->Environment(state.dataWeather->Envrn + 1).KindOfEnvrn) {
1961 116 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1962 : }
1963 : } else {
1964 : // if the last environment set the flag to true.
1965 194 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1966 : }
1967 : }
1968 :
1969 326115 : if (state.dataWeather->WaterMainsParameterReport) {
1970 : // this is done only once
1971 103 : if (state.dataWeather->WaterMainsTempsMethod == WaterMainsTempCalcMethod::CorrelationFromWeatherFile) {
1972 0 : if (!state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
1973 0 : state.dataWeather->OADryBulbAverage.CalcAnnualAndMonthlyDryBulbTemp(state);
1974 : }
1975 : }
1976 : // reports to eio file
1977 103 : ReportWaterMainsTempParameters(state);
1978 103 : state.dataWeather->WaterMainsParameterReport = false;
1979 : }
1980 326115 : }
1981 :
1982 3083 : void UpdateWeatherData(EnergyPlusData &state)
1983 : {
1984 :
1985 : // SUBROUTINE INFORMATION:
1986 : // AUTHOR Rick Strand
1987 : // DATE WRITTEN June 1997
1988 :
1989 : // PURPOSE OF THIS SUBROUTINE:
1990 : // This subroutine updates all of the daily weather data in the local
1991 : // module level variables and the global variables.
1992 : // This subroutine will temporarily transfer the weather data for the
1993 : // current day to the old data structure contained in envdat.inc until
1994 : // enough reengineering has taken place to eliminate the need for this
1995 : // include.
1996 :
1997 3083 : state.dataWeather->TodayVariables = state.dataWeather->TomorrowVariables; // Transfer Tomorrow's Daily Weather Variables to Today
1998 :
1999 3083 : if (state.dataGlobal->BeginEnvrnFlag) {
2000 538 : state.dataGlobal->PreviousHour = 24;
2001 : }
2002 :
2003 3083 : state.dataWeather->wvarsHrTsToday = state.dataWeather->wvarsHrTsTomorrow; // What a waste
2004 :
2005 : // Update Global Data
2006 :
2007 3083 : state.dataEnvrn->DayOfYear = state.dataWeather->TodayVariables.DayOfYear;
2008 3083 : state.dataEnvrn->Year = state.dataWeather->TodayVariables.Year;
2009 3083 : state.dataEnvrn->Month = state.dataWeather->TodayVariables.Month;
2010 3083 : state.dataEnvrn->DayOfMonth = state.dataWeather->TodayVariables.DayOfMonth;
2011 3083 : state.dataEnvrn->DayOfWeek = state.dataWeather->TodayVariables.DayOfWeek;
2012 3083 : state.dataEnvrn->HolidayIndex = state.dataWeather->TodayVariables.HolidayIndex;
2013 3083 : if (state.dataEnvrn->HolidayIndex > 0) {
2014 2333 : state.dataWeather->RptDayType = state.dataEnvrn->HolidayIndex;
2015 : } else {
2016 750 : state.dataWeather->RptDayType = state.dataEnvrn->DayOfWeek;
2017 : }
2018 3083 : state.dataEnvrn->DSTIndicator = state.dataWeather->TodayVariables.DaylightSavingIndex;
2019 3083 : state.dataEnvrn->EquationOfTime = state.dataWeather->TodayVariables.EquationOfTime;
2020 3083 : state.dataEnvrn->CosSolarDeclinAngle = state.dataWeather->TodayVariables.CosSolarDeclinAngle;
2021 3083 : state.dataEnvrn->SinSolarDeclinAngle = state.dataWeather->TodayVariables.SinSolarDeclinAngle;
2022 3083 : }
2023 :
2024 326120 : void SetCurrentWeather(EnergyPlusData &state)
2025 : {
2026 :
2027 : // SUBROUTINE INFORMATION:
2028 : // AUTHOR Russ Taylor
2029 : // DATE WRITTEN March 1990
2030 : // MODIFIED Aug94 (LKL) Fixed improper weighting
2031 : // Nov98 (FCW) Added call to get exterior illuminances
2032 : // Jan02 (FCW) Changed how ground reflectance for daylighting is set
2033 : // Mar12 (LKL) Changed settings for leap years/ current years.
2034 : // RE-ENGINEERED Apr97,May97 (RKS)
2035 :
2036 : // PURPOSE OF THIS SUBROUTINE:
2037 : // The purpose of this subroutine is to interpolate the hourly
2038 : // environment data for the sub-hourly time steps in EnergyPlus. In
2039 : // other words, this subroutine puts the current weather conditions
2040 : // into the proper variables. Rather than using the same data for
2041 : // each time step, environment data is interpolated as a continuum
2042 : // throughout the day.
2043 :
2044 : // METHODOLOGY EMPLOYED:
2045 : // The current hour (DataGlobals::HourOfDay) as well as the next hour are used
2046 : // to come up with environment data per time step interval. Method
2047 : // used is to assign a weighting for the current hour's data and
2048 : // (1-that weighting) to the next hour's data. Actual method is: if
2049 : // the current time step is 15 minutes into hour, the interpolated dry
2050 : // bulb temperature should be 3/4*dry bulb temperature of current hour
2051 : // and 1/4*dry bulb temperature of next environment hourly data. At
2052 : // day boundary (current hour = 24), the next hour is hour 1 of next
2053 : // weather data day (Tomorrow%).
2054 :
2055 : static constexpr std::string_view RoutineName("SetCurrentWeather");
2056 :
2057 326120 : state.dataWeather->NextHour = state.dataGlobal->HourOfDay + 1;
2058 :
2059 326120 : if (state.dataGlobal->HourOfDay == 24) { // Should investigate whether EndDayFlag is always set here and use that instead
2060 13821 : state.dataWeather->NextHour = 1;
2061 : }
2062 :
2063 326120 : if (state.dataGlobal->HourOfDay == 1) { // Should investigate whether DataGlobals::BeginDayFlag is always set here and use that instead
2064 14085 : state.dataEnvrn->DayOfYear_Schedule = General::OrdinalDay(state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, 1);
2065 : }
2066 :
2067 326120 : Sched::UpdateScheduleVals(state);
2068 :
2069 326120 : state.dataEnvrn->CurMnDyHr =
2070 652240 : format("{:02d}/{:02d} {:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, (unsigned short)(state.dataGlobal->HourOfDay - 1));
2071 326120 : state.dataEnvrn->CurMnDy = format("{:02d}/{:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth);
2072 326120 : state.dataEnvrn->CurMnDyYr =
2073 652240 : format("{:02d}/{:02d}/{:04d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->CalendarYear);
2074 :
2075 326120 : state.dataGlobal->WeightNow = state.dataWeather->Interpolation(state.dataGlobal->TimeStep);
2076 326120 : state.dataGlobal->WeightPreviousHour = 1.0 - state.dataGlobal->WeightNow;
2077 :
2078 326120 : state.dataGlobal->CurrentTime = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * (state.dataWeather->TimeStepFraction);
2079 326120 : state.dataGlobal->SimTimeSteps = (state.dataGlobal->DayOfSim - 1) * 24 * state.dataGlobal->TimeStepsInHour +
2080 326120 : (state.dataGlobal->HourOfDay - 1) * state.dataGlobal->TimeStepsInHour + state.dataGlobal->TimeStep;
2081 :
2082 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] =
2083 326120 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2084 326120 : state.dataEnvrn->GroundTempKelvin = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] + Constant::Kelvin;
2085 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod] =
2086 326120 : state.dataWeather->siteFCFactorMethodGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2087 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow] =
2088 326120 : state.dataWeather->siteShallowGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2089 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep] =
2090 326120 : state.dataWeather->siteDeepGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2091 326120 : state.dataEnvrn->GndReflectance = state.dataWeather->GroundReflectances(state.dataEnvrn->Month);
2092 326120 : state.dataEnvrn->GndReflectanceForDayltg = state.dataEnvrn->GndReflectance;
2093 :
2094 326120 : CalcWaterMainsTemp(state);
2095 :
2096 : // Determine if Sun is up or down, set Solar Cosine values for time step.
2097 326120 : DetermineSunUpDown(state, state.dataEnvrn->SOLCOS);
2098 326120 : if (state.dataEnvrn->SunIsUp && state.dataWeather->SolarAltitudeAngle < 0.0) {
2099 0 : ShowFatalError(state, format("SetCurrentWeather: At {} Sun is Up but Solar Altitude Angle is < 0.0", state.dataEnvrn->CurMnDyHr));
2100 : }
2101 :
2102 326120 : auto const &today = state.dataWeather->wvarsHrTsToday(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2103 326120 : state.dataEnvrn->OutDryBulbTemp = today.OutDryBulbTemp;
2104 326120 : if (state.dataEnvrn->EMSOutDryBulbOverrideOn) {
2105 2 : state.dataEnvrn->OutDryBulbTemp = state.dataEnvrn->EMSOutDryBulbOverrideValue;
2106 : }
2107 326120 : state.dataEnvrn->OutBaroPress = today.OutBaroPress;
2108 326120 : state.dataEnvrn->OutDewPointTemp = today.OutDewPointTemp;
2109 326120 : if (state.dataEnvrn->EMSOutDewPointTempOverrideOn) {
2110 5760 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->EMSOutDewPointTempOverrideValue;
2111 : }
2112 326120 : state.dataEnvrn->OutRelHum = today.OutRelHum;
2113 326120 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->OutRelHum / 100.0;
2114 326120 : if (state.dataEnvrn->EMSOutRelHumOverrideOn) {
2115 2 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->EMSOutRelHumOverrideValue / 100.0;
2116 2 : state.dataEnvrn->OutRelHum = state.dataEnvrn->EMSOutRelHumOverrideValue;
2117 : }
2118 :
2119 : // Humidity Ratio and Wet Bulb are derived
2120 326120 : state.dataEnvrn->OutHumRat = Psychrometrics::PsyWFnTdbRhPb(
2121 326120 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutRelHumValue, state.dataEnvrn->OutBaroPress, RoutineName);
2122 652240 : state.dataEnvrn->OutWetBulbTemp =
2123 326120 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat, state.dataEnvrn->OutBaroPress);
2124 326120 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
2125 29874 : state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2126 59748 : Real64 TempVal = Psychrometrics::PsyWFnTdbTwbPb(
2127 29874 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutBaroPress);
2128 29874 : state.dataEnvrn->OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, TempVal, state.dataEnvrn->OutBaroPress);
2129 : }
2130 :
2131 326120 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
2132 55473 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2133 : }
2134 :
2135 396260 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) ||
2136 70140 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay)) {
2137 :
2138 751458 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2139 495478 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2140 : }
2141 :
2142 255980 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2143 255980 : int const envrnDayNum = envCurr.DesignDayNum;
2144 255980 : auto const &desDayInput = state.dataWeather->DesDayInput(envrnDayNum);
2145 255980 : auto &spSiteSchedule = state.dataWeather->spSiteSchedules(envrnDayNum);
2146 255980 : auto const &desDayMod = state.dataWeather->desDayMods(envrnDayNum)(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2147 :
2148 255980 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Default) {
2149 0 : spSiteSchedule.OutDryBulbTemp = desDayMod.OutDryBulbTemp;
2150 : }
2151 :
2152 255980 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
2153 255977 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
2154 3 : spSiteSchedule.OutRelHum = desDayMod.OutRelHum;
2155 : }
2156 255980 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
2157 0 : spSiteSchedule.BeamSolarRad = desDayMod.BeamSolarRad;
2158 0 : spSiteSchedule.DifSolarRad = desDayMod.DifSolarRad;
2159 : }
2160 :
2161 255980 : if (envCurr.skyTempModel == SkyTempModel::ScheduleValue || envCurr.skyTempModel == SkyTempModel::DryBulbDelta ||
2162 255980 : envCurr.skyTempModel == SkyTempModel::DewPointDelta) {
2163 0 : spSiteSchedule.SkyTemp = desDayMod.SkyTemp;
2164 : }
2165 70140 : } else if (state.dataEnvrn->TotDesDays > 0) {
2166 105291 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2167 70194 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2168 : }
2169 : }
2170 :
2171 326120 : state.dataEnvrn->WindSpeed = today.WindSpeed;
2172 326120 : if (state.dataEnvrn->EMSWindSpeedOverrideOn) {
2173 2 : state.dataEnvrn->WindSpeed = state.dataEnvrn->EMSWindSpeedOverrideValue;
2174 : }
2175 326120 : state.dataEnvrn->WindDir = today.WindDir;
2176 326120 : if (state.dataEnvrn->EMSWindDirOverrideOn) {
2177 2 : state.dataEnvrn->WindDir = state.dataEnvrn->EMSWindDirOverrideValue;
2178 : }
2179 326120 : state.dataWeather->HorizIRSky = today.HorizIRSky;
2180 326120 : state.dataEnvrn->SkyTemp = today.SkyTemp;
2181 326120 : state.dataEnvrn->SkyTempKelvin = state.dataEnvrn->SkyTemp + Constant::Kelvin;
2182 326120 : state.dataEnvrn->DifSolarRad = today.DifSolarRad;
2183 326120 : if (state.dataEnvrn->EMSDifSolarRadOverrideOn) {
2184 2 : state.dataEnvrn->DifSolarRad = state.dataEnvrn->EMSDifSolarRadOverrideValue;
2185 : }
2186 326120 : state.dataEnvrn->BeamSolarRad = today.BeamSolarRad;
2187 326120 : if (state.dataEnvrn->EMSBeamSolarRadOverrideOn) {
2188 2 : state.dataEnvrn->BeamSolarRad = state.dataEnvrn->EMSBeamSolarRadOverrideValue;
2189 : }
2190 326120 : state.dataEnvrn->LiquidPrecipitation = today.LiquidPrecip / 1000.0; // convert from mm to m
2191 326120 : if ((state.dataEnvrn->RunPeriodEnvironment) && (!state.dataGlobal->WarmupFlag)) {
2192 35042 : int month = state.dataEnvrn->Month;
2193 35042 : state.dataWaterData->RainFall.MonthlyTotalPrecInWeather.at(month - 1) += state.dataEnvrn->LiquidPrecipitation * 1000.0;
2194 35042 : if ((state.dataEnvrn->LiquidPrecipitation > 0) && (state.dataGlobal->TimeStep == 1)) {
2195 43 : state.dataWaterData->RainFall.numRainyHoursInWeather.at(month - 1) += 1;
2196 : }
2197 : }
2198 :
2199 326120 : WaterManager::UpdatePrecipitation(state);
2200 :
2201 326120 : state.dataEnvrn->TotalCloudCover = today.TotalSkyCover;
2202 326120 : state.dataEnvrn->OpaqueCloudCover = today.OpaqueSkyCover;
2203 :
2204 326120 : if (state.dataWeather->UseRainValues) {
2205 : // It is set as LiquidPrecipitation >= .8 mm here: state.dataWeather->TomorrowLiquidPrecip(ts, hour) >=
2206 : // state.dataWeather->IsRainThreshold;
2207 326120 : state.dataEnvrn->IsRain = today.IsRain;
2208 326120 : if (state.dataWaterData->RainFall.ModeID == DataWater::RainfallMode::RainSchedDesign && state.dataEnvrn->RunPeriodEnvironment) {
2209 : // CurrentAmount unit: m
2210 1 : state.dataEnvrn->IsRain = state.dataWaterData->RainFall.CurrentAmount >= (state.dataWeather->IsRainThreshold / 1000.0);
2211 : }
2212 : } else {
2213 0 : state.dataEnvrn->IsRain = false;
2214 : }
2215 326120 : if (state.dataWeather->UseSnowValues) {
2216 326120 : state.dataEnvrn->IsSnow = today.IsSnow;
2217 : } else {
2218 0 : state.dataEnvrn->IsSnow = false;
2219 : }
2220 :
2221 326120 : if (state.dataEnvrn->IsSnow) {
2222 0 : state.dataEnvrn->GndReflectance = max(min(state.dataEnvrn->GndReflectance * state.dataWeather->SnowGndRefModifier, 1.0), 0.0);
2223 0 : state.dataEnvrn->GndReflectanceForDayltg =
2224 0 : max(min(state.dataEnvrn->GndReflectanceForDayltg * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0);
2225 : }
2226 :
2227 326120 : state.dataEnvrn->GndSolarRad =
2228 326120 : max((state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z + state.dataEnvrn->DifSolarRad) * state.dataEnvrn->GndReflectance, 0.0);
2229 :
2230 326120 : if (!state.dataEnvrn->SunIsUp) {
2231 166429 : state.dataEnvrn->DifSolarRad = 0.0;
2232 166429 : state.dataEnvrn->BeamSolarRad = 0.0;
2233 166429 : state.dataEnvrn->GndSolarRad = 0.0;
2234 : }
2235 :
2236 326120 : state.dataEnvrn->OutEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2237 652240 : state.dataEnvrn->OutAirDensity =
2238 326120 : Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2239 :
2240 326120 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
2241 0 : state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2242 : }
2243 326120 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
2244 0 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2245 : }
2246 :
2247 326120 : DayltgCurrentExtHorizIllum(state);
2248 :
2249 326120 : if (!state.dataEnvrn->IsRain) {
2250 325913 : state.dataWeather->RptIsRain = 0;
2251 : } else {
2252 207 : state.dataWeather->RptIsRain = 1;
2253 : }
2254 :
2255 326120 : if (!state.dataEnvrn->IsSnow) {
2256 326120 : state.dataWeather->RptIsSnow = 0;
2257 : } else {
2258 0 : state.dataWeather->RptIsSnow = 1;
2259 : }
2260 326120 : }
2261 :
2262 755 : void ReadWeatherForDay(EnergyPlusData &state,
2263 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2264 : int const Environ, // Environment being simulated
2265 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2266 : )
2267 : {
2268 :
2269 : // SUBROUTINE INFORMATION:
2270 : // AUTHOR Linda K. Lawrie
2271 : // DATE WRITTEN April 1999
2272 :
2273 : // PURPOSE OF THIS SUBROUTINE:
2274 : // This subroutine is the driving routine behind reading the weather data.
2275 : // Theoretically, several kinds of weather files could be read here. As
2276 : // distributed only EPW files are allowed.
2277 :
2278 755 : ReadEPlusWeatherForDay(state, DayToRead, Environ, BackSpaceAfterRead);
2279 755 : }
2280 :
2281 755 : void ReadEPlusWeatherForDay(EnergyPlusData &state,
2282 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2283 : int const Environ, // Environment being simulated
2284 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2285 : )
2286 : {
2287 :
2288 : // SUBROUTINE INFORMATION:
2289 : // AUTHOR Linda K. Lawrie
2290 : // DATE WRITTEN April 1999
2291 : // MODIFIED March 2012; add actual weather read.
2292 :
2293 : // PURPOSE OF THIS SUBROUTINE:
2294 : // This subroutine reads the appropriate day of EPW weather data.
2295 :
2296 : int WYear;
2297 : int WMonth;
2298 : int WDay;
2299 : int WHour;
2300 : int WMinute;
2301 : Real64 DryBulb;
2302 : Real64 DewPoint;
2303 : Real64 RelHum;
2304 : Real64 AtmPress;
2305 : Real64 ETHoriz;
2306 : Real64 ETDirect;
2307 : Real64 IRHoriz;
2308 : Real64 GLBHoriz;
2309 : Real64 DirectRad;
2310 : Real64 DiffuseRad;
2311 : Real64 GLBHorizIllum;
2312 : Real64 DirectNrmIllum;
2313 : Real64 DiffuseHorizIllum;
2314 : Real64 ZenLum;
2315 : Real64 WindDir;
2316 : Real64 WindSpeed;
2317 : Real64 TotalSkyCover;
2318 : Real64 OpaqueSkyCover;
2319 : Real64 Visibility;
2320 : Real64 CeilHeight;
2321 : Real64 PrecipWater;
2322 : Real64 AerosolOptDepth;
2323 : Real64 SnowDepth;
2324 : Real64 DaysSinceLastSnow;
2325 : Real64 Albedo;
2326 : Real64 LiquidPrecip;
2327 : int PresWeathObs;
2328 755 : Array1D_int PresWeathConds(9);
2329 :
2330 755 : constexpr std::string_view routineName = "ReadEPlusWeatherForDay";
2331 :
2332 755 : Array1D<WeatherVars> wvarsHr = Array1D<WeatherVars>(Constant::iHoursInDay);
2333 :
2334 755 : auto &thisEnviron = state.dataWeather->Environment(Environ);
2335 :
2336 755 : if (DayToRead == 1) {
2337 :
2338 : // Checks whether Weather file contains just one year of data. If yes then rewind and position to first
2339 : // day of weather file. The rest of code appropriately positions to the start day.
2340 :
2341 27 : bool Ready = false;
2342 27 : int NumRewinds = 0;
2343 : // Must position file to proper day
2344 : // File already position to first data record
2345 : // Set Current Day of Week to "start of Data Period"
2346 27 : state.dataWeather->ReadEPlusWeatherCurTime = 1.0 / double(state.dataWeather->NumIntervalsPerHour);
2347 27 : state.dataWeather->CurDayOfWeek = state.dataWeather->DataPeriods(1).WeekDay - 1;
2348 27 : WYear = 0;
2349 27 : WMonth = 0;
2350 27 : WDay = 0;
2351 27 : WHour = 0;
2352 27 : WMinute = 0;
2353 27 : state.dataWeather->LastHourSet = false;
2354 27 : InputFile::ReadResult<std::string> WeatherDataLine{"", true, false};
2355 752 : while (!Ready) {
2356 725 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2357 725 : if (WeatherDataLine.good) {
2358 : bool ErrorFound;
2359 724 : InterpretWeatherDataLine(state,
2360 : WeatherDataLine.data,
2361 : ErrorFound,
2362 : WYear,
2363 : WMonth,
2364 : WDay,
2365 : WHour,
2366 : WMinute,
2367 : DryBulb,
2368 : DewPoint,
2369 : RelHum,
2370 : AtmPress,
2371 : ETHoriz,
2372 : ETDirect,
2373 : IRHoriz,
2374 : GLBHoriz,
2375 : DirectRad,
2376 : DiffuseRad,
2377 : GLBHorizIllum,
2378 : DirectNrmIllum,
2379 : DiffuseHorizIllum,
2380 : ZenLum,
2381 : WindDir,
2382 : WindSpeed,
2383 : TotalSkyCover,
2384 : OpaqueSkyCover,
2385 : Visibility,
2386 : CeilHeight,
2387 : PresWeathObs,
2388 : PresWeathConds,
2389 : PrecipWater,
2390 : AerosolOptDepth,
2391 : SnowDepth,
2392 : DaysSinceLastSnow,
2393 : Albedo,
2394 : LiquidPrecip);
2395 1 : } else if (WeatherDataLine.eof) {
2396 1 : if (NumRewinds > 0) {
2397 0 : std::string date = fmt::to_string(thisEnviron.StartMonth) + '/' + fmt::to_string(thisEnviron.StartDay);
2398 0 : if (thisEnviron.MatchYear) {
2399 0 : date += '/' + fmt::to_string(thisEnviron.StartYear);
2400 : }
2401 0 : ShowSevereError(state, format("Multiple rewinds on EPW while searching for first day {}", date));
2402 0 : } else {
2403 1 : state.files.inputWeatherFile.rewind();
2404 1 : ++NumRewinds;
2405 1 : SkipEPlusWFHeader(state);
2406 1 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2407 : bool ErrorFound;
2408 1 : InterpretWeatherDataLine(state,
2409 : WeatherDataLine.data,
2410 : ErrorFound,
2411 : WYear,
2412 : WMonth,
2413 : WDay,
2414 : WHour,
2415 : WMinute,
2416 : DryBulb,
2417 : DewPoint,
2418 : RelHum,
2419 : AtmPress,
2420 : ETHoriz,
2421 : ETDirect,
2422 : IRHoriz,
2423 : GLBHoriz,
2424 : DirectRad,
2425 : DiffuseRad,
2426 : GLBHorizIllum,
2427 : DirectNrmIllum,
2428 : DiffuseHorizIllum,
2429 : ZenLum,
2430 : WindDir,
2431 : WindSpeed,
2432 : TotalSkyCover,
2433 : OpaqueSkyCover,
2434 : Visibility,
2435 : CeilHeight,
2436 : PresWeathObs,
2437 : PresWeathConds,
2438 : PrecipWater,
2439 : AerosolOptDepth,
2440 : SnowDepth,
2441 : DaysSinceLastSnow,
2442 : Albedo,
2443 : LiquidPrecip);
2444 : }
2445 : }
2446 725 : if (!WeatherDataLine.good) {
2447 0 : ShowFatalError(state,
2448 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2449 : WYear,
2450 : WMonth,
2451 : WDay,
2452 : WHour,
2453 : WMinute,
2454 0 : state.files.inputWeatherFile.error_state_to_string()),
2455 0 : OptionalOutputFileRef{state.files.eso});
2456 : }
2457 725 : if (state.dataWeather->CurDayOfWeek <= 7) {
2458 725 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2459 : }
2460 725 : bool RecordDateMatch =
2461 1423 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && !thisEnviron.MatchYear) ||
2462 698 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && thisEnviron.MatchYear && WYear == thisEnviron.StartYear);
2463 725 : if (RecordDateMatch) {
2464 27 : state.files.inputWeatherFile.backspace();
2465 27 : Ready = true;
2466 27 : if (state.dataWeather->CurDayOfWeek <= 7) {
2467 27 : --state.dataWeather->CurDayOfWeek;
2468 : }
2469 : // Do the range checks on the first set of fields -- no others.
2470 27 : bool ErrorsFound = false;
2471 27 : if (DryBulb < 99.9 && (DryBulb < -90.0 || DryBulb > 70.0)) {
2472 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2473 0 : ShowContinueError(state, format("DryBulb Temperature ({:.2R}) is out of range [-90.0, 70.0]", DryBulb));
2474 0 : ErrorsFound = true;
2475 : }
2476 :
2477 27 : if (DewPoint < 99.9 && (DewPoint < -90.0 || DewPoint > 70.0)) {
2478 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2479 0 : ShowContinueError(state, format("DewPoint Temperature ({:.2R}) is out of range [-90.0, 70.0]", DewPoint));
2480 0 : ErrorsFound = true;
2481 : }
2482 :
2483 27 : if (RelHum < 999.0 && (RelHum < 0.0 || RelHum > 110.0)) {
2484 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2485 0 : ShowContinueError(state, format("Relative Humidity ({:.2R}) is out of range [0.0, 100.0]", RelHum));
2486 0 : ErrorsFound = true;
2487 : }
2488 :
2489 27 : if (AtmPress < 999999.0 && (AtmPress <= 31000.0 || AtmPress > 120000.0)) {
2490 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2491 0 : ShowContinueError(state, format("Atmospheric Pressure ({:.0R}) is out of range [31000, 120000]", AtmPress));
2492 0 : ErrorsFound = true;
2493 : }
2494 :
2495 27 : if (DirectRad < 0.0) {
2496 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2497 0 : ShowContinueError(state, format("Direct Radiation ({:.2R}) is out of range [0.0, -]", DirectRad));
2498 0 : ErrorsFound = true;
2499 : }
2500 :
2501 27 : if (DiffuseRad < 0.0) {
2502 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2503 0 : ShowContinueError(state, format("Diffuse Radiation ({:.2R}) is out of range [0.0, -]", DiffuseRad));
2504 0 : ErrorsFound = true;
2505 : }
2506 :
2507 27 : if (WindDir < 999.0 && (WindDir < 0.0 || WindDir > 360.0)) {
2508 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2509 0 : ShowContinueError(state, format("Wind Direction ({:.2R}) is out of range [0.0, 360.0]", WindDir));
2510 0 : ErrorsFound = true;
2511 : }
2512 :
2513 27 : if (WindSpeed < 999.0 && (WindSpeed < 0.0 || WindSpeed > 40.0)) {
2514 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2515 0 : ShowContinueError(state, format("Wind Speed ({:.2R}) is out of range [0.0, 40.0]", WindSpeed));
2516 0 : ErrorsFound = true;
2517 : }
2518 :
2519 27 : if (ErrorsFound) {
2520 0 : ShowSevereError(state, "Out of Range errors found with initial day of WeatherFile");
2521 : }
2522 : } else {
2523 : // Must skip this day
2524 698 : for (int i = 2; i <= state.dataWeather->NumIntervalsPerHour; ++i) {
2525 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2526 0 : if (!WeatherDataLine.good) {
2527 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2528 0 : ShowFatalError(state,
2529 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2530 : WYear,
2531 : WMonth,
2532 : WDay,
2533 : WHour,
2534 : WMinute,
2535 0 : state.files.inputWeatherFile.error_state_to_string()),
2536 0 : OptionalOutputFileRef{state.files.eso});
2537 : }
2538 : }
2539 16752 : for (int i = 1; i <= 23 * state.dataWeather->NumIntervalsPerHour; ++i) {
2540 16054 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2541 16054 : if (!WeatherDataLine.good) {
2542 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2543 0 : ShowFatalError(state,
2544 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2545 : WYear,
2546 : WMonth,
2547 : WDay,
2548 : WHour,
2549 : WMinute,
2550 0 : state.files.inputWeatherFile.error_state_to_string()),
2551 0 : OptionalOutputFileRef{state.files.eso});
2552 : }
2553 : }
2554 : }
2555 : }
2556 :
2557 27 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2558 : // Why do some things here use state.dataWeather->Envrn and some the parameter Environ?
2559 :
2560 : // Positioned to proper day
2561 36 : if (!state.dataGlobal->KickOffSimulation && !state.dataGlobal->DoingSizing &&
2562 9 : thisEnviron.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
2563 6 : ++thisEnviron.CurrentCycle;
2564 6 : if (!thisEnviron.RollDayTypeOnRepeat) {
2565 0 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2566 0 : if (state.dataWeather->DaylightSavingIsActive) {
2567 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2568 : }
2569 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2570 6 : } else if (thisEnviron.CurrentCycle == 1) {
2571 6 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2572 6 : thisEnviron.SetWeekDays = true;
2573 6 : if (state.dataWeather->DaylightSavingIsActive) {
2574 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2575 : }
2576 6 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2577 : } else {
2578 0 : state.dataWeather->CurDayOfWeek = state.dataEnvrn->DayOfWeekTomorrow;
2579 : }
2580 : } else {
2581 21 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2582 : }
2583 27 : }
2584 :
2585 755 : bool TryAgain = true;
2586 755 : bool SkipThisDay = false;
2587 :
2588 1510 : while (TryAgain) {
2589 :
2590 755 : TryAgain = false;
2591 :
2592 18875 : for (int hour = 1; hour <= 24; ++hour) {
2593 36240 : for (int CurTimeStep = 1; CurTimeStep <= state.dataWeather->NumIntervalsPerHour; ++CurTimeStep) {
2594 18120 : state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour) = WeatherVars();
2595 18120 : auto WeatherDataLine = state.files.inputWeatherFile.readLine();
2596 18120 : if (!WeatherDataLine.good) {
2597 0 : WeatherDataLine.data.clear();
2598 : }
2599 18120 : if (WeatherDataLine.data.empty()) {
2600 0 : if (hour == 1) {
2601 0 : WeatherDataLine.eof = true;
2602 0 : WeatherDataLine.good = false;
2603 : } else {
2604 0 : WeatherDataLine.good = false;
2605 : }
2606 : }
2607 18120 : if (WeatherDataLine.good) {
2608 : bool ErrorFound;
2609 18120 : InterpretWeatherDataLine(state,
2610 : WeatherDataLine.data,
2611 : ErrorFound,
2612 : WYear,
2613 : WMonth,
2614 : WDay,
2615 : WHour,
2616 : WMinute,
2617 : DryBulb,
2618 : DewPoint,
2619 : RelHum,
2620 : AtmPress,
2621 : ETHoriz,
2622 : ETDirect,
2623 : IRHoriz,
2624 : GLBHoriz,
2625 : DirectRad,
2626 : DiffuseRad,
2627 : GLBHorizIllum,
2628 : DirectNrmIllum,
2629 : DiffuseHorizIllum,
2630 : ZenLum,
2631 : WindDir,
2632 : WindSpeed,
2633 : TotalSkyCover,
2634 : OpaqueSkyCover,
2635 : Visibility,
2636 : CeilHeight,
2637 : PresWeathObs,
2638 : PresWeathConds,
2639 : PrecipWater,
2640 : AerosolOptDepth,
2641 : SnowDepth,
2642 : DaysSinceLastSnow,
2643 : Albedo,
2644 : LiquidPrecip);
2645 : } else { // ReadStatus /=0
2646 0 : if (WeatherDataLine.eof &&
2647 0 : state.dataWeather->NumDataPeriods == 1) { // Standard End-of-file, rewind and position to first day...
2648 0 : if (state.dataWeather->DataPeriods(1).NumDays >= state.dataWeather->NumDaysInYear) {
2649 0 : state.files.inputWeatherFile.rewind();
2650 0 : SkipEPlusWFHeader(state);
2651 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2652 : bool ErrorFound;
2653 0 : InterpretWeatherDataLine(state,
2654 : WeatherDataLine.data,
2655 : ErrorFound,
2656 : WYear,
2657 : WMonth,
2658 : WDay,
2659 : WHour,
2660 : WMinute,
2661 : DryBulb,
2662 : DewPoint,
2663 : RelHum,
2664 : AtmPress,
2665 : ETHoriz,
2666 : ETDirect,
2667 : IRHoriz,
2668 : GLBHoriz,
2669 : DirectRad,
2670 : DiffuseRad,
2671 : GLBHorizIllum,
2672 : DirectNrmIllum,
2673 : DiffuseHorizIllum,
2674 : ZenLum,
2675 : WindDir,
2676 : WindSpeed,
2677 : TotalSkyCover,
2678 : OpaqueSkyCover,
2679 : Visibility,
2680 : CeilHeight,
2681 : PresWeathObs,
2682 : PresWeathConds,
2683 : PrecipWater,
2684 : AerosolOptDepth,
2685 : SnowDepth,
2686 : DaysSinceLastSnow,
2687 : Albedo,
2688 : LiquidPrecip);
2689 : } else {
2690 0 : ShowFatalError(state,
2691 0 : format("End-of-File encountered after {}/{}/{} {}:{}, starting from first day of Weather File would "
2692 : "not be \"next day\"",
2693 : WYear,
2694 : WMonth,
2695 : WDay,
2696 : WHour,
2697 : WMinute));
2698 : }
2699 : } else {
2700 0 : ShowFatalError(state,
2701 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2702 : WYear,
2703 : WMonth,
2704 : WDay,
2705 : WHour,
2706 : WMinute),
2707 0 : OptionalOutputFileRef{state.files.eso});
2708 : }
2709 : }
2710 :
2711 18120 : if (hour != WHour) {
2712 0 : ShowFatalError(state,
2713 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2714 : WYear,
2715 : WMonth,
2716 : WDay,
2717 : WHour,
2718 : WMinute),
2719 0 : OptionalOutputFileRef{state.files.eso});
2720 : }
2721 :
2722 : // Set possible missing values
2723 18120 : if (ETHoriz < 0.0) {
2724 0 : ETHoriz = 9999.0;
2725 : }
2726 18120 : if (ETDirect < 0.0) {
2727 0 : ETDirect = 9999.0;
2728 : }
2729 18120 : if (IRHoriz <= 0.0) {
2730 24 : IRHoriz = 9999.0;
2731 : }
2732 18120 : if (GLBHoriz < 0.0) {
2733 0 : GLBHoriz = 9999.0;
2734 : }
2735 18120 : if (state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
2736 0 : if (DirectRad >= 9999.0) {
2737 0 : ++state.dataWeather->wvarsMissedCounts.BeamSolarRad;
2738 : }
2739 0 : if (DiffuseRad >= 9999.0) {
2740 0 : state.dataWeather->wvarsMissedCounts.DifSolarRad = state.dataWeather->wvarsMissedCounts.BeamSolarRad + 1;
2741 : }
2742 0 : if (DirectRad < 0.0) {
2743 0 : DirectRad = 9999.0;
2744 0 : ++state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad;
2745 : }
2746 0 : if (DiffuseRad < 0.0) {
2747 0 : DiffuseRad = 9999.0;
2748 0 : ++state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad;
2749 : }
2750 : }
2751 18120 : if (GLBHorizIllum < 0.0) {
2752 0 : GLBHorizIllum = 999999.0;
2753 : }
2754 18120 : if (DirectNrmIllum < 0.0) {
2755 0 : DirectNrmIllum = 999999.0;
2756 : }
2757 18120 : if (DiffuseHorizIllum < 0.0) {
2758 0 : DiffuseHorizIllum = 999999.0;
2759 : }
2760 18120 : if (ZenLum < 0.0) {
2761 0 : ZenLum = 99999.0;
2762 : }
2763 18120 : if (AtmPress < 0.0) {
2764 0 : AtmPress = 999999.0;
2765 : }
2766 18120 : if (WindSpeed < 0.0) {
2767 0 : WindSpeed = 999.0;
2768 : }
2769 18120 : if (WindDir < -360.0 || WindDir > 360.0) {
2770 0 : WindDir = 999.0;
2771 : }
2772 18120 : if (TotalSkyCover < 0.0) {
2773 0 : TotalSkyCover = 99.0;
2774 : }
2775 18120 : if (RelHum < 0.0) {
2776 0 : RelHum = 999.0;
2777 : }
2778 18120 : if (OpaqueSkyCover < 0.0) {
2779 0 : OpaqueSkyCover = 99.0;
2780 : }
2781 18120 : if (Visibility < 0.0) {
2782 0 : Visibility = 9999.0;
2783 : }
2784 18120 : if (CeilHeight < 0.0) {
2785 0 : CeilHeight = 9999.0;
2786 : }
2787 18120 : if (PresWeathObs < 0) {
2788 0 : PresWeathObs = 9;
2789 : }
2790 18120 : if (PrecipWater < 0.0) {
2791 0 : PrecipWater = 999.0;
2792 : }
2793 18120 : if (AerosolOptDepth < 0.0) {
2794 0 : AerosolOptDepth = 999.0;
2795 : }
2796 18120 : if (SnowDepth < 0.0) {
2797 0 : SnowDepth = 999.0;
2798 : }
2799 18120 : if (DaysSinceLastSnow < 0.0) {
2800 0 : DaysSinceLastSnow = 99.0;
2801 : }
2802 18120 : if (Albedo < 0.0) {
2803 0 : Albedo = 999.0;
2804 : }
2805 18120 : if (LiquidPrecip < 0.0) {
2806 0 : LiquidPrecip = 999.0;
2807 : }
2808 :
2809 18120 : if (hour == 1 && CurTimeStep == 1) {
2810 755 : if (WMonth == 2 && WDay == 29 && (!state.dataEnvrn->CurrentYearIsLeapYear || !state.dataWeather->WFAllowsLeapYears)) {
2811 0 : state.dataWeather->EndDayOfMonth(2) = 28;
2812 0 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = 28;
2813 0 : SkipThisDay = true;
2814 0 : TryAgain = true;
2815 0 : ShowWarningError(state, "ReadEPlusWeatherForDay: Feb29 data encountered but will not be processed.");
2816 0 : if (!state.dataWeather->WFAllowsLeapYears) {
2817 0 : ShowContinueError(
2818 : state, "...WeatherFile does not allow Leap Years. HOLIDAYS/DAYLIGHT SAVINGS header must indicate \"Yes\".");
2819 : }
2820 0 : continue;
2821 : } else {
2822 755 : TryAgain = false;
2823 755 : SkipThisDay = false;
2824 : }
2825 :
2826 755 : if (thisEnviron.ActualWeather && state.dataEnvrn->CurrentYearIsLeapYear) {
2827 0 : if (WMonth == 3 && WDay == 1 && state.dataEnvrn->Month == 2 && state.dataEnvrn->DayOfMonth == 28) {
2828 0 : ShowFatalError(state, "ReadEPlusWeatherForDay: Current year is a leap year, but Feb29 data is missing.");
2829 : }
2830 : }
2831 :
2832 755 : state.dataWeather->TomorrowVariables.Year = WYear;
2833 755 : state.dataWeather->TomorrowVariables.Month = WMonth;
2834 755 : state.dataWeather->TomorrowVariables.DayOfMonth = WDay;
2835 755 : state.dataWeather->TomorrowVariables.DayOfYear = General::OrdinalDay(WMonth, WDay, state.dataWeather->LeapYearAdd);
2836 755 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule = General::OrdinalDay(WMonth, WDay, 1);
2837 : Real64 A;
2838 : Real64 B;
2839 : Real64 C;
2840 : Real64 AVSC;
2841 755 : CalculateDailySolarCoeffs(state,
2842 755 : state.dataWeather->TomorrowVariables.DayOfYear,
2843 : A,
2844 : B,
2845 : C,
2846 : AVSC,
2847 755 : state.dataWeather->TomorrowVariables.EquationOfTime,
2848 755 : state.dataWeather->TomorrowVariables.SinSolarDeclinAngle,
2849 755 : state.dataWeather->TomorrowVariables.CosSolarDeclinAngle);
2850 755 : if (state.dataWeather->CurDayOfWeek <= 7) {
2851 755 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2852 : }
2853 755 : state.dataWeather->TomorrowVariables.DayOfWeek = state.dataWeather->CurDayOfWeek;
2854 1510 : state.dataWeather->TomorrowVariables.DaylightSavingIndex =
2855 755 : state.dataWeather->DSTIndex(state.dataWeather->TomorrowVariables.DayOfYear);
2856 755 : state.dataWeather->TomorrowVariables.HolidayIndex =
2857 755 : state.dataWeather->SpecialDayTypes(state.dataWeather->TomorrowVariables.DayOfYear);
2858 : }
2859 :
2860 18120 : if (SkipThisDay) {
2861 0 : continue;
2862 : }
2863 :
2864 : // Check out missing values
2865 :
2866 18120 : if (DryBulb >= 99.9) {
2867 0 : DryBulb = state.dataWeather->wvarsMissing.OutDryBulbTemp;
2868 0 : ++state.dataWeather->wvarsMissedCounts.OutDryBulbTemp;
2869 : }
2870 18120 : if (DryBulb < -90.0 || DryBulb > 70.0) {
2871 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp;
2872 : }
2873 :
2874 18120 : if (DewPoint >= 99.9) {
2875 0 : DewPoint = state.dataWeather->wvarsMissing.OutDewPointTemp;
2876 0 : ++state.dataWeather->wvarsMissedCounts.OutDewPointTemp;
2877 : }
2878 18120 : if (DewPoint < -90.0 || DewPoint > 70.0) {
2879 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp;
2880 : }
2881 :
2882 18120 : if (RelHum >= 999.0) {
2883 0 : RelHum = state.dataWeather->wvarsMissing.OutRelHum;
2884 0 : ++state.dataWeather->wvarsMissedCounts.OutRelHum;
2885 : }
2886 18120 : if (RelHum < 0.0 || RelHum > 110.0) {
2887 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutRelHum;
2888 : }
2889 :
2890 18120 : if (AtmPress >= 999999.0) {
2891 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2892 0 : ++state.dataWeather->wvarsMissedCounts.OutBaroPress;
2893 : }
2894 18120 : if (AtmPress <= 31000.0 || AtmPress > 120000.0) {
2895 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress;
2896 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2897 : }
2898 :
2899 18120 : if (WindDir >= 999.0) {
2900 0 : WindDir = state.dataWeather->wvarsMissing.WindDir;
2901 0 : ++state.dataWeather->wvarsMissedCounts.WindDir;
2902 : }
2903 18120 : if (WindDir < 0.0 || WindDir > 360.0) {
2904 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindDir;
2905 : }
2906 :
2907 18120 : if (WindSpeed >= 999.0) {
2908 0 : WindSpeed = state.dataWeather->wvarsMissing.WindSpeed;
2909 0 : ++state.dataWeather->wvarsMissedCounts.WindSpeed;
2910 : }
2911 18120 : if (WindSpeed < 0.0 || WindSpeed > 40.0) {
2912 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindSpeed;
2913 : }
2914 :
2915 18120 : if (TotalSkyCover >= 99.0) {
2916 0 : TotalSkyCover = state.dataWeather->wvarsMissing.TotalSkyCover;
2917 0 : ++state.dataWeather->wvarsMissedCounts.TotalSkyCover;
2918 : }
2919 :
2920 18120 : if (OpaqueSkyCover >= 99.0) {
2921 0 : OpaqueSkyCover = state.dataWeather->wvarsMissing.OpaqueSkyCover;
2922 0 : ++state.dataWeather->wvarsMissedCounts.OpaqueSkyCover;
2923 : }
2924 :
2925 18120 : if (SnowDepth >= 999.0) {
2926 0 : SnowDepth = state.dataWeather->wvarsMissing.SnowDepth;
2927 0 : ++state.dataWeather->wvarsMissedCounts.SnowDepth;
2928 : }
2929 :
2930 18120 : if (Albedo >= 999.0) {
2931 16608 : Albedo = state.dataWeather->wvarsMissing.Albedo;
2932 16608 : ++state.dataWeather->wvarsMissedCounts.Albedo;
2933 : }
2934 :
2935 18120 : if (LiquidPrecip >= 999.0) {
2936 16641 : LiquidPrecip = state.dataWeather->wvarsMissing.LiquidPrecip;
2937 16641 : ++state.dataWeather->wvarsMissedCounts.LiquidPrecip;
2938 : }
2939 :
2940 18120 : auto &tomorrow = state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour);
2941 18120 : tomorrow.OutDryBulbTemp = DryBulb;
2942 18120 : tomorrow.OutDewPointTemp = DewPoint;
2943 18120 : tomorrow.OutBaroPress = AtmPress;
2944 18120 : tomorrow.OutRelHum = RelHum;
2945 18120 : RelHum *= 0.01;
2946 18120 : tomorrow.WindSpeed = WindSpeed;
2947 18120 : tomorrow.WindDir = WindDir;
2948 18120 : tomorrow.LiquidPrecip = LiquidPrecip;
2949 18120 : tomorrow.TotalSkyCover = TotalSkyCover;
2950 18120 : tomorrow.OpaqueSkyCover = OpaqueSkyCover;
2951 :
2952 18120 : calcSky(state, tomorrow.HorizIRSky, tomorrow.SkyTemp, OpaqueSkyCover, DryBulb, DewPoint, RelHum, IRHoriz);
2953 :
2954 18120 : if (ETHoriz >= 9999.0) {
2955 0 : ETHoriz = 0.0;
2956 : }
2957 18120 : if (ETDirect >= 9999.0) {
2958 0 : ETDirect = 0.0;
2959 : }
2960 18120 : if (GLBHoriz >= 9999.0) {
2961 0 : GLBHoriz = 0.0;
2962 : }
2963 18120 : if (DirectRad >= 9999.0) {
2964 0 : DirectRad = 0.0;
2965 : }
2966 18120 : if (DiffuseRad >= 9999.0) {
2967 0 : DiffuseRad = 0.0;
2968 : }
2969 18120 : if (GLBHorizIllum >= 999900.0) {
2970 0 : GLBHorizIllum = 0.0;
2971 : }
2972 18120 : if (DirectNrmIllum >= 999900.0) {
2973 0 : DirectNrmIllum = 0.0;
2974 : }
2975 18120 : if (DiffuseHorizIllum >= 999900.0) {
2976 0 : DiffuseHorizIllum = 0.0;
2977 : }
2978 18120 : if (ZenLum >= 99990.0) {
2979 0 : ZenLum = 0.0;
2980 : }
2981 18120 : if (state.dataEnvrn->IgnoreSolarRadiation) {
2982 0 : GLBHoriz = 0.0;
2983 0 : DirectRad = 0.0;
2984 0 : DiffuseRad = 0.0;
2985 : }
2986 18120 : if (state.dataEnvrn->IgnoreBeamRadiation) {
2987 0 : DirectRad = 0.0;
2988 : }
2989 18120 : if (state.dataEnvrn->IgnoreDiffuseRadiation) {
2990 0 : DiffuseRad = 0.0;
2991 : }
2992 :
2993 18120 : tomorrow.BeamSolarRad = DirectRad;
2994 18120 : tomorrow.DifSolarRad = DiffuseRad;
2995 :
2996 18120 : tomorrow.IsRain = false;
2997 18120 : if (PresWeathObs == 0) {
2998 0 : if (PresWeathConds(1) < 9 || PresWeathConds(2) < 9 || PresWeathConds(3) < 9) {
2999 0 : tomorrow.IsRain = true;
3000 : }
3001 : } else {
3002 18120 : tomorrow.IsRain = false;
3003 : }
3004 18120 : tomorrow.IsSnow = (SnowDepth > 0.0);
3005 :
3006 : // default if rain but none on weather file
3007 18120 : if (tomorrow.IsRain && tomorrow.LiquidPrecip == 0.0) {
3008 0 : tomorrow.LiquidPrecip = 2.0; // 2mm in an hour ~ .08 inch
3009 : }
3010 :
3011 18120 : state.dataWeather->wvarsMissing.OutDryBulbTemp = DryBulb;
3012 18120 : state.dataWeather->wvarsMissing.OutDewPointTemp = DewPoint;
3013 18120 : state.dataWeather->wvarsMissing.OutRelHum = static_cast<int>(std::round(RelHum * 100.0));
3014 18120 : state.dataWeather->wvarsMissing.OutBaroPress = AtmPress;
3015 18120 : state.dataWeather->wvarsMissing.WindDir = WindDir;
3016 18120 : state.dataWeather->wvarsMissing.WindSpeed = WindSpeed;
3017 18120 : state.dataWeather->wvarsMissing.TotalSkyCover = TotalSkyCover;
3018 18120 : state.dataWeather->wvarsMissing.OpaqueSkyCover = OpaqueSkyCover;
3019 18120 : state.dataWeather->wvarsMissing.Visibility = Visibility;
3020 18120 : state.dataWeather->wvarsMissing.Ceiling = CeilHeight;
3021 18120 : state.dataWeather->wvarsMissing.WaterPrecip = PrecipWater;
3022 18120 : state.dataWeather->wvarsMissing.AerOptDepth = AerosolOptDepth;
3023 18120 : state.dataWeather->wvarsMissing.SnowDepth = SnowDepth;
3024 18120 : state.dataWeather->wvarsMissing.DaysLastSnow = DaysSinceLastSnow;
3025 18120 : state.dataWeather->wvarsMissing.Albedo = Albedo;
3026 :
3027 18120 : } // for (CurTimeStep)
3028 :
3029 : } // for (Hour)
3030 :
3031 : } // Try Again While Loop
3032 :
3033 755 : if (BackSpaceAfterRead) {
3034 4 : state.files.inputWeatherFile.backspace();
3035 : }
3036 :
3037 755 : if (state.dataWeather->NumIntervalsPerHour == 1 && state.dataGlobal->TimeStepsInHour > 1) {
3038 : // Create interpolated weather for timestep orientation
3039 : // First copy ts=1 (hourly) from data arrays to Wthr structure
3040 18850 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3041 18096 : wvarsHr(hour) = state.dataWeather->wvarsHrTsTomorrow(1, hour);
3042 : }
3043 :
3044 754 : if (!state.dataWeather->LastHourSet) {
3045 : // For first day of weather, all time steps of the first hour will be
3046 : // equal to the first hour's value.
3047 : // 2021-06: An additional input is added to here to allow the user to have chosen which hour to use
3048 27 : int HrUsedtoInterp = thisEnviron.firstHrInterpUseHr1 ? 1 : 24;
3049 27 : state.dataWeather->wvarsLastHr = wvarsHr(HrUsedtoInterp);
3050 27 : state.dataWeather->LastHourSet = true;
3051 : }
3052 :
3053 18850 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3054 :
3055 18096 : int NextHr = (hour == Constant::iHoursInDay) ? 1 : hour + 1;
3056 :
3057 18096 : state.dataWeather->wvarsNextHr.BeamSolarRad = wvarsHr(NextHr).BeamSolarRad;
3058 18096 : state.dataWeather->wvarsNextHr.DifSolarRad = wvarsHr(NextHr).DifSolarRad;
3059 18096 : state.dataWeather->wvarsNextHr.LiquidPrecip = wvarsHr(NextHr).LiquidPrecip;
3060 :
3061 90480 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
3062 :
3063 72384 : Real64 wgtCurrHr = state.dataWeather->Interpolation(ts);
3064 72384 : Real64 wgtPrevHr = 1.0 - wgtCurrHr;
3065 :
3066 : // Do Solar "weighting"
3067 :
3068 72384 : Real64 wgtCurrHrSolar = state.dataWeather->SolarInterpolation(ts);
3069 : Real64 wgtPrevHrSolar;
3070 : Real64 wgtNextHrSolar;
3071 :
3072 72384 : if (state.dataGlobal->TimeStepsInHour == 1) {
3073 0 : wgtNextHrSolar = 1.0 - wgtCurrHr;
3074 0 : wgtPrevHrSolar = 0.0;
3075 72384 : } else if (wgtCurrHrSolar == 1.0) {
3076 : // It's at the half hour
3077 18096 : wgtPrevHrSolar = 0.0;
3078 18096 : wgtNextHrSolar = 0.0;
3079 54288 : } else if (ts * state.dataWeather->TimeStepFraction < 0.5) {
3080 18096 : wgtPrevHrSolar = 1.0 - wgtCurrHrSolar;
3081 18096 : wgtNextHrSolar = 0.0;
3082 : } else { // After the half hour
3083 36192 : wgtPrevHrSolar = 0.0;
3084 36192 : wgtNextHrSolar = 1.0 - wgtCurrHrSolar;
3085 : }
3086 :
3087 72384 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
3088 72384 : auto const &wvarsH = wvarsHr(hour);
3089 72384 : tomorrowTs.OutDryBulbTemp = state.dataWeather->wvarsLastHr.OutDryBulbTemp * wgtPrevHr + wvarsH.OutDryBulbTemp * wgtCurrHr;
3090 72384 : tomorrowTs.OutBaroPress = state.dataWeather->wvarsLastHr.OutBaroPress * wgtPrevHr + wvarsH.OutBaroPress * wgtCurrHr;
3091 72384 : tomorrowTs.OutDewPointTemp = state.dataWeather->wvarsLastHr.OutDewPointTemp * wgtPrevHr + wvarsH.OutDewPointTemp * wgtCurrHr;
3092 72384 : tomorrowTs.OutRelHum = state.dataWeather->wvarsLastHr.OutRelHum * wgtPrevHr + wvarsH.OutRelHum * wgtCurrHr;
3093 72384 : tomorrowTs.WindSpeed = state.dataWeather->wvarsLastHr.WindSpeed * wgtPrevHr + wvarsH.WindSpeed * wgtCurrHr;
3094 72384 : tomorrowTs.WindDir = interpolateWindDirection(state.dataWeather->wvarsLastHr.WindDir, wvarsH.WindDir, wgtCurrHr);
3095 72384 : tomorrowTs.TotalSkyCover = state.dataWeather->wvarsLastHr.TotalSkyCover * wgtPrevHr + wvarsH.TotalSkyCover * wgtCurrHr;
3096 72384 : tomorrowTs.OpaqueSkyCover = state.dataWeather->wvarsLastHr.OpaqueSkyCover * wgtPrevHr + wvarsH.OpaqueSkyCover * wgtCurrHr;
3097 : // Sky emissivity now takes interpolated timestep inputs rather than interpolated calculation esky results
3098 72384 : calcSky(state,
3099 72384 : tomorrowTs.HorizIRSky,
3100 72384 : tomorrowTs.SkyTemp,
3101 : tomorrowTs.OpaqueSkyCover,
3102 : tomorrowTs.OutDryBulbTemp,
3103 : tomorrowTs.OutDewPointTemp,
3104 72384 : tomorrowTs.OutRelHum * 0.01,
3105 72384 : state.dataWeather->wvarsLastHr.HorizIRSky * wgtPrevHr + wvarsH.HorizIRSky * wgtCurrHr);
3106 :
3107 72384 : tomorrowTs.DifSolarRad = state.dataWeather->wvarsLastHr.DifSolarRad * wgtPrevHrSolar + wvarsH.DifSolarRad * wgtCurrHrSolar +
3108 72384 : state.dataWeather->wvarsNextHr.DifSolarRad * wgtNextHrSolar;
3109 72384 : tomorrowTs.BeamSolarRad = state.dataWeather->wvarsLastHr.BeamSolarRad * wgtPrevHrSolar + wvarsH.BeamSolarRad * wgtCurrHrSolar +
3110 72384 : state.dataWeather->wvarsNextHr.BeamSolarRad * wgtNextHrSolar;
3111 :
3112 72384 : tomorrowTs.LiquidPrecip = state.dataWeather->wvarsLastHr.LiquidPrecip * wgtPrevHr + wvarsH.LiquidPrecip * wgtCurrHr;
3113 72384 : tomorrowTs.LiquidPrecip /= double(state.dataGlobal->TimeStepsInHour);
3114 72384 : tomorrowTs.IsRain = tomorrowTs.LiquidPrecip >= state.dataWeather->IsRainThreshold; // Wthr%IsRain
3115 72384 : tomorrowTs.IsSnow = wvarsH.IsSnow;
3116 : } // End of TS Loop
3117 :
3118 18096 : state.dataWeather->wvarsLastHr = wvarsHr(hour);
3119 : } // End of Hour Loop
3120 : }
3121 :
3122 755 : if (thisEnviron.WP_Type1 != 0) {
3123 0 : switch (state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).skyTempModel) {
3124 0 : case SkyTempModel::ScheduleValue: {
3125 : std::vector<Real64> const &dayVals =
3126 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3127 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3128 :
3129 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3130 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3131 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3132 : }
3133 : }
3134 0 : } break;
3135 :
3136 0 : case SkyTempModel::DryBulbDelta: {
3137 : std::vector<Real64> const &dayVals =
3138 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3139 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3140 :
3141 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3142 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3143 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3144 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3145 : }
3146 : }
3147 0 : } break;
3148 :
3149 0 : case SkyTempModel::DewPointDelta: {
3150 : std::vector<Real64> const &dayVals =
3151 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3152 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3153 :
3154 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3155 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3156 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3157 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3158 : }
3159 : }
3160 0 : } break;
3161 :
3162 0 : default:
3163 0 : break;
3164 : }
3165 : }
3166 755 : }
3167 :
3168 72433 : Real64 interpolateWindDirection(Real64 const prevHrWindDir, Real64 const curHrWindDir, Real64 const curHrWeight)
3169 : {
3170 : // adapted from http://stackoverflow.com/questions/2708476/rotation-interpolation
3171 72433 : Real64 curAng = curHrWindDir;
3172 72433 : Real64 prevAng = prevHrWindDir;
3173 72433 : Real64 diff = std::abs(curAng - prevAng);
3174 72433 : if (diff > 180.) {
3175 4148 : if (curAng > prevAng) {
3176 1792 : prevAng += 360.;
3177 : } else {
3178 2356 : curAng += 360.;
3179 : }
3180 : }
3181 72433 : Real64 interpAng = prevAng + (curAng - prevAng) * curHrWeight;
3182 72433 : return (fmod(interpAng, 360.)); // fmod is float modulus function
3183 : }
3184 :
3185 68408 : Real64 CalcSkyEmissivity(
3186 : EnergyPlusData &state, SkyTempModel const ESkyCalcType, Real64 const OSky, Real64 const DryBulb, Real64 const DewPoint, Real64 const RelHum)
3187 : {
3188 : // Calculate Sky Emissivity
3189 : // References:
3190 : // M. Li, Y. Jiang and C. F. M. Coimbra,
3191 : // "On the determination of atmospheric longwave irradiance under all-sky conditions,"
3192 : // Solar Energy 144, 2017, pp. 40–48,
3193 : // G. Clark and C. Allen, "The Estimation of Atmospheric Radiation for Clear and
3194 : // Cloudy Skies," Proc. 2nd National Passive Solar Conference (AS/ISES), 1978, pp. 675-678.
3195 :
3196 : Real64 ESky;
3197 :
3198 68408 : if (ESkyCalcType == SkyTempModel::Brunt) {
3199 2 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3200 2 : ESky = 0.618 + 0.056 * pow(PartialPress, 0.5);
3201 68406 : } else if (ESkyCalcType == SkyTempModel::Idso) {
3202 2 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3203 2 : ESky = 0.685 + 0.000032 * PartialPress * exp(1699 / (DryBulb + Constant::Kelvin));
3204 68404 : } else if (ESkyCalcType == SkyTempModel::BerdahlMartin) {
3205 2 : double const TDewC = min(DryBulb, DewPoint);
3206 2 : ESky = 0.758 + 0.521 * (TDewC / 100) + 0.625 * pow_2(TDewC / 100);
3207 : } else {
3208 68402 : ESky = 0.787 + 0.764 * std::log((min(DryBulb, DewPoint) + Constant::Kelvin) / Constant::Kelvin);
3209 : }
3210 68408 : return ESky * (1.0 + 0.0224 * OSky - 0.0035 * pow_2(OSky) + 0.00028 * pow_3(OSky));
3211 : }
3212 :
3213 27 : void SetDayOfWeekInitialValues(int const EnvironDayOfWeek, // Starting Day of Week for the (Weather) RunPeriod (User Input)
3214 : int ¤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 27 : if (EnvironDayOfWeek != 0) {
3228 27 : if (EnvironDayOfWeek <= 7) {
3229 27 : currentDayOfWeek = EnvironDayOfWeek - 1;
3230 : } else {
3231 0 : currentDayOfWeek = EnvironDayOfWeek;
3232 : }
3233 : }
3234 27 : }
3235 :
3236 0 : void ErrorInterpretWeatherDataLine(EnergyPlusData &state,
3237 : int const WYear,
3238 : int const WMonth,
3239 : int const WDay,
3240 : int const WHour,
3241 : int const WMinute,
3242 : std::string_view SaveLine,
3243 : std::string_view Line)
3244 : {
3245 0 : ShowSevereError(state, fmt::format("Invalid Weather Line at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3246 0 : ShowContinueError(state, fmt::format("Full Data Line={}", SaveLine));
3247 0 : ShowContinueError(state, fmt::format("Remainder of line={}", Line));
3248 0 : ShowFatalError(state, "Error in Reading Weather Data");
3249 0 : }
3250 :
3251 27632 : void InterpretWeatherDataLine(EnergyPlusData &state,
3252 : std::string_view Line,
3253 : bool &ErrorFound, // True if an error is found, false otherwise
3254 : int &WYear,
3255 : int &WMonth,
3256 : int &WDay,
3257 : int &WHour,
3258 : int &WMinute,
3259 : Real64 &DryBulb,
3260 : Real64 &DewPoint,
3261 : Real64 &RelHum,
3262 : Real64 &AtmPress,
3263 : Real64 Ð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 27632 : EP_SIZE_CHECK(WCodesArr, 9); // NOLINT(misc-static-assert)
3304 :
3305 : static constexpr std::string_view ValidDigits("0123456789");
3306 :
3307 27632 : std::string_view::size_type pos = 0;
3308 27632 : std::string_view current_line = Line;
3309 :
3310 27632 : ErrorFound = false;
3311 :
3312 : // Do the first five. (To get to the DataSource field)
3313 : {
3314 27632 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 5); // Returns the position **after** the nth occurrence of ','
3315 27632 : const bool succeeded = readList(current_line.substr(pos, (nth_pos - 1) - pos), WYear, WMonth, WDay, WHour, WMinute);
3316 27632 : if (!succeeded) {
3317 0 : ShowSevereError(state, "Invalid Date info in Weather Line");
3318 0 : ShowContinueError(state, fmt::format("Entire Data Line={}", Line));
3319 0 : ShowFatalError(state, "Error in Reading Weather Data");
3320 : }
3321 : }
3322 :
3323 27632 : bool DateInError = false;
3324 27632 : if (WMonth >= 1 && WMonth <= 12) {
3325 : // Month number is valid
3326 27632 : if (WMonth != 2) {
3327 25560 : if (WDay > state.dataWeather->EndDayOfMonth(WMonth)) {
3328 0 : DateInError = true;
3329 : }
3330 2072 : } else if (WDay > state.dataWeather->EndDayOfMonth(WMonth) + 1) { // Whether actually used is determined by calling routine.
3331 0 : DateInError = true;
3332 : }
3333 : } else {
3334 0 : DateInError = true;
3335 : }
3336 :
3337 27632 : if (DateInError) {
3338 0 : ShowSevereError(state, format("Reading Weather Data Line, Invalid Date, Year={}, Month={}, Day={}", WYear, WMonth, WDay));
3339 0 : ShowFatalError(state, "Program terminates due to previous condition.");
3340 : }
3341 :
3342 : // index, unlike nth_occurrence returns the position of the search char, not the position after it
3343 27632 : pos = index(Line, ','); // WYear
3344 27632 : if (pos == std::string::npos) {
3345 0 : ShowSevereError(
3346 0 : state, format("Invalid Weather Line (no commas) at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3347 0 : ShowContinueError(state, fmt::format("Full Data Line={}", Line));
3348 0 : ShowFatalError(state, "Error in Reading Weather Data");
3349 : }
3350 27632 : current_line.remove_prefix(nth_occurrence(Line, ',', 6)); // remove WYear,WMonth,WDay,WHour,WMinute,Data Source/Integrity
3351 :
3352 : // Now read more numerics with List Directed I/O (note there is another "character" field lurking)
3353 : Real64 RField21;
3354 : {
3355 27632 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 21);
3356 :
3357 27632 : const bool succeeded = readList(current_line.substr(0, nth_pos - 1),
3358 : DryBulb,
3359 : DewPoint,
3360 : RelHum,
3361 : AtmPress,
3362 : ETHoriz,
3363 : ETDirect,
3364 : IRHoriz,
3365 : GLBHoriz,
3366 : DirectRad,
3367 : DiffuseRad,
3368 : GLBHorizIllum,
3369 : DirectNrmIllum,
3370 : DiffuseHorizIllum,
3371 : ZenLum,
3372 : WindDir,
3373 : WindSpeed,
3374 : TotalSkyCover,
3375 : OpaqueSkyCover,
3376 : Visibility,
3377 : CeilHeight,
3378 : RField21);
3379 :
3380 27632 : if (!succeeded) {
3381 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3382 : }
3383 27632 : current_line.remove_prefix(nth_pos);
3384 : }
3385 27632 : pos = index(current_line, ',');
3386 27632 : std::string PresWeathCodes;
3387 27632 : if (pos != std::string::npos && pos != 0) {
3388 27632 : PresWeathCodes = current_line.substr(0, pos);
3389 : } else {
3390 0 : PresWeathCodes = "999999999";
3391 : }
3392 27632 : current_line.remove_prefix(pos + 1);
3393 :
3394 27632 : auto readNextNumber = // (AUTO_OK_LAMBDA)
3395 165792 : [reachedEndOfCommands = false, &state, &WYear, &WMonth, &WDay, &WHour, &WMinute, &Line, ¤t_line]() mutable -> Real64 {
3396 165792 : if (reachedEndOfCommands) {
3397 4 : return 999.0;
3398 : }
3399 : Real64 target;
3400 165788 : std::string_view::size_type pos = index(current_line, ',');
3401 : // We found a comma
3402 165788 : if (pos != std::string::npos) {
3403 : // Content is not empty
3404 165787 : if (pos != 0) {
3405 165787 : bool error = false;
3406 165787 : target = Util::ProcessNumber(current_line.substr(0, pos), error);
3407 165787 : if (error) {
3408 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3409 : }
3410 : } else {
3411 0 : target = 999.0;
3412 : }
3413 165787 : current_line.remove_prefix(pos + 1);
3414 : } else {
3415 : // Couldn't find next comma, but we need to process the potential current number
3416 1 : reachedEndOfCommands = true;
3417 1 : if (current_line.empty()) {
3418 0 : target = 999.0;
3419 : } else {
3420 1 : bool error = false;
3421 1 : target = Util::ProcessNumber(current_line, error);
3422 1 : if (error) {
3423 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3424 : }
3425 : }
3426 : }
3427 165788 : return target;
3428 27632 : };
3429 :
3430 27632 : PrecipWater = readNextNumber();
3431 27632 : AerosolOptDepth = readNextNumber();
3432 27632 : SnowDepth = readNextNumber();
3433 27632 : DaysSinceLastSnow = readNextNumber();
3434 27632 : Albedo = readNextNumber();
3435 27632 : LiquidPrecip = readNextNumber();
3436 :
3437 27632 : WObs = nint(RField21);
3438 27632 : if (WObs == 0) { // Obs Indicator indicates Weather Codes valid
3439 : // Check for miscellaneous characters
3440 2 : pos = index(PresWeathCodes, '\'');
3441 2 : while (pos != std::string::npos) {
3442 0 : PresWeathCodes[pos] = ' ';
3443 0 : pos = index(PresWeathCodes, '\'');
3444 : }
3445 2 : pos = index(PresWeathCodes, '"');
3446 2 : while (pos != std::string::npos) {
3447 0 : PresWeathCodes[pos] = ' ';
3448 0 : pos = index(PresWeathCodes, '"');
3449 : }
3450 2 : strip(PresWeathCodes);
3451 2 : if (len(PresWeathCodes) == 9) {
3452 20 : for (pos = 0; pos < 9; ++pos) {
3453 18 : if (!has(ValidDigits, PresWeathCodes[pos])) {
3454 0 : PresWeathCodes[pos] = '9';
3455 : }
3456 : }
3457 :
3458 : // we are trying to read a string of 9 integers with no spaces, each
3459 : // into its own integer, like:
3460 : // "123456789"
3461 : // becomes
3462 : // std::vector<int>{1,2,3,4,5,6,7,8,9};
3463 2 : std::stringstream reader = stringReader(PresWeathCodes);
3464 20 : for (auto &value : WCodesArr) {
3465 18 : char c[2] = {0, 0}; // a string of 2 characters, init both to 0
3466 18 : reader >> c[0]; // read next char into the first byte
3467 18 : value = std::atoi(c); // convert this short string into the appropriate int to read
3468 : }
3469 2 : } else {
3470 0 : ++state.dataWeather->wvarsMissedCounts.WeathCodes;
3471 0 : WCodesArr = 9;
3472 : }
3473 : } else {
3474 27630 : WCodesArr = 9;
3475 : }
3476 27632 : }
3477 :
3478 517 : void SetUpDesignDay(EnergyPlusData &state, int const EnvrnNum) // Environment number passed into the routine
3479 : {
3480 :
3481 : // SUBROUTINE INFORMATION:
3482 : // AUTHOR Linda Lawrie
3483 : // DATE WRITTEN February 1977
3484 : // MODIFIED June 1997 (RKS); May 2013 (LKL) add temperature profile for drybulb.
3485 : // RE-ENGINEERED August 2003;LKL -- to generate timestep weather for design days.
3486 :
3487 : // PURPOSE OF THIS SUBROUTINE:
3488 : // This purpose of this subroutine is to convert the user supplied input
3489 : // values for the design day parameters into an entire weather day
3490 : // record. This now bypasses any file I/O by keeping all of the
3491 : // weather day record information in the local module level derived type
3492 : // called DesignDay.
3493 :
3494 517 : constexpr Real64 GlobalSolarConstant = 1367.0;
3495 517 : constexpr Real64 ZHGlobalSolarConstant = 1355.0;
3496 :
3497 517 : Real64 constexpr ZhangHuang_C0 = 0.5598; // 37.6865d0
3498 517 : Real64 constexpr ZhangHuang_C1 = 0.4982; // 13.9263d0
3499 517 : Real64 constexpr ZhangHuang_C2 = -0.6762; // -20.2354d0
3500 517 : Real64 constexpr ZhangHuang_C3 = 0.02842; // 0.9695d0
3501 517 : Real64 constexpr ZhangHuang_C4 = -0.00317; // -0.2046d0
3502 517 : Real64 constexpr ZhangHuang_C5 = 0.014; // -0.0980d0
3503 517 : Real64 constexpr ZhangHuang_D = -17.853; // -10.8568d0
3504 517 : Real64 constexpr ZhangHuang_K = 0.843; // 49.3112d0
3505 : static constexpr std::string_view RoutineNamePsyWFnTdbTwbPb("SetUpDesignDay:PsyWFnTdbTwbPb");
3506 : static constexpr std::string_view RoutineNamePsyWFnTdpPb("SetUpDesignDay:PsyWFnTdpPb");
3507 : static constexpr std::string_view RoutineNamePsyWFnTdbH("SetUpDesignDay:PsyWFnTdbH");
3508 : static constexpr std::string_view WeatherManager("WeatherManager");
3509 : static constexpr std::string_view RoutineNameLong("WeatherManager.cc subroutine SetUpDesignDay");
3510 :
3511 517 : std::string StringOut;
3512 : // For reporting purposes, set year to current system year
3513 :
3514 : struct HourlyWeatherData
3515 : {
3516 : // Members
3517 : Array1D<Real64> BeamSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly direct normal solar irradiance
3518 : Array1D<Real64> DifSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly sky diffuse horizontal solar irradiance
3519 : };
3520 :
3521 : // Object Data
3522 517 : HourlyWeatherData Wthr;
3523 :
3524 517 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
3525 :
3526 517 : bool SaveWarmupFlag = state.dataGlobal->WarmupFlag;
3527 517 : state.dataGlobal->WarmupFlag = true;
3528 :
3529 517 : Array1D_int Date0(8);
3530 517 : date_and_time(_, _, _, Date0);
3531 517 : int CurrentYear = Date0(1);
3532 :
3533 517 : if (state.dataGlobal->BeginSimFlag) {
3534 103 : state.dataWeather->PrintDDHeader = true;
3535 : }
3536 :
3537 517 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
3538 517 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
3539 517 : designDay.Year = CurrentYear; // f90 date_and_time implemented. full 4 digit year !+ 1900
3540 517 : designDay.Month = desDayInput.Month;
3541 517 : designDay.DayOfMonth = desDayInput.DayOfMonth;
3542 517 : designDay.DayOfYear = General::OrdinalDay(designDay.Month, designDay.DayOfMonth, 0);
3543 : static constexpr std::string_view MnDyFmt("{:02}/{:02}");
3544 517 : state.dataEnvrn->CurMnDy = format(MnDyFmt, desDayInput.Month, desDayInput.DayOfMonth);
3545 : // EnvironmentName = DesDayInput( EnvrnNum ).Title;
3546 517 : state.dataEnvrn->RunPeriodEnvironment = false;
3547 : // Following builds Environment start/end for ASHRAE 55 warnings
3548 517 : state.dataEnvrn->EnvironmentStartEnd = state.dataEnvrn->CurMnDy + " - " + state.dataEnvrn->CurMnDy;
3549 :
3550 : // Check that barometric pressure is within range
3551 517 : if (desDayInput.PressureEntered) {
3552 517 : if (std::abs((desDayInput.PressBarom - state.dataEnvrn->StdBaroPress) / state.dataEnvrn->StdBaroPress) > 0.1) { // 10% off
3553 68 : ShowWarningError(state,
3554 68 : format("SetUpDesignDay: Entered DesignDay Barometric Pressure={:.0R} differs by more than 10% from Standard "
3555 : "Barometric Pressure={:.0R}.",
3556 34 : desDayInput.PressBarom,
3557 34 : state.dataEnvrn->StdBaroPress));
3558 68 : ShowContinueError(
3559 : state,
3560 68 : format("...occurs in DesignDay={}, Standard Pressure (based on elevation) will be used.", state.dataEnvrn->EnvironmentName));
3561 34 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3562 : }
3563 : } else {
3564 0 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3565 : }
3566 :
3567 : // verify that design WB or DP <= design DB
3568 517 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint && desDayInput.DewPointNeedsSet) {
3569 : // dew-point
3570 0 : Real64 testval = Psychrometrics::PsyWFnTdbRhPb(state, desDayInput.MaxDryBulb, 1.0, desDayInput.PressBarom);
3571 0 : desDayInput.HumIndValue = Psychrometrics::PsyTdpFnWPb(state, testval, desDayInput.PressBarom);
3572 : }
3573 :
3574 : // Day of week defaults to Monday, if day type specified, then that is used.
3575 517 : designDay.DayOfWeek = 2;
3576 517 : if (desDayInput.DayType <= 7) {
3577 0 : designDay.DayOfWeek = desDayInput.DayType;
3578 : }
3579 :
3580 : // set Holiday as indicated by user input
3581 517 : designDay.HolidayIndex = 0;
3582 517 : if (desDayInput.DayType > 7) {
3583 517 : designDay.HolidayIndex = desDayInput.DayType;
3584 : }
3585 :
3586 517 : designDay.DaylightSavingIndex = desDayInput.DSTIndicator;
3587 :
3588 : // Set up Solar parameters for day
3589 : Real64 A; // Apparent solar irradiation at air mass = 0
3590 : Real64 B; // Atmospheric extinction coefficient
3591 : Real64 C; // ASHRAE diffuse radiation factor
3592 : Real64 AVSC; // Annual variation in the solar constant
3593 517 : CalculateDailySolarCoeffs(
3594 517 : state, designDay.DayOfYear, A, B, C, AVSC, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle);
3595 :
3596 517 : if (state.dataWeather->PrintDDHeader && state.dataReportFlag->DoWeatherInitReporting) {
3597 : static constexpr std::string_view EnvDDHdFormat(
3598 : "! <Environment:Design Day Data>, Max Dry-Bulb Temp {C}, Temp Range {dC}, Temp Range Ind Type, "
3599 : "Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units, Pressure {Pa}, Wind Direction {deg CW from N}, Wind "
3600 : "Speed {m/s}, Clearness, Rain, Snow");
3601 66 : print(state.files.eio, "{}\n", EnvDDHdFormat);
3602 : static constexpr std::string_view DDayMiscHdFormat(
3603 : "! <Environment:Design Day Misc>,DayOfYear,ASHRAE A Coeff,ASHRAE B Coeff,ASHRAE C Coeff,Solar "
3604 : "Constant-Annual Variation,Eq of Time {minutes}, Solar Declination Angle {deg}, Solar Model");
3605 66 : print(state.files.eio, "{}\n", DDayMiscHdFormat);
3606 66 : state.dataWeather->PrintDDHeader = false;
3607 : }
3608 517 : if (state.dataReportFlag->DoWeatherInitReporting) {
3609 113 : std::string_view const AlpUseRain = (desDayInput.RainInd == 1) ? "Yes" : "No";
3610 113 : std::string_view const AlpUseSnow = (desDayInput.SnowInd == 1) ? "Yes" : "No";
3611 113 : print(state.files.eio, "Environment:Design Day Data,");
3612 113 : print(state.files.eio, "{:.2R},", desDayInput.MaxDryBulb);
3613 113 : print(state.files.eio, "{:.2R},", desDayInput.DailyDBRange);
3614 :
3615 : static constexpr std::array<std::string_view, (int)DesDayDryBulbRangeType::Num> DesDayDryBulbRangeTypeStrings = {
3616 : "DefaultMultipliers,", "MultiplierSchedule,", "DifferenceSchedule,", "TemperatureProfile,"};
3617 :
3618 113 : print(state.files.eio, "{}", DesDayDryBulbRangeTypeStrings[(int)desDayInput.dryBulbRangeType]);
3619 :
3620 : static constexpr std::array<std::string_view, (int)DesDayHumIndType::Num> DesDayHumIndTypeStrings = {
3621 : "Wetbulb,{:.2R},{{C}},",
3622 : "Dewpoint,{:.2R},{{C}},",
3623 : "Enthalpy,{:.2R},{{J/kgDryAir}},",
3624 : "HumidityRatio,{:.4R},{{kgWater/kgDryAir}},",
3625 : "Schedule,<schedule values from 0.0 to 100.0>,{{percent}},",
3626 : "WetBulbProfileDefaultMultipliers,{:.2R},{{C}},",
3627 : "WetBulbProfileDifferenceSchedule,{:.2R},{{C}},",
3628 : "WetBulbProfileMultiplierSchedule,{:.2R},{{C}},"};
3629 :
3630 : // Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units
3631 113 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
3632 0 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType]);
3633 113 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
3634 0 : print(state.files.eio,
3635 0 : DesDayHumIndTypeStrings[(int)desDayInput.HumIndType],
3636 0 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).HumIndValue);
3637 : } else {
3638 113 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType], desDayInput.HumIndValue);
3639 : }
3640 :
3641 113 : print(state.files.eio, "{:.0R},", desDayInput.PressBarom);
3642 113 : print(state.files.eio, "{:.0R},", desDayInput.WindDir);
3643 113 : print(state.files.eio, "{:.1R},", desDayInput.WindSpeed);
3644 113 : print(state.files.eio, "{:.2R},", desDayInput.SkyClear);
3645 :
3646 113 : print(state.files.eio, "{},{}\n", AlpUseRain, AlpUseSnow);
3647 :
3648 : static constexpr std::string_view DDayMiscFormat("Environment:Design Day Misc,{:3},");
3649 113 : print(state.files.eio, DDayMiscFormat, designDay.DayOfYear);
3650 113 : print(state.files.eio, "{:.1R},", A);
3651 113 : print(state.files.eio, "{:.4R},", B);
3652 113 : print(state.files.eio, "{:.4R},", C);
3653 113 : print(state.files.eio, "{:.1R},", AVSC);
3654 113 : print(state.files.eio, "{:.2R},", designDay.EquationOfTime * 60.0);
3655 113 : print(state.files.eio, "{:.1R},", std::asin(designDay.SinSolarDeclinAngle) / Constant::DegToRad);
3656 :
3657 : // Why have a different string for "Schedule" here than the one used for input? Really, why?
3658 : static constexpr std::array<std::string_view, (int)DesDaySolarModel::Num> DesDaySolarModelStrings = {
3659 : "ASHRAEClearSky", "ZhangHuang", "User supplied beam/diffuse from schedules", "ASHRAETau", "ASHRAETau2017"};
3660 :
3661 113 : print(state.files.eio, "{}\n", DesDaySolarModelStrings[(int)desDayInput.solarModel]);
3662 : }
3663 :
3664 : // Must set up weather values for Design Day. User can specify the "humidity indicator" as
3665 : // Wetbulb, DewPoint or input the relative humidity schedule. For both wetbulb and dewpoint indicators, the
3666 : // humidity for the day will be constant, using the drybulb (max) and humidity indicator temperature to
3667 : // set the values. For the scheduled values, these are already set in the DDxxx array.
3668 :
3669 517 : state.dataGlobal->CurrentTime = 25.0;
3670 : Real64 HumidityRatio; // Humidity Ratio -- when constant for day
3671 : bool ConstantHumidityRatio;
3672 :
3673 517 : switch (desDayInput.HumIndType) {
3674 514 : case DesDayHumIndType::WetBulb: {
3675 514 : HumidityRatio = Psychrometrics::PsyWFnTdbTwbPb(
3676 : state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdbTwbPb);
3677 514 : ConstantHumidityRatio = true;
3678 514 : } break;
3679 0 : case DesDayHumIndType::DewPoint: {
3680 0 : HumidityRatio = Psychrometrics::PsyWFnTdpPb(state, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdpPb);
3681 0 : ConstantHumidityRatio = true;
3682 0 : } break;
3683 0 : case DesDayHumIndType::HumRatio: {
3684 0 : HumidityRatio = desDayInput.HumIndValue;
3685 0 : ConstantHumidityRatio = true;
3686 0 : } break;
3687 1 : case DesDayHumIndType::Enthalpy: {
3688 : // HumIndValue is already in J/kg, so no conversions needed
3689 1 : HumidityRatio = Psychrometrics::PsyWFnTdbH(state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, RoutineNamePsyWFnTdbH);
3690 1 : ConstantHumidityRatio = true;
3691 1 : } break;
3692 0 : case DesDayHumIndType::RelHumSch: {
3693 : // nothing to do -- DDHumIndModifier already contains the scheduled Relative Humidity
3694 0 : ConstantHumidityRatio = false;
3695 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3696 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3697 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).OutRelHum =
3698 0 : state.dataWeather->desDayMods(EnvrnNum)(ts + 1, hr + 1).OutRelHum;
3699 : }
3700 : }
3701 0 : } break;
3702 2 : case DesDayHumIndType::WBProfDef:
3703 : case DesDayHumIndType::WBProfDif:
3704 : case DesDayHumIndType::WBProfMul: {
3705 2 : ConstantHumidityRatio = false;
3706 2 : } break;
3707 0 : default: {
3708 0 : ShowSevereError(state, "SetUpDesignDay: Invalid Humidity Indicator type");
3709 0 : ShowContinueError(state, format("Occurred in Design Day={}", desDayInput.Title));
3710 0 : } break;
3711 : } // switch
3712 :
3713 : int OSky; // Opaque Sky Cover (tenths)
3714 517 : if (desDayInput.RainInd != 0) {
3715 0 : OSky = 10;
3716 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3717 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3718 0 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3719 0 : wvars.IsRain = true;
3720 0 : wvars.LiquidPrecip = 3.0;
3721 : }
3722 : }
3723 : } else {
3724 517 : OSky = 0;
3725 12925 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3726 80424 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3727 68016 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3728 68016 : wvars.IsRain = false;
3729 68016 : wvars.LiquidPrecip = 0.0;
3730 : }
3731 : }
3732 : }
3733 :
3734 : Real64 GndReflet; // Ground Reflectivity
3735 517 : if (desDayInput.SnowInd == 0) {
3736 517 : GndReflet = 0.2;
3737 12925 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3738 80424 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3739 68016 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3740 68016 : wvars.IsSnow = false;
3741 : }
3742 : }
3743 : } else { // Snow
3744 0 : GndReflet = 0.7;
3745 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3746 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3747 0 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3748 0 : wvars.IsSnow = true;
3749 : }
3750 : }
3751 : }
3752 :
3753 : // Some values are constant
3754 :
3755 12925 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3756 80424 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3757 68016 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3758 68016 : wvars.OutBaroPress = desDayInput.PressBarom;
3759 68016 : wvars.WindSpeed = desDayInput.WindSpeed;
3760 68016 : wvars.WindDir = desDayInput.WindDir;
3761 68016 : wvars.Albedo = 0.0;
3762 : }
3763 : }
3764 :
3765 : // resolve daily ranges
3766 : Real64 DBRange; // working copy of dry-bulb daily range, C (or 1 if input is difference)
3767 517 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
3768 0 : DBRange = 1.0; // use unscaled multiplier values if difference
3769 517 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
3770 0 : DBRange = 0.0;
3771 : } else {
3772 517 : DBRange = desDayInput.DailyDBRange;
3773 : }
3774 : Real64 WBRange; // working copy of wet-bulb daily range. C (or 1 if input is difference)
3775 517 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
3776 0 : WBRange = 1.0; // use unscaled multiplier values if difference
3777 : } else {
3778 517 : WBRange = desDayInput.DailyWBRange;
3779 : }
3780 :
3781 517 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3782 12925 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3783 80424 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
3784 68016 : auto const &desDayModsTS = desDayModsEnvrn(ts, hour);
3785 68016 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
3786 68016 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
3787 : // dry-bulb profile
3788 68016 : tomorrowTs.OutDryBulbTemp = desDayInput.MaxDryBulb - desDayModsTS.OutDryBulbTemp * DBRange;
3789 : } else { // DesDayInput(EnvrnNum)%DBTempRangeType == DesDayDryBulbRangeType::Profile
3790 0 : tomorrowTs.OutDryBulbTemp = desDayModsTS.OutDryBulbTemp;
3791 : }
3792 :
3793 : // wet-bulb - generate from profile, humidity ratio, or dew point
3794 68016 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
3795 67824 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul) {
3796 192 : Real64 WetBulb = desDayInput.HumIndValue - desDayModsTS.OutRelHum * WBRange;
3797 192 : WetBulb = min(WetBulb, tomorrowTs.OutDryBulbTemp); // WB must be <= DB
3798 192 : Real64 OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3799 192 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3800 192 : tomorrowTs.OutRelHum =
3801 192 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3802 68016 : } else if (ConstantHumidityRatio) {
3803 : // Need Dew Point Temperature. Use Relative Humidity to get Humidity Ratio, unless Humidity Ratio is constant
3804 : // BG 9-26-07 moved following inside this IF statment; when HumIndType is 'Schedule' HumidityRatio wasn't being initialized
3805 : Real64 WetBulb =
3806 67824 : Psychrometrics::PsyTwbFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, RoutineNameLong);
3807 :
3808 67824 : Real64 OutHumRat = Psychrometrics::PsyWFnTdpPb(state, tomorrowTs.OutDryBulbTemp, desDayInput.PressBarom);
3809 67824 : if (HumidityRatio > OutHumRat) {
3810 18445 : WetBulb = tomorrowTs.OutDryBulbTemp;
3811 : } else {
3812 49379 : OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3813 : }
3814 67824 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3815 67824 : tomorrowTs.OutRelHum =
3816 67824 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3817 : } else {
3818 : HumidityRatio =
3819 0 : Psychrometrics::PsyWFnTdbRhPb(state, tomorrowTs.OutDryBulbTemp, desDayModsTS.OutRelHum / 100.0, desDayInput.PressBarom);
3820 0 : tomorrowTs.OutRelHum =
3821 0 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, WeatherManager) *
3822 : 100.0;
3823 : // TomorrowOutRelHum values set earlier
3824 0 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, HumidityRatio, desDayInput.PressBarom);
3825 : }
3826 :
3827 68016 : double DryBulb = tomorrowTs.OutDryBulbTemp;
3828 68016 : double RelHum = tomorrowTs.OutRelHum * 0.01;
3829 : Real64 ESky =
3830 68016 : CalcSkyEmissivity(state, envCurr.skyTempModel, OSky, DryBulb, tomorrowTs.OutDewPointTemp, RelHum); // Emissivitity of Sky
3831 68016 : tomorrowTs.HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
3832 :
3833 68016 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
3834 68016 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
3835 : // Design day not scheduled
3836 68016 : tomorrowTs.SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
3837 : }
3838 : // Generate solar values for timestep
3839 : // working results = BeamRad and DiffRad
3840 : // stored to program globals at end of loop
3841 : Real64 BeamRad;
3842 : Real64 DiffRad;
3843 68016 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
3844 : // scheduled: set value unconditionally (whether sun up or not)
3845 0 : BeamRad = desDayModsTS.BeamSolarRad;
3846 0 : DiffRad = desDayModsTS.DifSolarRad;
3847 : } else {
3848 :
3849 : // calc time = fractional hour of day
3850 : Real64 CurTime;
3851 68016 : if (state.dataGlobal->TimeStepsInHour != 1) {
3852 67392 : CurTime = double(hour - 1) + double(ts) * state.dataWeather->TimeStepFraction;
3853 : } else {
3854 624 : CurTime = double(hour) + state.dataEnvrn->TS1TimeOffset;
3855 : }
3856 :
3857 68016 : Vector3<Real64> SUNCOS; // Sun direction cosines
3858 68016 : CalculateSunDirectionCosines(
3859 : state, CurTime, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle, SUNCOS);
3860 68016 : Real64 CosZenith = SUNCOS.z; // Cosine of Zenith Angle of Sun
3861 68016 : if (CosZenith < DataEnvironment::SunIsUpValue) {
3862 34134 : BeamRad = 0.0;
3863 34134 : DiffRad = 0.0;
3864 : } else {
3865 33882 : Real64 SinSolarAltitude = SUNCOS.z;
3866 :
3867 33882 : switch (desDayInput.solarModel) {
3868 31727 : case DesDaySolarModel::ASHRAE_ClearSky: {
3869 31727 : Real64 Exponent = B / CosZenith;
3870 : Real64 TotHoriz; // Total Radiation on Horizontal Surface
3871 31727 : if (Exponent > 700.0) {
3872 4 : TotHoriz = 0.0;
3873 : } else {
3874 31723 : TotHoriz = desDayInput.SkyClear * A * (C + CosZenith) * std::exp(-B / CosZenith);
3875 : }
3876 : // Radiation on an extraterrestial horizontal surface
3877 31727 : Real64 HO = GlobalSolarConstant * AVSC * CosZenith;
3878 31727 : Real64 KT = TotHoriz / HO; // Radiation ratio
3879 31727 : KT = min(KT, 0.75);
3880 31727 : DiffRad = TotHoriz * (1.0045 + KT * (0.04349 + KT * (-3.5227 + 2.6313 * KT)));
3881 31727 : if (desDayInput.SkyClear > 0.70) {
3882 19061 : DiffRad = TotHoriz * C / (C + CosZenith);
3883 : }
3884 31727 : BeamRad = (TotHoriz - DiffRad) / CosZenith;
3885 31727 : DiffRad = max(0.0, DiffRad);
3886 31727 : BeamRad = max(0.0, BeamRad);
3887 :
3888 31727 : } break;
3889 2155 : case DesDaySolarModel::ASHRAE_Tau:
3890 : case DesDaySolarModel::ASHRAE_Tau2017: {
3891 2155 : Real64 ETR = GlobalSolarConstant * AVSC; // radiation of an extraterrestrial normal surface, W/m2
3892 : Real64 GloHorzRad;
3893 2155 : ASHRAETauModel(
3894 : state, desDayInput.solarModel, ETR, CosZenith, desDayInput.TauB, desDayInput.TauD, BeamRad, DiffRad, GloHorzRad);
3895 2155 : } break;
3896 0 : case DesDaySolarModel::Zhang_Huang: {
3897 0 : int Hour3Ago = mod(hour + 20, 24) + 1; // hour 3 hours before
3898 0 : Real64 const TotSkyCover = max(1.0 - desDayInput.SkyClear, 0.0);
3899 0 : Real64 GloHorzRad = (ZHGlobalSolarConstant * SinSolarAltitude *
3900 0 : (ZhangHuang_C0 + ZhangHuang_C1 * TotSkyCover + ZhangHuang_C2 * pow_2(TotSkyCover) +
3901 0 : ZhangHuang_C3 * (tomorrowTs.OutDryBulbTemp -
3902 0 : state.dataWeather->wvarsHrTsTomorrow(ts, Hour3Ago).OutDryBulbTemp) +
3903 0 : ZhangHuang_C4 * tomorrowTs.OutRelHum + ZhangHuang_C5 * tomorrowTs.WindSpeed) +
3904 : ZhangHuang_D) /
3905 0 : ZhangHuang_K;
3906 0 : GloHorzRad = max(GloHorzRad, 0.0);
3907 0 : Real64 ClearnessIndex_kt = GloHorzRad / (GlobalSolarConstant * SinSolarAltitude);
3908 : // ClearnessIndex_kt=DesDayInput(EnvrnNum)%SkyClear
3909 0 : Real64 ClearnessIndex_ktc = 0.4268 + 0.1934 * SinSolarAltitude;
3910 : Real64 ClearnessIndex_kds;
3911 0 : if (ClearnessIndex_kt < ClearnessIndex_ktc) {
3912 0 : ClearnessIndex_kds = (3.996 - 3.862 * SinSolarAltitude + 1.54 * pow_2(SinSolarAltitude)) * pow_3(ClearnessIndex_kt);
3913 : } else {
3914 0 : ClearnessIndex_kds = ClearnessIndex_kt - (1.107 + 0.03569 * SinSolarAltitude + 1.681 * pow_2(SinSolarAltitude)) *
3915 0 : pow_3(1.0 - ClearnessIndex_kt);
3916 : }
3917 : // Calculate direct normal radiation, W/m2
3918 0 : BeamRad = ZHGlobalSolarConstant * SinSolarAltitude * ClearnessIndex_kds *
3919 0 : ((1.0 - ClearnessIndex_kt) / (1.0 - ClearnessIndex_kds));
3920 : // Calculation diffuse horizontal radiation, W/m2
3921 0 : DiffRad =
3922 0 : ZHGlobalSolarConstant * SinSolarAltitude * ((ClearnessIndex_kt - ClearnessIndex_kds) / (1.0 - ClearnessIndex_kds));
3923 :
3924 0 : } break;
3925 0 : default:
3926 0 : break;
3927 : }
3928 : }
3929 68016 : }
3930 :
3931 : // override result to 0 per environment var (for testing)
3932 68016 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreBeamRadiation) {
3933 0 : BeamRad = 0.0;
3934 : }
3935 68016 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreDiffuseRadiation) {
3936 0 : DiffRad = 0.0;
3937 : }
3938 :
3939 68016 : tomorrowTs.BeamSolarRad = BeamRad;
3940 68016 : tomorrowTs.DifSolarRad = DiffRad;
3941 :
3942 : } // Timestep (TS) Loop
3943 : } // Hour Loop
3944 :
3945 : // back-fill hour values from timesteps
3946 : // hour values = integrated over hour ending at time of hour
3947 : // insurance: hourly values not known to be needed
3948 12925 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3949 12408 : int Hour1Ago = mod(hour + 22, Constant::iHoursInDay) + 1;
3950 12408 : auto const &tomorrowHr = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, hour);
3951 12408 : auto const &tomorrowHr1Ago = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, Hour1Ago);
3952 :
3953 12408 : Real64 BeamRad = (tomorrowHr1Ago.BeamSolarRad + tomorrowHr.BeamSolarRad) / 2.0;
3954 12408 : Real64 DiffRad = (tomorrowHr1Ago.DifSolarRad + tomorrowHr.DifSolarRad) / 2.0;
3955 12408 : if (state.dataGlobal->TimeStepsInHour > 1) {
3956 67392 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour - 1; ++iTS) {
3957 55608 : BeamRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).BeamSolarRad;
3958 55608 : DiffRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).DifSolarRad;
3959 : }
3960 : }
3961 12408 : Wthr.BeamSolarRad(hour) = BeamRad / state.dataGlobal->TimeStepsInHour;
3962 12408 : Wthr.DifSolarRad(hour) = DiffRad / state.dataGlobal->TimeStepsInHour;
3963 : }
3964 :
3965 517 : if (envCurr.WP_Type1 != 0) {
3966 :
3967 0 : switch (state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel) {
3968 0 : case SkyTempModel::ScheduleValue: {
3969 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3970 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3971 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3972 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3973 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = desDayModsEnvrn(ts + 1, hr + 1).SkyTemp =
3974 0 : dayVals[hr * state.dataGlobal->TimeStepsInHour];
3975 : }
3976 : }
3977 0 : } break;
3978 :
3979 0 : case SkyTempModel::DryBulbDelta: {
3980 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3981 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3982 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3983 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3984 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3985 0 : desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3986 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3987 : }
3988 : }
3989 0 : } break;
3990 :
3991 0 : case SkyTempModel::DewPointDelta: {
3992 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3993 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3994 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3995 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3996 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3997 0 : desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3998 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3999 : }
4000 : }
4001 0 : } break;
4002 :
4003 0 : default: {
4004 0 : } break;
4005 : } // switch (skyTempModel)
4006 : } // if (envCurr.WP_Type1 != 0)
4007 :
4008 517 : state.dataGlobal->WarmupFlag = SaveWarmupFlag;
4009 517 : }
4010 :
4011 2159 : Real64 AirMass(Real64 const CosZen) // COS( solar zenith), 0 - 1
4012 : {
4013 :
4014 : // SUBROUTINE INFORMATION:
4015 : // AUTHOR C Barnaby
4016 : // DATE WRITTEN Nov 2010
4017 :
4018 : // PURPOSE OF THIS SUBROUTINE:
4019 : // Calculate relative air mass using Kasten and Young approximation
4020 :
4021 : // METHODOLOGY EMPLOYED:
4022 : // Eqn (16), ASHRAE HOF 2009, p. 14.9
4023 :
4024 : // REFERENCES:
4025 : // ASHRAE HOF 2009 Chapter 14
4026 : // Kasten, F and T. Young. 1989. Revised optical air mass tables
4027 : // and approximating formula. Applied Optics 28:4735-4738.
4028 :
4029 : Real64 AirMass;
4030 : Real64 SunAltD;
4031 :
4032 2159 : if (CosZen <= 0.001) {
4033 5 : AirMass = 37.07837343; // limit value calc'd with Excel
4034 : // value increases little as CosZen -> 0
4035 2154 : } else if (CosZen >= 1.0) {
4036 4 : AirMass = 1.0;
4037 : } else {
4038 : // note: COS( Zen) = SIN( Alt)
4039 2150 : SunAltD = std::asin(CosZen) / Constant::DegToRad; // altitude, degrees
4040 2150 : AirMass = 1.0 / (CosZen + 0.50572 * std::pow(6.07995 + SunAltD, -1.6364));
4041 : }
4042 2159 : return AirMass;
4043 : }
4044 :
4045 : //------------------------------------------------------------------------------
4046 :
4047 2157 : void ASHRAETauModel([[maybe_unused]] EnergyPlusData &state,
4048 : DesDaySolarModel const TauModel, // ASHRAETau solar model type ASHRAE_Tau or ASHRAE_Tau2017
4049 : Real64 const ETR, // extraterrestrial normal irradiance, W/m2
4050 : Real64 const CosZen, // COS( solar zenith angle), 0 - 1
4051 : Real64 const TauB, // beam tau factor
4052 : Real64 const TauD, // dif tau factor
4053 : Real64 &IDirN, // returned: direct (beam) irradiance on normal surface, W/m2
4054 : Real64 &IDifH, // returned: diffuse irradiance on horiz surface, W/m2
4055 : Real64 &IGlbH // returned: global irradiance on horiz surface, W/m2
4056 : )
4057 : {
4058 :
4059 : // SUBROUTINE INFORMATION:
4060 : // AUTHOR C Barnaby
4061 : // DATE WRITTEN Nov 2010
4062 :
4063 : // PURPOSE OF THIS SUBROUTINE:
4064 : // Calculate clear-sky direct and diffuse irradiance using ASHRAE "tau" model
4065 :
4066 : // METHODOLOGY EMPLOYED:
4067 : // Eqns (17-18), ASHRAE HOF 2009, p. 14.9
4068 : // Eqns (19-20), ASHRAE HOF 2013 p. 14.9 and 2017 p. 14.10
4069 :
4070 : // REFERENCES:
4071 : // ASHRAE HOF 2009 Chapter 14
4072 :
4073 : Real64 AB; // air mass exponents
4074 : Real64 AD;
4075 : Real64 M; // air mass
4076 :
4077 2157 : if (CosZen < DataEnvironment::SunIsUpValue || TauB <= 0.0 || TauD <= 0.0) {
4078 0 : IDirN = 0.0;
4079 0 : IDifH = 0.0;
4080 0 : IGlbH = 0.0;
4081 : } else {
4082 2157 : if (TauModel == DesDaySolarModel::ASHRAE_Tau) {
4083 2045 : AB = 1.219 - 0.043 * TauB - 0.151 * TauD - 0.204 * TauB * TauD;
4084 2045 : AD = 0.202 + 0.852 * TauB - 0.007 * TauD - 0.357 * TauB * TauD;
4085 : } else {
4086 : // TauModelType == ASHRAE_Tau2017
4087 112 : AB = 1.454 - 0.406 * TauB - 0.268 * TauD + 0.021 * TauB * TauD;
4088 112 : AD = 0.507 + 0.205 * TauB - 0.080 * TauD - 0.190 * TauB * TauD;
4089 : }
4090 2157 : M = AirMass(CosZen);
4091 2157 : IDirN = ETR * std::exp(-TauB * std::pow(M, AB));
4092 2157 : IDifH = ETR * std::exp(-TauD * std::pow(M, AD));
4093 2157 : IGlbH = IDirN * CosZen + IDifH;
4094 : }
4095 2157 : }
4096 :
4097 117 : void AllocateWeatherData(EnergyPlusData &state)
4098 : {
4099 :
4100 : // SUBROUTINE INFORMATION:
4101 : // AUTHOR Linda Lawrie
4102 : // DATE WRITTEN December 2000
4103 :
4104 : // PURPOSE OF THIS SUBROUTINE:
4105 : // This subroutine allocates the weather data structures (Today, Tomorrow,
4106 : // Design Day) to the proper number of "time steps in hour" requested by the user.
4107 : // Interpolation of data is done later after either setting up the design day (hourly
4108 : // data) or reading in hourly weather data.
4109 :
4110 117 : state.dataWeather->wvarsHrTsToday.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
4111 117 : state.dataWeather->wvarsHrTsTomorrow.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
4112 117 : }
4113 :
4114 1272 : void CalculateDailySolarCoeffs(EnergyPlusData const &state,
4115 : int const DayOfYear, // Day of year (1 - 366)
4116 : Real64 &A, // ASHRAE "A" - Apparent solar irradiation at air mass = 0 [W/M**2]
4117 : Real64 &B, // ASHRAE "B" - Atmospheric extinction coefficient
4118 : Real64 &C, // ASHRAE "C" - Diffuse radiation factor
4119 : Real64 &AnnVarSolConstant, // Annual variation in the solar constant
4120 : Real64 &EquationOfTime, // Equation of Time
4121 : Real64 &SineSolarDeclination, // Sine of Solar Declination
4122 : Real64 &CosineSolarDeclination // Cosine of Solar Declination
4123 : )
4124 : {
4125 :
4126 : // SUBROUTINE INFORMATION:
4127 : // AUTHOR George Walton
4128 : // DATE WRITTEN May 1985
4129 : // MODIFIED 1999 for EnergyPlus
4130 : // RE-ENGINEERED 2001; LKL; Remove need for English -> SI conversion
4131 : // Implement Tarp "fix" for Southern Hemisphere
4132 :
4133 : // PURPOSE OF THIS SUBROUTINE:
4134 : // This subroutine computes the daily solar coefficients used in other
4135 : // calculations. Specifically, this routine computes values of the solar declination, equation
4136 : // of time, and ashrae sky coefficients a, b, and c for a given
4137 : // day of the year.
4138 :
4139 : // METHODOLOGY EMPLOYED:
4140 : // The method is the same as that recommended in the ASHRAE loads
4141 : // algorithms manual, except that the fourier series expressions
4142 : // have been extended by two terms for greater accuracy.
4143 : // coefficients for the new expressions were determined at USACERL
4144 : // using data from the cited references.
4145 :
4146 : // REFERENCES:
4147 : // J. L. Threlkeld, "Thermal Environmental Engineering", 1970,
4148 : // p.316, for declination and equation of time.
4149 : // "ASHRAE Handbook of Fundamentals", 1972, p.387 for sky
4150 : // coefficients a, b, and c.
4151 : // See SUN3 in SolarShading. See SUN2 in BLAST. See SUN3 in Tarp.
4152 :
4153 1272 : Real64 const DayCorrection(Constant::Pi * 2.0 / 366.0);
4154 :
4155 : // Fitted coefficients of Fourier series | Sine of declination coefficients
4156 : static constexpr std::array<Real64, 9> SineSolDeclCoef = {
4157 : 0.00561800, 0.0657911, -0.392779, 0.00064440, -0.00618495, -0.00010101, -0.00007951, -0.00011691, 0.00002096};
4158 : // Fitted coefficients of Fourier Series | Equation of Time coefficients
4159 : static constexpr std::array<Real64, 9> EqOfTimeCoef = {
4160 : 0.00021971, -0.122649, 0.00762856, -0.156308, -0.0530028, -0.00388702, -0.00123978, -0.00270502, -0.00167992};
4161 : // Fitted coefficients of Fourier Series | ASHRAE A Factor coefficients
4162 : static constexpr std::array<Real64, 9> ASHRAE_A_Coef = {1161.6685, 1.1554, 77.3575, -0.5359, -3.7622, 0.9875, -3.3924, -1.7445, 1.1198};
4163 : // Fitted coefficients of Fourier Series | ASHRAE B Factor coefficients
4164 : static constexpr std::array<Real64, 9> ASHRAE_B_Coef = {
4165 : 0.171631, -0.00400448, -0.0344923, 0.00000209, 0.00325428, -0.00085429, 0.00229562, 0.0009034, -0.0011867};
4166 : // Fitted coefficients of Fourier Series | ASHRAE C Factor coefficients
4167 : static constexpr std::array<Real64, 9> ASHRAE_C_Coef = {
4168 : 0.0905151, -0.00322522, -0.0407966, 0.000104164, 0.00745899, -0.00086461, 0.0013111, 0.000808275, -0.00170515};
4169 :
4170 : // Day of Year in Radians (Computed from Input DayOfYear)
4171 1272 : Real64 X = DayCorrection * DayOfYear; // Convert Julian date (Day of Year) to angle X
4172 :
4173 : // Calculate sines and cosines of X
4174 1272 : Real64 SinX = std::sin(X);
4175 1272 : Real64 CosX = std::cos(X);
4176 :
4177 1272 : SineSolarDeclination = SineSolDeclCoef[0] + SineSolDeclCoef[1] * SinX + SineSolDeclCoef[2] * CosX + SineSolDeclCoef[3] * (SinX * CosX * 2.0) +
4178 1272 : SineSolDeclCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4179 1272 : SineSolDeclCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4180 1272 : SineSolDeclCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4181 1272 : SineSolDeclCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4182 1272 : SineSolDeclCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4183 1272 : CosineSolarDeclination = std::sqrt(1.0 - pow_2(SineSolarDeclination));
4184 :
4185 1272 : EquationOfTime = EqOfTimeCoef[0] + EqOfTimeCoef[1] * SinX + EqOfTimeCoef[2] * CosX + EqOfTimeCoef[3] * (SinX * CosX * 2.0) +
4186 1272 : EqOfTimeCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4187 1272 : EqOfTimeCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4188 1272 : EqOfTimeCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4189 1272 : EqOfTimeCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4190 1272 : EqOfTimeCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4191 :
4192 1272 : AnnVarSolConstant = 1.000047 + 0.000352615 * SinX + 0.0334454 * CosX;
4193 :
4194 1272 : A = ASHRAE_A_Coef[0] + ASHRAE_A_Coef[1] * SinX + ASHRAE_A_Coef[2] * CosX + ASHRAE_A_Coef[3] * (SinX * CosX * 2.0) +
4195 1272 : ASHRAE_A_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_A_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4196 1272 : ASHRAE_A_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4197 1272 : ASHRAE_A_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4198 1272 : ASHRAE_A_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4199 :
4200 : // Compute B and C coefficients
4201 :
4202 1272 : if (state.dataEnvrn->Latitude < 0.0) {
4203 : // If in southern hemisphere, compute B and C with a six month time shift.
4204 0 : X -= Constant::Pi;
4205 0 : SinX = std::sin(X);
4206 0 : CosX = std::cos(X);
4207 : }
4208 :
4209 1272 : B = ASHRAE_B_Coef[0] + ASHRAE_B_Coef[1] * SinX + ASHRAE_B_Coef[2] * CosX + ASHRAE_B_Coef[3] * (SinX * CosX * 2.0) +
4210 1272 : ASHRAE_B_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_B_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4211 1272 : ASHRAE_B_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4212 1272 : ASHRAE_B_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4213 1272 : ASHRAE_B_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4214 :
4215 1272 : C = ASHRAE_C_Coef[0] + ASHRAE_C_Coef[1] * SinX + ASHRAE_C_Coef[2] * CosX + ASHRAE_C_Coef[3] * (SinX * CosX * 2.0) +
4216 1272 : ASHRAE_C_Coef[4] * (pow_2(CosX) - pow_2(SinX)) + ASHRAE_C_Coef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4217 1272 : ASHRAE_C_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4218 1272 : ASHRAE_C_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4219 1272 : ASHRAE_C_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4220 1272 : }
4221 :
4222 68016 : void CalculateSunDirectionCosines(EnergyPlusData const &state,
4223 : Real64 const TimeValue, // Current Time of Day
4224 : Real64 const EqOfTime, // Equation of Time
4225 : Real64 const SinSolDeclin, // Sine of Solar Declination
4226 : Real64 const CosSolDeclin, // Cosine of Solar Declination
4227 : Vector3<Real64> &SUNCOS)
4228 : {
4229 :
4230 : // SUBROUTINE INFORMATION:
4231 : // AUTHOR George Walton
4232 : // DATE WRITTEN May 1975
4233 : // MODIFIED 1999 for EnergyPlus
4234 :
4235 : // PURPOSE OF THIS SUBROUTINE:
4236 : // This routine computes the solar direction cosines for hourly
4237 : // radiation calculations.
4238 :
4239 : // REFERENCES:
4240 : // "NECAP Engineering Manual", 1974, p.3-117
4241 :
4242 68016 : EP_SIZE_CHECK(SUNCOS, 3); // NOLINT(misc-static-assert)
4243 :
4244 : // COMPUTE THE HOUR ANGLE
4245 68016 : Real64 H = (15.0 * (12.0 - (TimeValue + EqOfTime)) + (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude)) * Constant::DegToRad;
4246 68016 : Real64 COSH = std::cos(H);
4247 : // COMPUTE THE COSINE OF THE SOLAR ZENITH ANGLE.
4248 : // This is also the Sine of the Solar Altitude Angle
4249 :
4250 68016 : SUNCOS.z = SinSolDeclin * state.dataEnvrn->SinLatitude + CosSolDeclin * state.dataEnvrn->CosLatitude * COSH;
4251 :
4252 68016 : if (SUNCOS.z >= DataEnvironment::SunIsUpValue) { // If Sun above horizon, compute other direction cosines
4253 33882 : SUNCOS.y = SinSolDeclin * state.dataEnvrn->CosLatitude - CosSolDeclin * state.dataEnvrn->SinLatitude * COSH;
4254 33882 : SUNCOS.x = CosSolDeclin * std::sin(H);
4255 : } else { // Sun is down, set to 0.0
4256 34134 : SUNCOS.x = 0.0;
4257 34134 : SUNCOS.y = 0.0;
4258 : }
4259 68016 : }
4260 :
4261 326122 : void DetermineSunUpDown(EnergyPlusData &state, Vector3<Real64> &SunCOS)
4262 : {
4263 :
4264 : // SUBROUTINE INFORMATION:
4265 : // AUTHOR Linda Lawrie
4266 : // DATE WRITTEN 1999
4267 :
4268 : // PURPOSE OF THIS SUBROUTINE:
4269 : // This subroutine determines if the sun is up or down for the current
4270 : // hour/timestep.
4271 :
4272 : // REFERENCES:
4273 : // Sun routines from IBLAST, authored by Walton.
4274 :
4275 : // COMPUTE THE HOUR ANGLE
4276 326122 : if (state.dataGlobal->TimeStepsInHour != 1) {
4277 643576 : state.dataWeather->HrAngle = (15.0 * (12.0 - (state.dataGlobal->CurrentTime + state.dataWeather->TodayVariables.EquationOfTime)) +
4278 321788 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4279 : } else {
4280 4334 : state.dataWeather->HrAngle =
4281 4334 : (15.0 *
4282 4334 : (12.0 - ((state.dataGlobal->CurrentTime + state.dataEnvrn->TS1TimeOffset) + state.dataWeather->TodayVariables.EquationOfTime)) +
4283 4334 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4284 : }
4285 326122 : Real64 H = state.dataWeather->HrAngle * Constant::DegToRad;
4286 :
4287 : // Compute the Cosine of the Solar Zenith (Altitude) Angle.
4288 326122 : Real64 CosZenith = state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle +
4289 326122 : state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H);
4290 :
4291 326122 : Real64 SolarZenith = std::acos(CosZenith);
4292 326122 : Real64 SinAltitude = state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H) +
4293 326122 : state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle;
4294 326122 : Real64 SolarAltitude = std::asin(SinAltitude);
4295 326122 : Real64 CosAzimuth = -(state.dataEnvrn->SinLatitude * CosZenith - state.dataWeather->TodayVariables.SinSolarDeclinAngle) /
4296 326122 : (state.dataEnvrn->CosLatitude * std::sin(SolarZenith));
4297 : // Following because above can yield invalid cos value. (e.g. at south pole)
4298 326122 : CosAzimuth = max(CosAzimuth, -1.0);
4299 326122 : CosAzimuth = min(1.0, CosAzimuth);
4300 326122 : Real64 SolarAzimuth = std::acos(CosAzimuth);
4301 :
4302 326122 : state.dataWeather->SolarAltitudeAngle = SolarAltitude / Constant::DegToRad;
4303 326122 : state.dataWeather->SolarAzimuthAngle = SolarAzimuth / Constant::DegToRad;
4304 326122 : if (state.dataWeather->HrAngle < 0.0) {
4305 163219 : state.dataWeather->SolarAzimuthAngle = 360.0 - state.dataWeather->SolarAzimuthAngle;
4306 : }
4307 :
4308 326122 : SunCOS.z = CosZenith;
4309 326122 : state.dataEnvrn->SunIsUpPrevTS = state.dataEnvrn->SunIsUp;
4310 326122 : if (CosZenith < DataEnvironment::SunIsUpValue) {
4311 166429 : state.dataEnvrn->SunIsUp = false;
4312 166429 : SunCOS.y = 0.0;
4313 166429 : SunCOS.x = 0.0;
4314 : } else {
4315 159693 : state.dataEnvrn->SunIsUp = true;
4316 159693 : SunCOS.y = state.dataWeather->TodayVariables.SinSolarDeclinAngle * state.dataEnvrn->CosLatitude -
4317 159693 : state.dataWeather->TodayVariables.CosSolarDeclinAngle * state.dataEnvrn->SinLatitude * std::cos(H);
4318 159693 : SunCOS.x = state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::sin(H);
4319 : }
4320 326122 : }
4321 :
4322 115 : void OpenWeatherFile(EnergyPlusData &state, bool &ErrorsFound)
4323 : {
4324 :
4325 : // SUBROUTINE INFORMATION:
4326 : // AUTHOR Linda Lawrie
4327 : // DATE WRITTEN June 1999
4328 :
4329 : // PURPOSE OF THIS SUBROUTINE:
4330 : // This subroutine checks to see if a weather file and what kind of weather file
4331 : // exists in the working directory and calls appropriate routines to
4332 : // open the files and set up for use.
4333 :
4334 115 : state.dataWeather->WeatherFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
4335 115 : if (state.dataWeather->WeatherFileExists) {
4336 29 : OpenEPlusWeatherFile(state, ErrorsFound, true);
4337 : }
4338 115 : }
4339 :
4340 73 : void OpenEPlusWeatherFile(EnergyPlusData &state,
4341 : bool &ErrorsFound, // Will be set to true if errors found
4342 : bool const ProcessHeader // Set to true when headers should be processed (rather than just read)
4343 : )
4344 : {
4345 :
4346 : // SUBROUTINE INFORMATION:
4347 : // AUTHOR Linda K. Lawrie
4348 : // DATE WRITTEN June 1999
4349 :
4350 : // PURPOSE OF THIS SUBROUTINE:
4351 : // This subroutine opens the EnergyPlus Weather File (in.epw) and processes
4352 : // the initial header records.
4353 :
4354 : // METHODOLOGY EMPLOYED:
4355 : // List directed reads, as possible.
4356 :
4357 73 : state.files.inputWeatherFile.close();
4358 73 : state.files.inputWeatherFile.filePath = state.files.inputWeatherFilePath.filePath;
4359 73 : state.files.inputWeatherFile.open();
4360 73 : if (!state.files.inputWeatherFile.good()) {
4361 0 : ShowFatalError(state, "OpenWeatherFile: Could not OPEN EPW Weather File", OptionalOutputFileRef(state.files.eso));
4362 : }
4363 :
4364 73 : if (ProcessHeader) {
4365 : // Read in Header Information
4366 :
4367 : // Headers should come in order
4368 261 : for (int typeNum = static_cast<int>(EpwHeaderType::Location); typeNum < static_cast<int>(EpwHeaderType::Num); ++typeNum) {
4369 232 : auto Line = state.files.inputWeatherFile.readLine();
4370 232 : if (Line.eof) {
4371 0 : ShowFatalError(
4372 : state,
4373 0 : format("OpenWeatherFile: Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}",
4374 0 : epwHeaders[typeNum]),
4375 0 : OptionalOutputFileRef(state.files.eso));
4376 : }
4377 :
4378 232 : int endcol = len(Line.data);
4379 232 : if (endcol > 0) {
4380 232 : if (int(Line.data[endcol - 1]) == DataSystemVariables::iUnicode_end) {
4381 0 : ShowSevereError(state,
4382 : "OpenWeatherFile: EPW Weather File appears to be a Unicode or binary file.",
4383 0 : OptionalOutputFileRef(state.files.eso));
4384 0 : ShowContinueError(state, "...This file cannot be read by this program. Please save as PC or Unix file and try again");
4385 0 : ShowFatalError(state, "Program terminates due to previous condition.");
4386 : }
4387 : }
4388 232 : std::string::size_type const Pos = FindNonSpace(Line.data);
4389 232 : std::string::size_type const HdPos = index(Line.data, epwHeaders[typeNum]);
4390 232 : if (Pos != HdPos) {
4391 0 : continue;
4392 : }
4393 232 : ProcessEPWHeader(state, static_cast<EpwHeaderType>(typeNum), Line.data, ErrorsFound);
4394 232 : }
4395 : } else { // Header already processed, just read
4396 44 : SkipEPlusWFHeader(state);
4397 : }
4398 73 : }
4399 :
4400 961 : void CloseWeatherFile(EnergyPlusData &state)
4401 : {
4402 961 : state.files.inputWeatherFile.close();
4403 961 : }
4404 :
4405 112 : void ResolveLocationInformation(EnergyPlusData &state, bool &ErrorsFound) // Set to true if no location evident
4406 : {
4407 :
4408 : // SUBROUTINE INFORMATION:
4409 : // AUTHOR Rick Strand
4410 : // DATE WRITTEN June 1997
4411 :
4412 : // PURPOSE OF THIS SUBROUTINE:
4413 : // This subroutine is currently the main interface between the old data
4414 : // structure on the BLAST Weather file and the new data structure contained
4415 : // in this module. At some point, this subroutine will be converted
4416 : // to read information directly from the new input file.
4417 :
4418 164 : if (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather &&
4419 52 : state.dataWeather->WeatherFileExists) {
4420 27 : if (state.dataWeather->LocationGathered) {
4421 : // See if "matching" location
4422 26 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4423 32 : if (std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude) > 1.0 ||
4424 14 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude) > 1.0 ||
4425 39 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone) > 0.0 ||
4426 7 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) / max(state.dataEnvrn->Elevation, 1.0) >
4427 : 0.10) {
4428 36 : ShowWarningError(state, "Weather file location will be used rather than entered (IDF) Location object.");
4429 18 : ShowContinueError(state, format("..Location object={}", state.dataWeather->LocationTitle));
4430 18 : ShowContinueError(state, format("..Weather File Location={}", state.dataEnvrn->WeatherFileLocationTitle));
4431 36 : ShowContinueError(
4432 : state,
4433 36 : format("..due to location differences, Latitude difference=[{:.2R}] degrees, Longitude difference=[{:.2R}] degrees.",
4434 18 : std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude),
4435 18 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude)));
4436 36 : ShowContinueError(state,
4437 36 : format("..Time Zone difference=[{:.1R}] hour(s), Elevation difference=[{:.2R}] percent, [{:.2R}] meters.",
4438 18 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone),
4439 36 : std::abs((state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) /
4440 18 : max(state.dataEnvrn->Elevation, 1.0) * 100.0),
4441 36 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation)));
4442 : }
4443 : }
4444 : }
4445 27 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4446 26 : state.dataWeather->LocationTitle = state.dataEnvrn->WeatherFileLocationTitle;
4447 26 : state.dataEnvrn->Latitude = state.dataWeather->WeatherFileLatitude;
4448 26 : state.dataEnvrn->Longitude = state.dataWeather->WeatherFileLongitude;
4449 26 : state.dataEnvrn->TimeZoneNumber = state.dataWeather->WeatherFileTimeZone;
4450 26 : state.dataEnvrn->Elevation = state.dataWeather->WeatherFileElevation;
4451 : }
4452 85 : } else if (!state.dataWeather->LocationGathered) {
4453 1 : state.dataWeather->LocationTitle = "Not Entered";
4454 2 : ShowSevereError(state, "No Location given. Must have location information for simulation.");
4455 1 : ErrorsFound = true;
4456 : }
4457 :
4458 112 : if (!ErrorsFound) {
4459 111 : state.dataEnvrn->StdBaroPress = DataEnvironment::StdPressureSeaLevel * std::pow(1.0 - 2.25577e-05 * state.dataEnvrn->Elevation, 5.2559);
4460 222 : state.dataEnvrn->StdRhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(
4461 111 : state, state.dataEnvrn->StdBaroPress, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
4462 : // Write Final Location Information to the initialization output file
4463 : static constexpr std::string_view LocHdFormat(
4464 : "! <Site:Location>, Location Name, Latitude {N+/S- Deg}, Longitude {E+/W- Deg}, Time Zone Number "
4465 : "{GMT+/-}, Elevation {m}, Standard Pressure at Elevation {Pa}, Standard RhoAir at Elevation\n");
4466 111 : print(state.files.eio, "{}", LocHdFormat);
4467 :
4468 : static constexpr std::string_view LocFormat("Site:Location,{},{:.2R},{:.2R},{:.2R},{:.2R},{:.0R},{:.4R}\n");
4469 111 : print(state.files.eio,
4470 : LocFormat,
4471 111 : state.dataWeather->LocationTitle,
4472 111 : state.dataEnvrn->Latitude,
4473 111 : state.dataEnvrn->Longitude,
4474 111 : state.dataEnvrn->TimeZoneNumber,
4475 111 : state.dataEnvrn->Elevation,
4476 111 : state.dataEnvrn->StdBaroPress,
4477 111 : state.dataEnvrn->StdRhoAir);
4478 : }
4479 112 : }
4480 :
4481 113 : void CheckLocationValidity(EnergyPlusData &state)
4482 : {
4483 :
4484 : // SUBROUTINE INFORMATION:
4485 : // AUTHOR Rick Strand
4486 : // DATE WRITTEN June 1997
4487 :
4488 : // PURPOSE OF THIS SUBROUTINE:
4489 : // This subroutine is checks to see whether the user specified location
4490 : // or the weather file location (if one exists) is valid. The standard
4491 : // time meridian is also calculated and compared to the user supplied
4492 : // or weather file time zone number.
4493 :
4494 113 : bool LocationError = false; // Set to true if there is a problem detected
4495 :
4496 113 : if ((state.dataEnvrn->Latitude == -999.0) && (state.dataEnvrn->Longitude == -999.0) && (state.dataEnvrn->TimeZoneNumber != -999.0)) {
4497 0 : ShowSevereError(state, "No location specified");
4498 0 : LocationError = true;
4499 : }
4500 :
4501 113 : if ((state.dataEnvrn->Latitude < -90.0) || (state.dataEnvrn->Latitude > 90.0)) {
4502 0 : ShowSevereError(state, format("Latitude must be between -90 and 90; Entered={:.2R}", state.dataEnvrn->Latitude));
4503 0 : LocationError = true;
4504 : }
4505 :
4506 113 : if ((state.dataEnvrn->Longitude < -180.0) || (state.dataEnvrn->Longitude > 180.0)) {
4507 0 : ShowSevereError(state, format("Longitude must be between -180 and 180; Entered={:.2R}", state.dataEnvrn->Longitude));
4508 0 : LocationError = true;
4509 : }
4510 :
4511 113 : if ((state.dataEnvrn->TimeZoneNumber < -12.00) || (state.dataEnvrn->TimeZoneNumber > 14.00)) {
4512 0 : ShowSevereError(state, format("Time Zone must be between -12 and +14; Entered={:.2R}", state.dataEnvrn->TimeZoneNumber));
4513 0 : LocationError = true;
4514 : }
4515 :
4516 113 : Real64 const StdTimeMerid = GetSTM(state.dataEnvrn->Longitude); // Standard time meridian.
4517 :
4518 : // Compare the standard time meridian with the time zone number. If
4519 : // different, notify the user. If StdTimeMerid couldn't be calculated,
4520 : // produce an error message.
4521 :
4522 113 : if (state.dataEnvrn->varyingLocationLatSched != nullptr || state.dataEnvrn->varyingLocationLongSched != nullptr) {
4523 : // don't do any warnings, the building is moving
4524 113 : } else if (StdTimeMerid >= -12.0 && StdTimeMerid <= 12.0) {
4525 113 : if (state.dataEnvrn->TimeZoneNumber != StdTimeMerid) {
4526 : // Difference between Standard Time Meridian and TimeZone
4527 6 : Real64 const DiffCalc = std::abs(state.dataEnvrn->TimeZoneNumber - StdTimeMerid);
4528 6 : if (DiffCalc > 1.0 && DiffCalc < 24.0) {
4529 0 : if (DiffCalc < 3.0) {
4530 0 : ShowWarningError(state,
4531 0 : format("Standard Time Meridian and Time Zone differ by more than 1, Difference=\"{:.1R}\"", DiffCalc));
4532 0 : ShowContinueError(state, "Solar Positions may be incorrect");
4533 : } else {
4534 0 : ShowSevereError(state, format("Standard Time Meridian and Time Zone differ by more than 2, Difference=\"{:.1R}\"", DiffCalc));
4535 0 : ShowContinueError(state, "Solar Positions will be incorrect");
4536 : // LocationError=.TRUE.
4537 : }
4538 : }
4539 : }
4540 113 : } else {
4541 0 : ShowSevereError(state, "Unable to calculate the standard time meridian");
4542 0 : LocationError = true;
4543 : }
4544 :
4545 : // Error handling: if there are any errors in the location information
4546 : // the simulation must be terminated
4547 :
4548 113 : if (LocationError) {
4549 0 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4550 : }
4551 :
4552 113 : if (state.dataEnvrn->TimeZoneNumber <= 12.00) {
4553 113 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0;
4554 : } else {
4555 0 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0 - 360.0;
4556 : }
4557 113 : state.dataEnvrn->SinLatitude = std::sin(Constant::DegToRad * state.dataEnvrn->Latitude);
4558 113 : state.dataEnvrn->CosLatitude = std::cos(Constant::DegToRad * state.dataEnvrn->Latitude);
4559 :
4560 113 : if (state.dataEnvrn->Latitude == 0.0 && state.dataEnvrn->Longitude == 0.0 && state.dataEnvrn->TimeZoneNumber == 0.0) {
4561 3 : ShowWarningError(state,
4562 : "Did you realize that you have Latitude=0.0, Longitude=0.0 and TimeZone=0.0? Your building site is in the middle of "
4563 : "the Atlantic Ocean.");
4564 : }
4565 113 : }
4566 :
4567 50 : void CheckWeatherFileValidity(EnergyPlusData &state)
4568 : {
4569 :
4570 : // SUBROUTINE INFORMATION:
4571 : // AUTHOR Linda Lawrie
4572 : // DATE WRITTEN February 1977
4573 : // MODIFIED June 1997 (RKS)
4574 :
4575 : // PURPOSE OF THIS SUBROUTINE:
4576 : // This subroutine contains a portion of the legacy subroutine CKBLDE.
4577 : // The main purpose of this routine is to check the validity of the
4578 : // weather dates provided by the user and the attached weather file.
4579 : // These functions may eventually be pushed to an interface. This
4580 : // routine also sends the weather file header information at the
4581 : // Environment derived type.
4582 :
4583 50 : if (!state.dataWeather->WeatherFileExists) { // No weather file exists but the user requested one--print error message
4584 :
4585 25 : if (state.dataGlobal->DoWeathSim) {
4586 2 : ShowSevereError(state, "GetNextEnvironment: Weather Environment(s) requested, but no weather file found");
4587 3 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4588 : }
4589 :
4590 : } // ... end of WeatherFileExists IF-THEN
4591 49 : }
4592 :
4593 107 : void ReportOutputFileHeaders(EnergyPlusData &state)
4594 : {
4595 :
4596 : // SUBROUTINE INFORMATION:
4597 : // AUTHOR Rick Strand
4598 : // DATE WRITTEN June 1997
4599 : // MODIFIED December 2017; Jason DeGraw
4600 :
4601 : // PURPOSE OF THIS SUBROUTINE:
4602 : // This subroutine prints out the necessary header information required
4603 : // by the EnergyPlus output file format. This subroutine can be
4604 : // replicated in any other modules which must send data to the output
4605 : // file.
4606 :
4607 : // METHODOLOGY EMPLOYED:
4608 : // For each report, the report flag integer must be saved from the
4609 : // global report number counter. Then, the report counter must be
4610 : // incremented. Finally, the header information for the report must
4611 : // be sent to the output file.
4612 :
4613 : using OutputProcessor::ReportFreq;
4614 :
4615 : static constexpr std::string_view EnvironmentString(",5,Environment Title[],Latitude[deg],Longitude[deg],Time Zone[],Elevation[m]");
4616 :
4617 : static constexpr std::array<std::string_view, (int)ReportFreq::Num> freqStrings = {
4618 : "", // No EachCall string
4619 : ",8,Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],Hour[],StartMinute[],EndMinute[],DayType",
4620 : "", // No Hour string
4621 : ",5,Cumulative Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],DayType ! When Daily ",
4622 : ",2,Cumulative Days of Simulation[],Month[] ! When Monthly ",
4623 : ",1,Cumulative Days of Simulation[] ! When Run Period ",
4624 : ",1,Calendar Year of Simulation[] ! When Annual "};
4625 :
4626 107 : auto &op = state.dataOutputProcessor;
4627 :
4628 107 : state.dataWeather->EnvironmentReportNbr = ++op->ReportNumberCounter;
4629 107 : if (state.dataWeather->EnvironmentReportNbr != 1) { // problem
4630 0 : ShowFatalError(state, "ReportOutputFileHeaders: Assigned report number for Environment title is not 1. Contact Support.");
4631 : }
4632 107 : state.dataWeather->EnvironmentReportChr = fmt::to_string(state.dataWeather->EnvironmentReportNbr);
4633 107 : strip(state.dataWeather->EnvironmentReportChr);
4634 107 : print(state.files.eso, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4635 107 : print(state.files.mtr, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4636 :
4637 : // TImeStep and Hour share a stamp
4638 107 : op->freqStampReportNums[(int)ReportFreq::Hour] = op->freqStampReportNums[(int)ReportFreq::TimeStep] = ++op->ReportNumberCounter;
4639 107 : print(state.files.eso, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4640 107 : print(state.files.mtr, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4641 :
4642 535 : for (ReportFreq freq : {ReportFreq::Day, ReportFreq::Month, ReportFreq::Simulation, ReportFreq::Year}) {
4643 428 : op->freqStampReportNums[(int)freq] = ++op->ReportNumberCounter;
4644 428 : print(state.files.eso, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Report Variables Requested");
4645 428 : print(state.files.mtr, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Meters Requested");
4646 : }
4647 107 : }
4648 :
4649 326115 : void ReportWeatherAndTimeInformation(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
4650 : {
4651 :
4652 : // SUBROUTINE INFORMATION:
4653 : // AUTHOR Rick Strand
4654 : // DATE WRITTEN June 1997
4655 :
4656 : // PURPOSE OF THIS SUBROUTINE:
4657 : // This subroutine is the main driver of the weather reporting. This
4658 : // routine is also responsible for printing the time and environment
4659 : // stamps.
4660 :
4661 : // METHODOLOGY EMPLOYED:
4662 : // Reporting is only done for non-warmup days. The environment stamp
4663 : // is only reported at the beginning of an environment, but after the
4664 : // warmup days (to allow all modules to print the report headers to the
4665 : // output file. This is controlled by the PrintEnvrnStamp variable
4666 : // which is passed in and reset if necessary.
4667 :
4668 : // Report the time stamp and the current weather to the output file
4669 :
4670 326115 : if (!state.dataGlobal->WarmupFlag && !state.dataWeather->RPReadAllWeatherData) { // Write the required output information
4671 :
4672 : // The first time through in a non-warmup day, the environment header
4673 : // must be printed. This must be done here and not in the generic
4674 : // DataGlobals::BeginEnvrnFlag block above because other modules in the simulation
4675 : // must also print out header information. This can be done during
4676 : // the simulation warmup if the environment stamp printing is delayed
4677 : // until the warmup is completed. The stamp should only be printed once
4678 : // per environment (set/reset of PrintEnvrnStamp). In addition, before
4679 : // the first environment, the end of the header block flag must also be
4680 : // sent to the output file.
4681 :
4682 34684 : if (printEnvrnStamp) {
4683 :
4684 16044 : if (state.dataReportFlag->PrintEndDataDictionary && state.dataGlobal->DoOutputReporting) {
4685 : static constexpr std::string_view EndOfHeaderString("End of Data Dictionary"); // End of data dictionary marker
4686 73 : print(state.files.eso, "{}\n", EndOfHeaderString);
4687 73 : print(state.files.mtr, "{}\n", EndOfHeaderString);
4688 73 : state.dataReportFlag->PrintEndDataDictionary = false;
4689 : }
4690 16044 : if (state.dataGlobal->DoOutputReporting) {
4691 129 : std::string const &Title = state.dataWeather->Environment(state.dataWeather->Envrn).Title;
4692 : static constexpr std::string_view EnvironmentStampFormatStr(
4693 : "{},{},{:7.2F},{:7.2F},{:7.2F},{:7.2F}\n"); // Format descriptor for environ stamp
4694 129 : print(state.files.eso,
4695 : EnvironmentStampFormatStr,
4696 129 : state.dataWeather->EnvironmentReportChr,
4697 : Title,
4698 129 : state.dataEnvrn->Latitude,
4699 129 : state.dataEnvrn->Longitude,
4700 129 : state.dataEnvrn->TimeZoneNumber,
4701 129 : state.dataEnvrn->Elevation);
4702 129 : print(state.files.mtr,
4703 : EnvironmentStampFormatStr,
4704 129 : state.dataWeather->EnvironmentReportChr,
4705 : Title,
4706 129 : state.dataEnvrn->Latitude,
4707 129 : state.dataEnvrn->Longitude,
4708 129 : state.dataEnvrn->TimeZoneNumber,
4709 129 : state.dataEnvrn->Elevation);
4710 129 : printEnvrnStamp = false;
4711 : }
4712 : }
4713 : } // ... end of .NOT.WarmupFlag IF-THEN block.
4714 326115 : }
4715 :
4716 111 : void ReadUserWeatherInput(EnergyPlusData &state)
4717 : {
4718 :
4719 : // SUBROUTINE INFORMATION:
4720 : // AUTHOR Richard Liesen
4721 : // DATE WRITTEN September 1997
4722 :
4723 : // PURPOSE OF THIS SUBROUTINE:
4724 : // This subroutine is the main driver of the weather manager module.
4725 : // It controls the assignment of weather related global variables as
4726 : // well as the reads and writes for retrieving weather information.
4727 :
4728 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4729 111 : bool ErrorsFound(false);
4730 :
4731 : // Get the number of design days and annual runs from user inpout
4732 111 : state.dataEnvrn->TotDesDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:DesignDay");
4733 111 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
4734 111 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
4735 111 : state.dataWeather->TotRunPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriod");
4736 111 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4737 111 : state.dataGlobal->WeathSimReq = state.dataWeather->TotRunPers > 0;
4738 111 : state.dataWeather->TotReportPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Output:Table:ReportPeriod");
4739 : #ifdef GET_OUT
4740 : state.dataWeather->SPSiteScheduleNamePtr.allocate(state.dataEnvrn->TotDesDays * 5);
4741 : state.dataWeather->SPSiteScheduleUnits.allocate(state.dataEnvrn->TotDesDays * 5);
4742 :
4743 : state.dataWeather->SPSiteScheduleNamePtr = 0;
4744 : state.dataWeather->SPSiteScheduleUnits = "";
4745 : #endif //
4746 : // Allocate the Design Day and Environment array to the # of DD's or/and
4747 : // Annual runs on input file
4748 111 : state.dataWeather->DesignDay.allocate(state.dataEnvrn->TotDesDays);
4749 111 : state.dataWeather->Environment.allocate(state.dataWeather->NumOfEnvrn);
4750 :
4751 : // Set all Environments to DesignDay and then the weather environment will be set
4752 : // in the get annual run data subroutine
4753 288 : for (int Env = 1; Env <= state.dataEnvrn->TotDesDays; ++Env) {
4754 177 : state.dataWeather->Environment(Env).KindOfEnvrn = Constant::KindOfSim::DesignDay;
4755 : }
4756 111 : for (int Env = 1; Env <= RPD1 + RPD2; ++Env) {
4757 0 : if (!state.dataSysVars->DDOnly) {
4758 0 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
4759 : } else {
4760 0 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4761 : }
4762 : }
4763 164 : for (int Env = 1; Env <= state.dataWeather->TotRunPers; ++Env) {
4764 53 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + RPD1 + RPD2 + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4765 : }
4766 :
4767 111 : if (state.dataEnvrn->TotDesDays >= 1) {
4768 103 : GetDesignDayData(state, state.dataEnvrn->TotDesDays, ErrorsFound);
4769 : }
4770 :
4771 111 : if (RPD1 >= 1 || RPD2 >= 1) {
4772 0 : GetRunPeriodDesignData(state, ErrorsFound);
4773 : }
4774 :
4775 : // the last environment(s) is designated the weather environment if an annual run
4776 : // is selected. All of the design systems is done from the design day info
4777 : // which will have to be completed to run the annual run.
4778 111 : if (state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4779 50 : GetRunPeriodData(state, state.dataWeather->TotRunPers, ErrorsFound);
4780 : }
4781 :
4782 111 : if (state.dataWeather->TotReportPers > 0) {
4783 0 : GetReportPeriodData(state, state.dataWeather->TotReportPers, ErrorsFound);
4784 0 : GroupReportPeriodByType(state, state.dataWeather->TotReportPers);
4785 : }
4786 :
4787 111 : if (state.dataSysVars->FullAnnualRun) {
4788 : // GetRunPeriodData may have reset the value of TotRunPers
4789 0 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4790 : }
4791 :
4792 111 : if (RPD1 >= 1 || RPD2 >= 1 || state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4793 50 : GetSpecialDayPeriodData(state, ErrorsFound);
4794 50 : GetDSTData(state, ErrorsFound);
4795 50 : if (state.dataWeather->IDFDaylightSaving) {
4796 0 : state.dataWeather->DST = state.dataWeather->IDFDST;
4797 : }
4798 : }
4799 :
4800 111 : GetLocationInfo(state, ErrorsFound);
4801 :
4802 111 : GetGroundTemps(state);
4803 :
4804 111 : GetGroundReflectances(state, ErrorsFound);
4805 :
4806 111 : GetSnowGroundRefModifiers(state, ErrorsFound);
4807 :
4808 111 : GetWaterMainsTemperatures(state, ErrorsFound);
4809 :
4810 111 : GetWeatherStation(state, ErrorsFound);
4811 :
4812 111 : SetupEnvironmentTypes(state);
4813 :
4814 111 : GetWeatherProperties(state, ErrorsFound);
4815 : #ifdef GET_OUT
4816 : // Deallocate ones used for schedule pointers
4817 : state.dataWeather->SPSiteScheduleNamePtr.deallocate();
4818 : state.dataWeather->SPSiteScheduleUnits.deallocate();
4819 : #endif //
4820 111 : if (ErrorsFound) {
4821 0 : ShowFatalError(state, "GetWeatherInput: Above errors cause termination");
4822 : }
4823 111 : }
4824 :
4825 55 : static int findYearForWeekday(int const month, int const day, Sched::DayType const weekday)
4826 : {
4827 : // Find a year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
4828 : // the date with the weekday specified.
4829 :
4830 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4831 : static std::array<int, 13> const defaultYear{{2013, 2014, 2015, 2010, 2011, 2017, 2007, 2013, 2014, 2015, 2010, 2011, 2017}};
4832 :
4833 55 : int rem = calculateDayOfYear(month, day) % 7;
4834 55 : return defaultYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4835 : }
4836 :
4837 2 : static int findLeapYearForWeekday(int const month, int const day, Sched::DayType const weekday)
4838 : {
4839 : // Find a leap year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
4840 : // the date with the weekday specified.
4841 :
4842 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4843 : static std::array<int, 13> const defaultLeapYear{{2008, 1992, 2004, 2016, 2000, 2012, 1996, 2008, 1992, 2004, 2016, 2000, 2012}};
4844 :
4845 2 : int rem = calculateDayOfYear(month, day, true) % 7;
4846 2 : return defaultLeapYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4847 : }
4848 :
4849 5 : void GetReportPeriodData(EnergyPlusData &state,
4850 : int nReportPeriods, // Total number of Report Periods requested
4851 : bool &ErrorsFound)
4852 : {
4853 5 : constexpr std::string_view routineName = "GetReportPeriodData";
4854 5 : state.dataWeather->ReportPeriodInput.allocate(nReportPeriods);
4855 :
4856 5 : auto const &ipsc = state.dataIPShortCut;
4857 5 : ipsc->cCurrentModuleObject = "Output:Table:ReportPeriod";
4858 5 : int Count = 0;
4859 : int NumAlpha; // Number of alphas being input
4860 : int NumNumeric; // Number of numbers being input
4861 : int IOStat; // IO Status when calling get input subroutine
4862 15 : for (int i = 1; i <= nReportPeriods; ++i) {
4863 20 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4864 10 : ipsc->cCurrentModuleObject,
4865 : i,
4866 10 : ipsc->cAlphaArgs,
4867 : NumAlpha,
4868 10 : ipsc->rNumericArgs,
4869 : NumNumeric,
4870 : IOStat,
4871 10 : ipsc->lNumericFieldBlanks,
4872 10 : ipsc->lAlphaFieldBlanks,
4873 10 : ipsc->cAlphaFieldNames,
4874 10 : ipsc->cNumericFieldNames);
4875 :
4876 10 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
4877 10 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
4878 : // A1, \field Name
4879 10 : if (std::find_if(state.dataWeather->ReportPeriodInput.begin(),
4880 10 : state.dataWeather->ReportPeriodInput.end(),
4881 30 : [&newName](ReportPeriodData const &rpd) { return newName == rpd.title; }) !=
4882 10 : state.dataWeather->ReportPeriodInput.end()) {
4883 1 : ShowSevereDuplicateName(state, eoh);
4884 1 : ErrorsFound = true;
4885 : }
4886 :
4887 10 : ++Count;
4888 :
4889 10 : auto &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4890 : // Loop = RP + Ptr;
4891 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "ReportPeriod 1" anyways
4892 : // which is fine for our reporting below
4893 10 : reportPeriodInput.title = newName;
4894 : // A2, \field Report Name
4895 10 : reportPeriodInput.reportName = ipsc->cAlphaArgs(2);
4896 :
4897 : // set the start and end day of month from user input
4898 : // N1, \field Begin Year
4899 : // N2, \field Begin Month
4900 : // N3, \field Begin Day of Month
4901 : // N4, \field Begin Hour of Day
4902 : // N5, \field End Year
4903 : // N6, \field End Month
4904 : // N7, \field End Day of Month
4905 : // N8; \field End Hour of Day
4906 10 : reportPeriodInput.startYear = int(ipsc->rNumericArgs(1));
4907 10 : reportPeriodInput.startMonth = int(ipsc->rNumericArgs(2));
4908 10 : reportPeriodInput.startDay = int(ipsc->rNumericArgs(3));
4909 10 : reportPeriodInput.startHour = int(ipsc->rNumericArgs(4));
4910 10 : reportPeriodInput.endYear = int(ipsc->rNumericArgs(5));
4911 10 : reportPeriodInput.endMonth = int(ipsc->rNumericArgs(6));
4912 10 : reportPeriodInput.endDay = int(ipsc->rNumericArgs(7));
4913 10 : reportPeriodInput.endHour = int(ipsc->rNumericArgs(8));
4914 :
4915 : // Validate year inputs
4916 10 : if (reportPeriodInput.startYear == 0) {
4917 10 : if (reportPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
4918 0 : ShowSevereError(state,
4919 0 : format("{}: object={}, end year cannot be specified if the start year is not.",
4920 0 : ipsc->cCurrentModuleObject,
4921 0 : reportPeriodInput.title));
4922 0 : ErrorsFound = true;
4923 : }
4924 0 : } else if (reportPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
4925 0 : ShowSevereError(state,
4926 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
4927 0 : ipsc->cCurrentModuleObject,
4928 0 : reportPeriodInput.title,
4929 0 : reportPeriodInput.startYear));
4930 0 : ErrorsFound = true;
4931 : }
4932 :
4933 10 : if (reportPeriodInput.endYear != 0 && reportPeriodInput.startYear > reportPeriodInput.endYear) {
4934 0 : ShowSevereError(state,
4935 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
4936 0 : ipsc->cCurrentModuleObject,
4937 0 : reportPeriodInput.title,
4938 0 : reportPeriodInput.startYear,
4939 0 : reportPeriodInput.endYear));
4940 0 : ErrorsFound = true;
4941 : }
4942 :
4943 10 : reportPeriodInput.startJulianDate =
4944 10 : computeJulianDate(reportPeriodInput.startYear, reportPeriodInput.startMonth, reportPeriodInput.startDay);
4945 10 : reportPeriodInput.endJulianDate = computeJulianDate(reportPeriodInput.endYear, reportPeriodInput.endMonth, reportPeriodInput.endDay);
4946 10 : }
4947 5 : }
4948 :
4949 4 : void GroupReportPeriodByType(EnergyPlusData &state, const int nReportPeriods)
4950 : {
4951 : // transfer data from the reporting period object to the corresponding report period type arrays
4952 : // ThermalResilienceSummary, CO2ResilienceSummary, VisualResilienceSummary, and AllResilienceSummaries
4953 13 : for (auto const &reportPeriodInput : state.dataWeather->ReportPeriodInput) {
4954 :
4955 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4956 4 : ++state.dataWeather->TotThermalReportPers;
4957 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4958 3 : ++state.dataWeather->TotCO2ReportPers;
4959 2 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4960 2 : ++state.dataWeather->TotVisualReportPers;
4961 0 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4962 0 : ++state.dataWeather->TotThermalReportPers;
4963 0 : ++state.dataWeather->TotCO2ReportPers;
4964 0 : ++state.dataWeather->TotVisualReportPers;
4965 : }
4966 : }
4967 :
4968 4 : state.dataWeather->ThermalReportPeriodInput.allocate(state.dataWeather->TotThermalReportPers);
4969 4 : state.dataWeather->CO2ReportPeriodInput.allocate(state.dataWeather->TotCO2ReportPers);
4970 4 : state.dataWeather->VisualReportPeriodInput.allocate(state.dataWeather->TotVisualReportPers);
4971 :
4972 13 : for (int i = 1, iThermal = 1, iVisual = 1, iCO2 = 1; i <= nReportPeriods; ++i) {
4973 9 : auto const &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4974 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4975 4 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4976 4 : ++iThermal;
4977 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4978 3 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4979 3 : ++iCO2;
4980 2 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4981 2 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4982 2 : ++iVisual;
4983 0 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4984 0 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4985 0 : ++iThermal;
4986 0 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4987 0 : ++iCO2;
4988 0 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4989 0 : ++iVisual;
4990 : }
4991 : }
4992 4 : }
4993 :
4994 55 : void GetRunPeriodData(EnergyPlusData &state,
4995 : int nRunPeriods, // Total number of Run Periods requested
4996 : bool &ErrorsFound)
4997 : {
4998 :
4999 : // SUBROUTINE INFORMATION:
5000 : // AUTHOR Richard Liesen
5001 : // DATE WRITTEN October 1997
5002 : // MODIFIED February 1999, Add multiple run periods, Change name.
5003 : // March 2012, LKL, Add features to object; New "actual weather" object;
5004 :
5005 : // PURPOSE OF THIS SUBROUTINE:
5006 : // This subroutine gets the run period info from User input and the
5007 : // simulation dates
5008 :
5009 55 : constexpr std::string_view routineName = "GetRunPeriodData";
5010 : // Call Input Get routine to retrieve annual run data
5011 55 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
5012 :
5013 55 : auto const &ipsc = state.dataIPShortCut;
5014 55 : ipsc->cCurrentModuleObject = "RunPeriod";
5015 55 : int Count = 0;
5016 : int NumAlpha; // Number of alphas being input
5017 : int NumNumeric; // Number of numbers being input
5018 : int IOStat; // IO Status when calling get input subroutine
5019 119 : for (int i = 1; i <= nRunPeriods; ++i) {
5020 128 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5021 64 : ipsc->cCurrentModuleObject,
5022 : i,
5023 64 : ipsc->cAlphaArgs,
5024 : NumAlpha,
5025 64 : ipsc->rNumericArgs,
5026 : NumNumeric,
5027 : IOStat,
5028 64 : ipsc->lNumericFieldBlanks,
5029 64 : ipsc->lAlphaFieldBlanks,
5030 64 : ipsc->cAlphaFieldNames,
5031 64 : ipsc->cNumericFieldNames);
5032 :
5033 : // A1, \field Name
5034 64 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5035 64 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5036 :
5037 64 : if (std::find_if(state.dataWeather->RunPeriodInput.begin(),
5038 64 : state.dataWeather->RunPeriodInput.end(),
5039 178 : [&newName](RunPeriodData const &rpd) { return rpd.title == newName; }) != state.dataWeather->RunPeriodInput.end()) {
5040 0 : ShowSevereDuplicateName(state, eoh);
5041 0 : ErrorsFound = true;
5042 : }
5043 :
5044 64 : ++Count;
5045 : // Loop = RP + Ptr;
5046 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "RUNPERIOD 1" anyways
5047 : // which is fine for our reporting below
5048 64 : auto &runPeriodInput = state.dataWeather->RunPeriodInput(i);
5049 64 : runPeriodInput.title = ipsc->cAlphaArgs(1);
5050 :
5051 : // set the start and end day of month from user input
5052 : // N1 , \field Begin Month
5053 : // N2 , \field Begin Day of Month
5054 : // N3, \field Start Year
5055 : // N4 , \field End Month
5056 : // N5 , \field End Day of Month
5057 : // N6, \field End Year
5058 64 : runPeriodInput.startMonth = int(ipsc->rNumericArgs(1));
5059 64 : runPeriodInput.startDay = int(ipsc->rNumericArgs(2));
5060 64 : runPeriodInput.startYear = int(ipsc->rNumericArgs(3));
5061 64 : runPeriodInput.endMonth = int(ipsc->rNumericArgs(4));
5062 64 : runPeriodInput.endDay = int(ipsc->rNumericArgs(5));
5063 64 : runPeriodInput.endYear = int(ipsc->rNumericArgs(6));
5064 64 : runPeriodInput.TreatYearsAsConsecutive = true;
5065 :
5066 64 : if (state.dataSysVars->FullAnnualRun && i == 1) {
5067 0 : runPeriodInput.startMonth = 1;
5068 0 : runPeriodInput.startDay = 1;
5069 0 : runPeriodInput.endMonth = 12;
5070 0 : runPeriodInput.endDay = 31;
5071 : }
5072 :
5073 : // Validate year inputs
5074 64 : if (runPeriodInput.startYear == 0) {
5075 58 : if (runPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
5076 2 : ShowSevereError(state,
5077 2 : format("{}: object={}, end year cannot be specified if the start year is not.",
5078 1 : ipsc->cCurrentModuleObject,
5079 1 : runPeriodInput.title));
5080 1 : ErrorsFound = true;
5081 : }
5082 6 : } else if (runPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
5083 0 : ShowSevereError(state,
5084 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
5085 0 : ipsc->cCurrentModuleObject,
5086 0 : runPeriodInput.title,
5087 0 : runPeriodInput.startYear));
5088 0 : ErrorsFound = true;
5089 : }
5090 :
5091 64 : if (runPeriodInput.endYear != 0 && runPeriodInput.startYear > runPeriodInput.endYear) {
5092 0 : ShowSevereError(state,
5093 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
5094 0 : ipsc->cCurrentModuleObject,
5095 0 : runPeriodInput.title,
5096 0 : runPeriodInput.startYear,
5097 0 : runPeriodInput.endYear));
5098 0 : ErrorsFound = true;
5099 : }
5100 :
5101 : // A2 , \field Day of Week for Start Day
5102 64 : bool inputWeekday = false;
5103 64 : if (!state.dataIPShortCut->lAlphaFieldBlanks(2)) { // Have input
5104 61 : int dayType = getEnumValue(Sched::dayTypeNamesUC, state.dataIPShortCut->cAlphaArgs(2));
5105 61 : if (dayType < 1) {
5106 0 : ShowWarningError(state,
5107 0 : format("{}: object={}{} invalid (Day of Week) [{}] for Start is not valid, Sunday will be used.",
5108 0 : state.dataIPShortCut->cCurrentModuleObject,
5109 0 : state.dataWeather->RunPeriodInput(i).title,
5110 0 : state.dataIPShortCut->cAlphaFieldNames(2),
5111 0 : state.dataIPShortCut->cAlphaArgs(2)));
5112 0 : runPeriodInput.startWeekDay = Sched::DayType::Sunday;
5113 : } else {
5114 61 : runPeriodInput.startWeekDay = static_cast<Sched::DayType>(dayType);
5115 61 : inputWeekday = true;
5116 : }
5117 : } else { // No input, set the default as Sunday. This may get overriden below
5118 3 : runPeriodInput.startWeekDay = Sched::DayType::Sunday;
5119 : }
5120 :
5121 : // Validate the dates now that the weekday field has been looked at
5122 64 : if (runPeriodInput.startMonth == 2 && runPeriodInput.startDay == 29) {
5123 : // Requested start date is a leap year
5124 4 : if (runPeriodInput.startYear == 0) { // No input starting year
5125 2 : if (inputWeekday) {
5126 2 : runPeriodInput.startYear =
5127 2 : findLeapYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
5128 : } else {
5129 : // 2012 is the default year, 1/1 is a Sunday
5130 0 : runPeriodInput.startYear = 2012;
5131 0 : runPeriodInput.startWeekDay =
5132 0 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5133 : }
5134 : } else { // Have an input start year
5135 2 : if (!isLeapYear(runPeriodInput.startYear)) { // Start year is not a leap year
5136 2 : ShowSevereError(state,
5137 2 : format("{}: object={}, start year ({}) is not a leap year but the requested start date is 2/29.",
5138 1 : ipsc->cCurrentModuleObject,
5139 1 : runPeriodInput.title,
5140 1 : runPeriodInput.startYear));
5141 1 : ErrorsFound = true;
5142 : } else { // Start year is a leap year
5143 : Sched::DayType weekday =
5144 1 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5145 1 : if (inputWeekday) { // Check for correctness of input
5146 1 : if (weekday != runPeriodInput.startWeekDay) {
5147 0 : ShowWarningError(state,
5148 0 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
5149 0 : ipsc->cCurrentModuleObject,
5150 0 : runPeriodInput.title,
5151 0 : ipsc->cAlphaArgs(2),
5152 0 : runPeriodInput.startYear,
5153 0 : Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
5154 0 : runPeriodInput.startWeekDay = weekday;
5155 : }
5156 : } else { // Set the weekday if it was not input
5157 0 : runPeriodInput.startWeekDay = weekday;
5158 : }
5159 : }
5160 : }
5161 4 : } else {
5162 : // Non leap-day start date
5163 60 : if (!validMonthDay(runPeriodInput.startMonth, runPeriodInput.startDay)) {
5164 0 : ShowSevereError(state,
5165 0 : format("{}: object={}, Invalid input start month/day ({}/{})",
5166 0 : ipsc->cCurrentModuleObject,
5167 0 : runPeriodInput.title,
5168 0 : runPeriodInput.startMonth,
5169 0 : runPeriodInput.startDay));
5170 0 : ErrorsFound = true;
5171 : } else { // Month/day is valid
5172 60 : if (runPeriodInput.startYear == 0) { // No input starting year
5173 56 : if (inputWeekday) {
5174 55 : runPeriodInput.startYear =
5175 55 : findYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
5176 : } else {
5177 : // 2017 is the default year, 1/1 is a Sunday
5178 1 : runPeriodInput.startYear = 2017;
5179 1 : runPeriodInput.startWeekDay =
5180 1 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5181 : }
5182 : } else { // Have an input starting year
5183 : Sched::DayType weekday =
5184 4 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5185 4 : if (inputWeekday) { // Check for correctness of input
5186 2 : if (weekday != runPeriodInput.startWeekDay) {
5187 2 : ShowWarningError(state,
5188 3 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
5189 1 : ipsc->cCurrentModuleObject,
5190 1 : runPeriodInput.title,
5191 1 : ipsc->cAlphaArgs(2),
5192 1 : runPeriodInput.startYear,
5193 1 : Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
5194 1 : runPeriodInput.startWeekDay = weekday;
5195 : }
5196 : } else { // Set the weekday if it was not input
5197 2 : runPeriodInput.startWeekDay = weekday;
5198 : }
5199 : }
5200 : }
5201 : }
5202 :
5203 : // Compute the Julian date of the start date
5204 64 : runPeriodInput.startJulianDate = computeJulianDate(runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5205 :
5206 : // Validate the end date
5207 64 : if (runPeriodInput.endMonth == 2 && runPeriodInput.endDay == 29) {
5208 : // Requested end date is a leap year
5209 0 : if (runPeriodInput.endYear == 0) { // No input end year
5210 0 : if (isLeapYear(runPeriodInput.startYear) && runPeriodInput.startMonth < 3) {
5211 : // The run period is from some date on or before 2/29 through 2/29
5212 0 : runPeriodInput.endYear = runPeriodInput.startYear;
5213 : } else {
5214 : // There might be a better approach here, but for now just loop forward for the next leap year
5215 0 : for (int yr = runPeriodInput.startYear + 1; yr < runPeriodInput.startYear + 10; yr++) {
5216 0 : if (isLeapYear(yr)) {
5217 0 : runPeriodInput.endYear = yr;
5218 0 : break;
5219 : }
5220 : }
5221 : }
5222 : } else { // Have an input end year
5223 0 : if (!isLeapYear(runPeriodInput.endYear)) { // End year is not a leap year
5224 0 : ShowSevereError(state,
5225 0 : format("{}: object={}, end year ({}) is not a leap year but the requested end date is 2/29.",
5226 0 : ipsc->cCurrentModuleObject,
5227 0 : runPeriodInput.title,
5228 0 : runPeriodInput.startYear));
5229 0 : ErrorsFound = true;
5230 : } else {
5231 0 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5232 0 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5233 0 : ShowSevereError(state,
5234 0 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5235 0 : ipsc->cCurrentModuleObject,
5236 0 : runPeriodInput.title,
5237 0 : runPeriodInput.startJulianDate,
5238 0 : runPeriodInput.endJulianDate));
5239 0 : ErrorsFound = true;
5240 : }
5241 : }
5242 : }
5243 0 : } else {
5244 : // Non leap-day end date
5245 64 : if (!validMonthDay(runPeriodInput.endMonth, runPeriodInput.endDay)) {
5246 0 : ShowSevereError(state,
5247 0 : format("{}: object={}, Invalid input end month/day ({}/{})",
5248 0 : ipsc->cCurrentModuleObject,
5249 0 : runPeriodInput.title,
5250 0 : runPeriodInput.startMonth,
5251 0 : runPeriodInput.startDay));
5252 0 : ErrorsFound = true;
5253 : } else { // Month/day is valid
5254 64 : if (runPeriodInput.endYear == 0) { // No input end year
5255 : // Assume same year as start year
5256 61 : runPeriodInput.endYear = runPeriodInput.startYear;
5257 61 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5258 61 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5259 0 : runPeriodInput.endJulianDate = 0; // Force recalculation later
5260 0 : runPeriodInput.endYear += 1;
5261 : }
5262 : } else { // Have an input end year
5263 3 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5264 3 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5265 2 : ShowSevereError(state,
5266 2 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5267 1 : ipsc->cCurrentModuleObject,
5268 1 : runPeriodInput.title,
5269 1 : runPeriodInput.startJulianDate,
5270 1 : runPeriodInput.endJulianDate));
5271 1 : ErrorsFound = true;
5272 : }
5273 : }
5274 : }
5275 : }
5276 :
5277 64 : if (runPeriodInput.endJulianDate == 0) {
5278 0 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5279 : }
5280 :
5281 64 : runPeriodInput.numSimYears = runPeriodInput.endYear - runPeriodInput.startYear + 1;
5282 :
5283 : // A3, \field Use Weather File Holidays and Special Days
5284 : BooleanSwitch b;
5285 64 : if (ipsc->lAlphaFieldBlanks(3)) {
5286 0 : runPeriodInput.useHolidays = true;
5287 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5288 64 : runPeriodInput.useHolidays = static_cast<bool>(b);
5289 : } else {
5290 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5291 0 : ErrorsFound = true;
5292 : }
5293 :
5294 : // A4, \field Use Weather File Daylight Saving Period
5295 64 : if (ipsc->lAlphaFieldBlanks(4)) {
5296 0 : runPeriodInput.useDST = true;
5297 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5298 64 : runPeriodInput.useDST = static_cast<bool>(b);
5299 : } else {
5300 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5301 0 : ErrorsFound = true;
5302 : }
5303 :
5304 : // A5, \field Apply Weekend Holiday Rule
5305 64 : if (ipsc->lAlphaFieldBlanks(5)) {
5306 0 : runPeriodInput.applyWeekendRule = true;
5307 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5308 64 : runPeriodInput.applyWeekendRule = static_cast<bool>(b);
5309 : } else {
5310 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5311 0 : ErrorsFound = true;
5312 : }
5313 :
5314 : // A6, \field Use Weather File Rain Indicators
5315 64 : if (ipsc->lAlphaFieldBlanks(6)) {
5316 0 : runPeriodInput.useRain = true;
5317 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(6)))) != BooleanSwitch::Invalid) {
5318 64 : runPeriodInput.useRain = static_cast<bool>(b);
5319 : } else {
5320 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6));
5321 0 : ErrorsFound = true;
5322 : }
5323 :
5324 : // A7, \field Use Weather File Snow Indicators
5325 64 : if (ipsc->lAlphaFieldBlanks(7)) {
5326 0 : runPeriodInput.useSnow = true;
5327 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
5328 64 : runPeriodInput.useSnow = static_cast<bool>(b);
5329 : } else {
5330 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7));
5331 0 : ErrorsFound = true;
5332 : }
5333 :
5334 : // A8, \field Treat Weather as Actual
5335 64 : if (ipsc->lAlphaFieldBlanks(8)) {
5336 62 : runPeriodInput.actualWeather = false;
5337 2 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
5338 2 : runPeriodInput.actualWeather = static_cast<bool>(b);
5339 : } else {
5340 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8));
5341 0 : ErrorsFound = true;
5342 : }
5343 :
5344 : // A9, \field First Hour Interpolation Starting Values
5345 64 : if (ipsc->lAlphaFieldBlanks(9) || Util::SameString(ipsc->cAlphaArgs(8), "Hour24")) {
5346 62 : runPeriodInput.firstHrInterpUsingHr1 = false;
5347 2 : } else if (Util::SameString(ipsc->cAlphaArgs(9), "Hour1")) {
5348 2 : runPeriodInput.firstHrInterpUsingHr1 = true;
5349 : } else {
5350 : // fail-safe default
5351 0 : runPeriodInput.firstHrInterpUsingHr1 = false;
5352 : }
5353 :
5354 64 : runPeriodInput.dayOfWeek = static_cast<int>(runPeriodInput.startWeekDay);
5355 64 : runPeriodInput.isLeapYear = isLeapYear(runPeriodInput.startYear);
5356 :
5357 : // calculate the annual start and end days from the user inputted month and day
5358 64 : runPeriodInput.monWeekDay = 0;
5359 64 : if (runPeriodInput.dayOfWeek != 0 && !ErrorsFound) {
5360 62 : SetupWeekDaysByMonth(state, runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.dayOfWeek, runPeriodInput.monWeekDay);
5361 : }
5362 64 : }
5363 :
5364 55 : if (nRunPeriods == 0 && state.dataSysVars->FullAnnualRun) {
5365 0 : ShowWarningError(state, "No Run Periods input but Full Annual Simulation selected. Adding Run Period to 1/1 through 12/31.");
5366 0 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
5367 0 : state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
5368 0 : nRunPeriods = 1;
5369 0 : state.dataGlobal->WeathSimReq = true;
5370 0 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
5371 0 : auto &runPerInput1 = state.dataWeather->RunPeriodInput(1);
5372 0 : runPerInput1.startJulianDate = General::OrdinalDay(runPerInput1.startMonth, runPerInput1.startDay, state.dataWeather->LeapYearAdd);
5373 0 : runPerInput1.endJulianDate = General::OrdinalDay(runPerInput1.endMonth, runPerInput1.endDay, state.dataWeather->LeapYearAdd);
5374 0 : runPerInput1.monWeekDay = 0;
5375 0 : if (runPerInput1.dayOfWeek != 0 && !ErrorsFound) {
5376 0 : SetupWeekDaysByMonth(state, runPerInput1.startMonth, runPerInput1.startDay, runPerInput1.dayOfWeek, runPerInput1.monWeekDay);
5377 : }
5378 55 : } else if (nRunPeriods > 1 && state.dataSysVars->FullAnnualRun) {
5379 0 : nRunPeriods = 1;
5380 : }
5381 55 : }
5382 :
5383 1 : void GetRunPeriodDesignData(EnergyPlusData &state, bool &ErrorsFound)
5384 : {
5385 :
5386 : // SUBROUTINE INFORMATION:
5387 : // AUTHOR Linda Lawrie
5388 : // DATE WRITTEN March 2008
5389 :
5390 : // PURPOSE OF THIS SUBROUTINE:
5391 : // This subroutine gets the run period design info from User input and the
5392 : // simulation dates
5393 :
5394 1 : constexpr std::string_view routineName = "GetRunPeriodDesignData";
5395 : // Call Input Get routine to retrieve annual run data
5396 1 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
5397 1 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
5398 1 : state.dataWeather->TotRunDesPers = RPD1 + RPD2;
5399 :
5400 1 : state.dataWeather->RunPeriodDesignInput.allocate(RPD1 + RPD2);
5401 :
5402 1 : int Count = 0;
5403 1 : auto const &ipsc = state.dataIPShortCut;
5404 1 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileDays";
5405 2 : for (int i = 1; i <= RPD1; ++i) {
5406 : int NumAlphas; // Number of alphas being input
5407 : int NumNumerics; // Number of Numerics being input
5408 : int IOStat; // IO Status when calling get input subroutine
5409 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5410 1 : ipsc->cCurrentModuleObject,
5411 : i,
5412 1 : ipsc->cAlphaArgs,
5413 : NumAlphas,
5414 1 : ipsc->rNumericArgs,
5415 : NumNumerics,
5416 : IOStat,
5417 1 : ipsc->lNumericFieldBlanks,
5418 1 : ipsc->lAlphaFieldBlanks,
5419 1 : ipsc->cAlphaFieldNames,
5420 1 : ipsc->cNumericFieldNames);
5421 :
5422 1 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5423 1 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5424 2 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5425 2 : state.dataWeather->RunPeriodDesignInput.end(),
5426 2 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5427 2 : state.dataWeather->RunPeriodDesignInput.end()) {
5428 0 : ShowSevereDuplicateName(state, eoh);
5429 0 : ErrorsFound = true;
5430 : }
5431 :
5432 1 : ++Count;
5433 :
5434 1 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5435 1 : runPerDesInput.title = newName;
5436 1 : runPerDesInput.periodType = "User Selected WeatherFile RunPeriod (Design)";
5437 :
5438 : // set the start and end day of month from user input
5439 1 : runPerDesInput.startMonth = int(ipsc->rNumericArgs(1));
5440 1 : runPerDesInput.startDay = int(ipsc->rNumericArgs(2));
5441 1 : runPerDesInput.endMonth = int(ipsc->rNumericArgs(3));
5442 1 : runPerDesInput.endDay = int(ipsc->rNumericArgs(4));
5443 :
5444 1 : switch (runPerDesInput.startMonth) {
5445 0 : case 1:
5446 : case 3:
5447 : case 5:
5448 : case 7:
5449 : case 8:
5450 : case 10:
5451 : case 12: {
5452 0 : if (runPerDesInput.startDay > 31) {
5453 0 : ShowSevereError(state,
5454 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5455 0 : ipsc->cCurrentModuleObject,
5456 0 : runPerDesInput.title,
5457 0 : ipsc->cNumericFieldNames(2),
5458 0 : runPerDesInput.startDay));
5459 0 : ErrorsFound = true;
5460 : }
5461 0 : } break;
5462 1 : case 4:
5463 : case 6:
5464 : case 9:
5465 : case 11: {
5466 1 : if (runPerDesInput.startDay > 30) {
5467 2 : ShowSevereError(state,
5468 3 : format("{}: object={} {} invalid (Day of Month) [{}]",
5469 1 : ipsc->cCurrentModuleObject,
5470 1 : runPerDesInput.title,
5471 1 : ipsc->cNumericFieldNames(2),
5472 1 : runPerDesInput.startDay));
5473 1 : ErrorsFound = true;
5474 : }
5475 1 : } break;
5476 0 : case 2: {
5477 0 : if (runPerDesInput.startDay > 28 + state.dataWeather->LeapYearAdd) {
5478 0 : ShowSevereError(state,
5479 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5480 0 : ipsc->cCurrentModuleObject,
5481 0 : runPerDesInput.title,
5482 0 : ipsc->cNumericFieldNames(2),
5483 0 : runPerDesInput.startDay));
5484 0 : ErrorsFound = true;
5485 : }
5486 0 : } break;
5487 0 : default: {
5488 0 : ShowSevereError(state,
5489 0 : format("{}: object={} {} invalid (Month) [{}]",
5490 0 : ipsc->cCurrentModuleObject,
5491 0 : runPerDesInput.title,
5492 0 : ipsc->cNumericFieldNames(1),
5493 0 : runPerDesInput.startMonth));
5494 0 : ErrorsFound = true;
5495 0 : } break;
5496 : } // switch
5497 :
5498 1 : if (ipsc->lAlphaFieldBlanks(2)) {
5499 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5500 : } else {
5501 1 : runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
5502 1 : if (runPerDesInput.dayOfWeek < 1 || runPerDesInput.dayOfWeek == 8) {
5503 0 : ShowWarningError(state,
5504 0 : format("{}: object={} {} invalid (Day of Week) [{} for Start is not Valid, Monday will be Used.",
5505 0 : ipsc->cCurrentModuleObject,
5506 0 : runPerDesInput.title,
5507 0 : ipsc->cAlphaFieldNames(1),
5508 0 : ipsc->cAlphaArgs(1)));
5509 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5510 : }
5511 : }
5512 :
5513 : BooleanSwitch b;
5514 1 : if (ipsc->lAlphaFieldBlanks(3)) {
5515 0 : runPerDesInput.useDST = true;
5516 1 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5517 1 : runPerDesInput.useDST = static_cast<bool>(b);
5518 : } else {
5519 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5520 0 : ErrorsFound = true;
5521 : }
5522 :
5523 1 : if (ipsc->lAlphaFieldBlanks(4)) {
5524 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5525 1 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5526 1 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5527 : } else {
5528 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5529 0 : ErrorsFound = true;
5530 : }
5531 :
5532 : // calculate the annual start and end days from the user inputted month and day
5533 1 : runPerDesInput.startJulianDate = General::OrdinalDay(runPerDesInput.startMonth, runPerDesInput.startDay, state.dataWeather->LeapYearAdd);
5534 1 : runPerDesInput.endJulianDate = General::OrdinalDay(runPerDesInput.endMonth, runPerDesInput.endDay, state.dataWeather->LeapYearAdd);
5535 1 : if (runPerDesInput.startJulianDate <= runPerDesInput.endJulianDate) {
5536 1 : runPerDesInput.totalDays = (runPerDesInput.endJulianDate - runPerDesInput.startJulianDate + 1) * runPerDesInput.numSimYears;
5537 : } else {
5538 0 : runPerDesInput.totalDays = (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - runPerDesInput.startJulianDate + 1 +
5539 0 : runPerDesInput.endJulianDate) *
5540 0 : runPerDesInput.numSimYears;
5541 : }
5542 1 : runPerDesInput.monWeekDay = 0;
5543 1 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5544 1 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5545 0 : SetupWeekDaysByMonth(state,
5546 : runPeriodDesignInput1.startMonth,
5547 : runPeriodDesignInput1.startDay,
5548 : runPeriodDesignInput1.dayOfWeek,
5549 0 : runPeriodDesignInput1.monWeekDay);
5550 : }
5551 1 : }
5552 :
5553 1 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileConditionType";
5554 1 : for (int i = 1; i <= RPD2; ++i) {
5555 : int NumAlphas; // Number of alphas being input
5556 : int NumNumerics; // Number of Numerics being input
5557 : int IOStat; // IO Status when calling get input subroutine
5558 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5559 0 : ipsc->cCurrentModuleObject,
5560 : i,
5561 0 : ipsc->cAlphaArgs,
5562 : NumAlphas,
5563 0 : ipsc->rNumericArgs,
5564 : NumNumerics,
5565 : IOStat,
5566 0 : ipsc->lNumericFieldBlanks,
5567 0 : ipsc->lAlphaFieldBlanks,
5568 0 : ipsc->cAlphaFieldNames,
5569 0 : ipsc->cNumericFieldNames);
5570 0 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5571 :
5572 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5573 0 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5574 0 : state.dataWeather->RunPeriodDesignInput.end(),
5575 0 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5576 0 : state.dataWeather->RunPeriodDesignInput.end()) {
5577 0 : ShowSevereDuplicateName(state, eoh);
5578 0 : ErrorsFound = true;
5579 : }
5580 :
5581 0 : ++Count;
5582 0 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5583 0 : runPerDesInput.title = ipsc->cAlphaArgs(1);
5584 0 : runPerDesInput.periodType = "User Selected WeatherFile Typical/Extreme Period (Design)=" + ipsc->cAlphaArgs(2);
5585 :
5586 : // Period Selection
5587 0 : if (ipsc->lAlphaFieldBlanks(2)) {
5588 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
5589 0 : ErrorsFound = true;
5590 : } else {
5591 0 : int WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue);
5592 0 : if (WhichPeriod == 0) {
5593 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue1);
5594 : if (WhichPeriod != 0) {
5595 : }
5596 : }
5597 0 : if (WhichPeriod == 0) {
5598 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue2);
5599 0 : if (WhichPeriod != 0) {
5600 0 : ShowWarningError(state,
5601 0 : format("{}: object={} {}={} matched to {}",
5602 0 : ipsc->cCurrentModuleObject,
5603 0 : runPerDesInput.title,
5604 0 : ipsc->cAlphaFieldNames(2),
5605 0 : ipsc->cAlphaArgs(2),
5606 0 : state.dataWeather->TypicalExtremePeriods(WhichPeriod).MatchValue2));
5607 : }
5608 : }
5609 0 : if (WhichPeriod == 0) {
5610 0 : ShowSevereError(state,
5611 0 : format("{}: object={} {} invalid (not on Weather File)={}",
5612 0 : ipsc->cCurrentModuleObject,
5613 0 : runPerDesInput.title,
5614 0 : ipsc->cAlphaFieldNames(2),
5615 0 : ipsc->cAlphaArgs(2)));
5616 0 : ErrorsFound = true;
5617 : } else {
5618 0 : auto const &typicalExtPer = state.dataWeather->TypicalExtremePeriods(WhichPeriod);
5619 0 : runPerDesInput.startDay = typicalExtPer.StartDay;
5620 0 : runPerDesInput.startMonth = typicalExtPer.StartMonth;
5621 0 : runPerDesInput.startJulianDate = typicalExtPer.StartJDay;
5622 0 : runPerDesInput.endDay = typicalExtPer.EndDay;
5623 0 : runPerDesInput.endMonth = typicalExtPer.EndMonth;
5624 0 : runPerDesInput.endJulianDate = typicalExtPer.EndJDay;
5625 0 : runPerDesInput.totalDays = typicalExtPer.TotalDays;
5626 : }
5627 : }
5628 :
5629 0 : if (ipsc->lAlphaFieldBlanks(3)) {
5630 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5631 : } else {
5632 0 : runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(3));
5633 0 : if (runPerDesInput.dayOfWeek < (int)Sched::DayType::Sunday || runPerDesInput.dayOfWeek == (int)Sched::DayType::Holiday) {
5634 : // Sunday-Saturday, SummerDesignDay, WinterDesignDay, CustomDay1, and CustomDay2 are all valid. Holiday is not valid.
5635 : // The input processor should trap invalid key choices, so this should never trip.
5636 0 : assert(false);
5637 : }
5638 : }
5639 :
5640 : BooleanSwitch b;
5641 0 : if (ipsc->lAlphaFieldBlanks(4)) {
5642 0 : runPerDesInput.useDST = true;
5643 0 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5644 0 : runPerDesInput.useDST = static_cast<bool>(b);
5645 : } else {
5646 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5647 0 : ErrorsFound = true;
5648 : }
5649 :
5650 0 : if (ipsc->lAlphaFieldBlanks(5)) {
5651 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5652 0 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5653 0 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5654 : } else {
5655 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5656 0 : ErrorsFound = true;
5657 : }
5658 0 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5659 0 : runPeriodDesignInput1.monWeekDay = 0;
5660 0 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5661 0 : SetupWeekDaysByMonth(state,
5662 : runPeriodDesignInput1.startMonth,
5663 : runPeriodDesignInput1.startDay,
5664 : runPeriodDesignInput1.dayOfWeek,
5665 0 : runPeriodDesignInput1.monWeekDay);
5666 : }
5667 0 : }
5668 1 : }
5669 :
5670 50 : void GetSpecialDayPeriodData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5671 : {
5672 :
5673 : // SUBROUTINE INFORMATION:
5674 : // AUTHOR Linda Lawrie
5675 : // DATE WRITTEN June 2000
5676 :
5677 : // PURPOSE OF THIS SUBROUTINE:
5678 : // This subroutine reads any special day period data from the IDF and
5679 : // processes it into the data structure that will drive the values
5680 : // in the SpecialDayTypes array.
5681 :
5682 : // METHODOLOGY EMPLOYED:
5683 : // Processes the following IDD definition:
5684 : // SpecialDayPeriod,
5685 : // \memo This object sets up holidays/special days to be used during weather file
5686 : // \memo run periods. (These are not used with DesignDay objects.)
5687 : // \memo Depending on the value in the run period, days on the weather file may also
5688 : // \memo be used. However, the weather file specification will take precedence over
5689 : // \memo any specification shown here. (No error message on duplicate days or overlapping
5690 : // \memo days).
5691 : // A1, \field Holiday Name
5692 : // A2, \field StartDate
5693 : // \memo Dates can be several formats:
5694 : // \memo <number>/<number> (month/day)
5695 : // \memo <number> Month
5696 : // \memo Month <number>
5697 : // \memo Months are January, February, March, April, May, June, July, August, September, October, November, December
5698 : // \memo Months can be the first 3 letters of the month
5699 : // \note will eventually allow: 3 Monday April (meaning 3rd Monday in April)
5700 : // N1, \field duration (number of days)
5701 : // A3; \field SpecialDayType
5702 : // \note SpecialDayType selects the schedules appropriate for each day so labeled
5703 : // \type choice
5704 : // \key Holiday
5705 : // \key SummerDesignDay
5706 : // \key WinterDesignDay
5707 : // \key CustomDay1
5708 : // \key CustomDay2
5709 :
5710 50 : constexpr std::string_view routineName = "GetSpecialDayPeriodData";
5711 :
5712 50 : auto const &ipsc = state.dataIPShortCut;
5713 50 : ipsc->cCurrentModuleObject = "RunPeriodControl:SpecialDays";
5714 50 : int NumSpecDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5715 : int Count;
5716 50 : if (allocated(state.dataWeather->SpecialDays)) { // EPW already allocated the array
5717 25 : Count = state.dataWeather->NumSpecialDays - NumSpecDays + 1;
5718 : } else {
5719 25 : state.dataWeather->SpecialDays.allocate(NumSpecDays);
5720 25 : state.dataWeather->NumSpecialDays = NumSpecDays;
5721 25 : Count = 1;
5722 : }
5723 :
5724 50 : Array1D_string AlphArray(3);
5725 : int NumAlphas;
5726 50 : Array1D<Real64> Duration(1);
5727 : int NumNumbers;
5728 : int IOStat;
5729 :
5730 50 : for (int i = 1; i <= NumSpecDays; ++i, ++Count) {
5731 :
5732 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
5733 0 : state, ipsc->cCurrentModuleObject, i, AlphArray, NumAlphas, Duration, NumNumbers, IOStat);
5734 :
5735 0 : auto &specialDay = state.dataWeather->SpecialDays(Count);
5736 :
5737 0 : specialDay.Name = AlphArray(1);
5738 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, specialDay.Name};
5739 :
5740 : int PMonth;
5741 : int PDay;
5742 : int PWeekDay;
5743 : DateType dateType;
5744 0 : General::ProcessDateString(state, AlphArray(2), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
5745 0 : if (dateType == DateType::MonthDay) {
5746 0 : specialDay.dateType = dateType;
5747 0 : specialDay.Month = PMonth;
5748 0 : specialDay.Day = PDay;
5749 0 : specialDay.WeekDay = 0;
5750 0 : specialDay.CompDate = PMonth * 32 + PDay;
5751 0 : specialDay.WthrFile = false;
5752 0 : } else if (dateType != DateType::Invalid) {
5753 0 : specialDay.dateType = dateType;
5754 0 : specialDay.Month = PMonth;
5755 0 : specialDay.Day = PDay;
5756 0 : specialDay.WeekDay = PWeekDay;
5757 0 : specialDay.CompDate = 0;
5758 0 : specialDay.WthrFile = false;
5759 : } else {
5760 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
5761 0 : ErrorsFound = true;
5762 : }
5763 :
5764 0 : if (Duration(1) > 0) {
5765 0 : specialDay.Duration = int(Duration(1));
5766 : } else {
5767 0 : ShowSevereError(
5768 0 : state, format("{}: {} Invalid {}={:.0T}", ipsc->cCurrentModuleObject, AlphArray(1), ipsc->cNumericFieldNames(1), Duration(1)));
5769 0 : ErrorsFound = true;
5770 : }
5771 :
5772 0 : int DayType = getEnumValue(Sched::dayTypeNamesUC, AlphArray(3));
5773 0 : if (DayType == 0) {
5774 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), AlphArray(3));
5775 0 : ErrorsFound = true;
5776 : } else {
5777 0 : specialDay.DayType = DayType;
5778 : }
5779 : }
5780 50 : }
5781 :
5782 5 : void CalcSpecialDayTypes(EnergyPlusData &state)
5783 : {
5784 :
5785 : // SUBROUTINE INFORMATION:
5786 : // AUTHOR Linda Lawrie
5787 : // DATE WRITTEN June 2000
5788 :
5789 : // PURPOSE OF THIS SUBROUTINE:
5790 : // This subroutine creates the array of Special Day types used during
5791 : // the simulation.
5792 :
5793 : // METHODOLOGY EMPLOYED:
5794 : // Sets up the SpecialDayTypes array that then is used during simulation.
5795 :
5796 5 : state.dataWeather->SpecialDayTypes = 0; // Initialize/Reset Special Day Types array
5797 :
5798 5 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
5799 0 : auto const &specialDay = state.dataWeather->SpecialDays(i);
5800 0 : if (specialDay.WthrFile) {
5801 0 : continue;
5802 : }
5803 :
5804 0 : int Warn = 0;
5805 :
5806 0 : int JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd) - 1;
5807 :
5808 0 : for (int j = 1; j <= specialDay.Duration; ++j) {
5809 0 : ++JDay;
5810 0 : if (JDay > 366) {
5811 0 : ShowWarningError(state, format("SpecialDay={} causes index of more than 366, ignoring those beyond 366", specialDay.Name));
5812 : } else {
5813 0 : if (state.dataWeather->SpecialDayTypes(JDay) != 0 && Warn == 0) {
5814 0 : ShowWarningError(state, format("SpecialDay={} attempted overwrite of previous set special day", specialDay.Name));
5815 0 : Warn = 1;
5816 0 : } else if (state.dataWeather->SpecialDayTypes(JDay) == 0) {
5817 0 : state.dataWeather->SpecialDayTypes(JDay) = specialDay.DayType;
5818 : }
5819 : }
5820 : }
5821 : }
5822 5 : }
5823 :
5824 50 : void GetDSTData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5825 : {
5826 :
5827 : // SUBROUTINE INFORMATION:
5828 : // AUTHOR Linda Lawrie
5829 : // DATE WRITTEN August 2000
5830 :
5831 : // PURPOSE OF THIS SUBROUTINE:
5832 : // This subroutine gets a possible "Daylight Saving Period" from the IDF. Using this
5833 : // will overwrite any prior DST data.
5834 :
5835 : // METHODOLOGY EMPLOYED:
5836 : // Processes the following IDD definition:
5837 : // DaylightSavingPeriod,
5838 : // \memo This object sets up the Daylight Saving period for any RunPeriod.
5839 : // \memo Ignores any DaylightSavingperiod values on the weather file and uses this definition.
5840 : // \memo (These are not used with DesignDay objects.)
5841 : // A1, \field StartDate
5842 : // A2, \field EndDate
5843 : // \memo Dates can be several formats:
5844 : // \memo <number>/<number> (month/day)
5845 : // \memo <number> <Month>
5846 : // \memo <Month> <number>
5847 : // \memo <Nth> <Weekday> in <Month)
5848 : // \memo Last <WeekDay> in <Month>
5849 : // \memo <Month> can be January, February, March, April, May, June, July, August, September,
5850 : // October, November, December
5851 : // \memo Months can be the first 3 letters of the month
5852 : // \memo <Weekday> can be Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
5853 : // \memo <Nth> can be 1 or 1st, 2 or 2nd, etc. up to 5(?)
5854 :
5855 50 : constexpr std::string_view routineName = "GetDSTData";
5856 :
5857 50 : auto const &ipsc = state.dataIPShortCut;
5858 50 : ipsc->cCurrentModuleObject = "RunPeriodControl:DaylightSavingTime";
5859 50 : int NumFound = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5860 :
5861 50 : if (NumFound == 1) {
5862 : int NumAlphas;
5863 : int IOStat;
5864 : int NumNumbers;
5865 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5866 0 : ipsc->cCurrentModuleObject,
5867 : 1,
5868 0 : ipsc->cAlphaArgs,
5869 : NumAlphas,
5870 0 : ipsc->rNumericArgs,
5871 : NumNumbers,
5872 : IOStat,
5873 0 : ipsc->lNumericFieldBlanks,
5874 0 : ipsc->lAlphaFieldBlanks,
5875 0 : ipsc->cAlphaFieldNames,
5876 0 : ipsc->cNumericFieldNames);
5877 :
5878 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
5879 :
5880 0 : if (NumAlphas != 2) {
5881 0 : ShowSevereError(state, format("{}: Insufficient fields, must have Start AND End Dates", ipsc->cCurrentModuleObject));
5882 0 : ErrorsFound = true;
5883 : } else { // Correct number of arguments
5884 0 : General::ProcessDateString(state,
5885 0 : ipsc->cAlphaArgs(1),
5886 0 : state.dataWeather->IDFDST.StMon,
5887 0 : state.dataWeather->IDFDST.StDay,
5888 0 : state.dataWeather->IDFDST.StWeekDay,
5889 0 : state.dataWeather->IDFDST.StDateType,
5890 : ErrorsFound);
5891 0 : if (state.dataWeather->IDFDST.StDateType == DateType::Invalid) {
5892 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
5893 0 : ErrorsFound = true;
5894 : }
5895 0 : General::ProcessDateString(state,
5896 0 : ipsc->cAlphaArgs(2),
5897 0 : state.dataWeather->IDFDST.EnMon,
5898 0 : state.dataWeather->IDFDST.EnDay,
5899 0 : state.dataWeather->IDFDST.EnWeekDay,
5900 0 : state.dataWeather->IDFDST.EnDateType,
5901 : ErrorsFound);
5902 0 : if (state.dataWeather->IDFDST.EnDateType == DateType::Invalid) {
5903 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
5904 0 : ErrorsFound = true;
5905 : }
5906 0 : state.dataWeather->IDFDaylightSaving = true;
5907 : }
5908 50 : } else if (NumFound > 1) {
5909 0 : ShowSevereError(state, format("{}: Too many objects in Input File, only one allowed.", ipsc->cCurrentModuleObject));
5910 0 : ErrorsFound = true;
5911 : }
5912 50 : }
5913 :
5914 106 : void GetDesignDayData(EnergyPlusData &state,
5915 : int TotDesDays, // Total number of Design days to Setup
5916 : bool &ErrorsFound)
5917 : {
5918 :
5919 : // SUBROUTINE INFORMATION:
5920 : // AUTHOR Richard Liesen
5921 : // DATE WRITTEN September 1997
5922 :
5923 : // PURPOSE OF THIS SUBROUTINE:
5924 : // This subroutine retrieves the design day info from user input file
5925 : // which is later to be used in the Setup Design Day Routine.
5926 :
5927 : // REFERENCES:
5928 : // SizingPeriod:DesignDay,
5929 : // A1, \field Name
5930 : // N1, \field Month
5931 : // N2, \field Day of Month
5932 : // A2, \field Day Type
5933 : // N3, \field Maximum Dry-Bulb Temperature
5934 : // N4, \field Daily Dry-Bulb Temperature Range
5935 : // A3, \field Dry-Bulb Temperature Range Modifier Type
5936 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
5937 : // A5, \field Humidity Condition Type
5938 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
5939 : // A6, \field Humidity Condition Day Schedule Name
5940 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
5941 : // N7, \field Enthalpy at Maximum Dry-Bulb !will require units transition.
5942 : // N8, \field Daily Wet-Bulb Temperature Range
5943 : // N9, \field Barometric Pressure
5944 : // N10, \field Wind Speed
5945 : // N11, \field Wind Direction
5946 : // A7, \field Rain Indicator
5947 : // A8, \field Snow Indicator
5948 : // A9, \field Daylight Saving Time Indicator
5949 : // A10, \field Solar Model Indicator
5950 : // A11, \field Beam Solar Day Schedule Name
5951 : // A12, \field Diffuse Solar Day Schedule Name
5952 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
5953 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
5954 : // N14; \field Sky Clearness
5955 :
5956 : static constexpr std::array<std::string_view, static_cast<int>(DesDayHumIndType::Num)> DesDayHumIndTypeStringRep = {
5957 : "Wetbulb [C]",
5958 : "Dewpoint [C]",
5959 : "Enthalpy [J/kg]",
5960 : "Humidity Ratio []",
5961 : "Schedule []",
5962 : "WetBulbProfileDefaultMultipliers []",
5963 : "WetBulbProfileDifferenceSchedule []",
5964 : "WetBulbProfileMultiplierSchedule []"};
5965 :
5966 : // Below are the 2009 fractions, HOF, Chap 14, Table 6
5967 : static constexpr std::array<Real64, 24> DefaultTempRangeMult = {0.88, 0.92, 0.95, 0.98, 1.0, 0.98, 0.91, 0.74, 0.55, 0.38, 0.23, 0.13,
5968 : 0.05, 0.00, 0.00, 0.06, 0.14, 0.24, 0.39, 0.50, 0.59, 0.68, 0.75, 0.82};
5969 :
5970 : static constexpr std::string_view routineName = "GetDesignDayData";
5971 :
5972 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5973 : Constant::Units unitType;
5974 :
5975 106 : state.dataWeather->DesDayInput.allocate(TotDesDays); // Allocate the array to the # of DD's
5976 106 : state.dataWeather->desDayMods.allocate(TotDesDays);
5977 288 : for (int iDD = 1; iDD <= TotDesDays; ++iDD) {
5978 182 : state.dataWeather->desDayMods(iDD).allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
5979 : }
5980 :
5981 106 : state.dataWeather->spSiteSchedules.dimension(TotDesDays, Weather::SPSiteSchedules());
5982 :
5983 106 : if (state.dataSysVars->ReverseDD && TotDesDays <= 1) {
5984 0 : ShowSevereError(state, "GetDesignDayData: Reverse Design Day requested but # Design Days <=1");
5985 : }
5986 :
5987 106 : auto const &ipsc = state.dataIPShortCut;
5988 106 : ipsc->cCurrentModuleObject = "SizingPeriod:DesignDay";
5989 288 : for (int iDesDay = 1; iDesDay <= TotDesDays; ++iDesDay) {
5990 :
5991 : int EnvrnNum;
5992 182 : if (!state.dataSysVars->ReverseDD) {
5993 182 : EnvrnNum = iDesDay;
5994 0 : } else if (iDesDay == 1 && TotDesDays > 1) {
5995 0 : EnvrnNum = 2;
5996 0 : } else if (iDesDay == 2) {
5997 0 : EnvrnNum = 1;
5998 : } else {
5999 0 : EnvrnNum = iDesDay;
6000 : }
6001 :
6002 : // Call Input Get routine to retrieve design day data
6003 182 : bool MaxDryBulbEntered = false;
6004 182 : bool PressureEntered = false;
6005 : int NumAlpha; // Number of material alpha names being passed
6006 : int NumNumerics; // Number of material properties being passed
6007 : int IOStat; // IO Status when calling get input subroutine
6008 364 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6009 182 : ipsc->cCurrentModuleObject,
6010 : iDesDay,
6011 182 : ipsc->cAlphaArgs,
6012 : NumAlpha,
6013 182 : ipsc->rNumericArgs,
6014 : NumNumerics,
6015 : IOStat,
6016 182 : ipsc->lNumericFieldBlanks,
6017 182 : ipsc->lAlphaFieldBlanks,
6018 182 : ipsc->cAlphaFieldNames,
6019 182 : ipsc->cNumericFieldNames);
6020 :
6021 182 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
6022 182 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
6023 182 : desDayInput.Title = ipsc->cAlphaArgs(1); // Environment name
6024 182 : envCurr.Title = desDayInput.Title;
6025 :
6026 182 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, desDayInput.Title};
6027 :
6028 : // N3, \field Maximum Dry-Bulb Temperature
6029 : // N4, \field Daily Dry-Bulb Temperature Range
6030 : // N9, \field Barometric Pressure
6031 : // N10, \field Wind Speed
6032 : // N11, \field Wind Direction
6033 182 : desDayInput.MaxDryBulb = ipsc->rNumericArgs(3); // Maximum Dry-Bulb Temperature (C)
6034 182 : MaxDryBulbEntered = !ipsc->lNumericFieldBlanks(3);
6035 182 : desDayInput.DailyDBRange = ipsc->rNumericArgs(4); // Daily dry-bulb temperature range (deltaC)
6036 182 : desDayInput.PressBarom = ipsc->rNumericArgs(9); // Atmospheric/Barometric Pressure (Pascals)
6037 182 : PressureEntered = !ipsc->lNumericFieldBlanks(9);
6038 182 : desDayInput.PressureEntered = PressureEntered;
6039 182 : desDayInput.WindSpeed = ipsc->rNumericArgs(10); // Wind Speed (m/s)
6040 182 : desDayInput.WindDir = mod(ipsc->rNumericArgs(11), 360.0); // Wind Direction
6041 : // (degrees clockwise from North, N=0, E=90, S=180, W=270)
6042 : // N1, \field Month
6043 : // N2, \field Day of Month
6044 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
6045 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
6046 : // N8, \field Daily Wet-Bulb Temperature Range
6047 182 : desDayInput.Month = int(ipsc->rNumericArgs(1)); // Month of Year ( 1 - 12 )
6048 182 : desDayInput.DayOfMonth = int(ipsc->rNumericArgs(2)); // Day of Month ( 1 - 31 )
6049 182 : desDayInput.TauB = ipsc->rNumericArgs(12); // beam tau >= 0
6050 182 : desDayInput.TauD = ipsc->rNumericArgs(13); // diffuse tau >= 0
6051 182 : desDayInput.DailyWBRange = ipsc->rNumericArgs(8); // Daily wet-bulb temperature range (deltaC)
6052 :
6053 : // N14; \field Sky Clearness
6054 182 : desDayInput.SkyClear = ipsc->rNumericArgs(14); // Sky Clearness (0 to 1)
6055 :
6056 : // N15, \field Maximum Warmup Days Between Sizing Periods
6057 182 : if (ipsc->lNumericFieldBlanks(15)) {
6058 : // Default to -1 if not input
6059 182 : desDayInput.maxWarmupDays = -1;
6060 : } else {
6061 0 : desDayInput.maxWarmupDays = int(ipsc->rNumericArgs(15));
6062 : }
6063 : // A13, \field Begin Environment Reset Mode
6064 182 : if (ipsc->lAlphaFieldBlanks(13)) {
6065 180 : desDayInput.suppressBegEnvReset = false;
6066 : } else {
6067 2 : if (Util::SameString(ipsc->cAlphaArgs(13), "FullResetAtBeginEnvironment")) {
6068 2 : desDayInput.suppressBegEnvReset = false;
6069 0 : } else if (Util::SameString(ipsc->cAlphaArgs(13), "SuppressThermalResetAtBeginEnvironment")) {
6070 0 : desDayInput.suppressBegEnvReset = true;
6071 : }
6072 : }
6073 : // for PerformancePrecisionTradeoffs
6074 182 : if (state.dataEnvrn->forceBeginEnvResetSuppress) {
6075 0 : desDayInput.suppressBegEnvReset = true;
6076 : }
6077 : // A7, \field Rain Indicator
6078 : BooleanSwitch b;
6079 :
6080 182 : if (ipsc->lAlphaFieldBlanks(7)) {
6081 0 : desDayInput.RainInd = 0;
6082 182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
6083 182 : desDayInput.RainInd = (int)b;
6084 : } else {
6085 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7), "No");
6086 0 : desDayInput.RainInd = 0;
6087 : }
6088 :
6089 : // A8, \field Snow Indicator
6090 182 : if (ipsc->lAlphaFieldBlanks(8)) {
6091 0 : desDayInput.SnowInd = 0;
6092 182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
6093 182 : desDayInput.SnowInd = (int)b;
6094 : } else {
6095 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8), "No");
6096 0 : desDayInput.SnowInd = 0;
6097 : }
6098 :
6099 : // A3, \field Dry-Bulb Temperature Range Modifier Type
6100 : // check DB profile input
6101 182 : if (ipsc->lAlphaFieldBlanks(3)) {
6102 166 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
6103 32 : } else if ((desDayInput.dryBulbRangeType = static_cast<DesDayDryBulbRangeType>(
6104 16 : getEnumValue(DesDayDryBulbRangeTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))))) != DesDayDryBulbRangeType::Invalid) {
6105 : } else {
6106 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6107 0 : ErrorsFound = true;
6108 0 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
6109 : }
6110 :
6111 : // std::string units; // not used
6112 182 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
6113 : // units = "[]";
6114 0 : unitType = Constant::Units::None;
6115 182 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
6116 : // units = "[deltaC]";
6117 0 : unitType = Constant::Units::deltaC;
6118 182 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
6119 : // units = "[C]";
6120 0 : unitType = Constant::Units::C;
6121 : }
6122 :
6123 182 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile && !MaxDryBulbEntered && ipsc->cAlphaArgs(3) != "invalid field") {
6124 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(3), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6125 0 : ErrorsFound = true;
6126 : }
6127 :
6128 : // Assume either "multiplier" option will make full use of range...
6129 182 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Difference &&
6130 182 : desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
6131 182 : Real64 testval = desDayInput.MaxDryBulb - desDayInput.DailyDBRange;
6132 182 : if (testval < -90.0 || testval > 70.0) {
6133 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6134 0 : ShowContinueError(state, format("{} ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(3), testval));
6135 0 : ErrorsFound = true;
6136 : }
6137 : }
6138 :
6139 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
6140 182 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Default) {
6141 : // Default dry-bulb temperature Range
6142 182 : Real64 LastHrValue = DefaultTempRangeMult[23];
6143 4550 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
6144 29208 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
6145 24840 : Real64 WNow = state.dataWeather->Interpolation(ts);
6146 24840 : Real64 WPrev = 1.0 - WNow;
6147 24840 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutDryBulbTemp =
6148 24840 : LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
6149 : }
6150 4368 : LastHrValue = DefaultTempRangeMult[hour - 1];
6151 : }
6152 :
6153 0 : } else if (ipsc->lAlphaFieldBlanks(4)) {
6154 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaFieldNames(3), "SCHEDULE");
6155 0 : ErrorsFound = true;
6156 0 : } else if ((desDayInput.tempRangeSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(4))) == nullptr) {
6157 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
6158 0 : ErrorsFound = true;
6159 :
6160 : } else {
6161 0 : std::vector<Real64> const &dayVals = desDayInput.tempRangeSched->getDayVals(state);
6162 0 : auto &desDayModEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6163 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6164 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6165 0 : desDayModEnvrn(ts + 1, hr + 1).OutDryBulbTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6166 : }
6167 : }
6168 :
6169 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6170 0 : state.dataWeather->spSiteSchedNums.end(),
6171 0 : desDayInput.tempRangeSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6172 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.tempRangeSched->Num);
6173 0 : SetupOutputVariable(state,
6174 : "Sizing Period Site Drybulb Temperature Range Modifier Schedule Value",
6175 : unitType,
6176 0 : state.dataWeather->spSiteSchedules(EnvrnNum).OutDryBulbTemp,
6177 : OutputProcessor::TimeStepType::Zone,
6178 : OutputProcessor::StoreType::Average,
6179 0 : ipsc->cAlphaArgs(4));
6180 : }
6181 :
6182 0 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
6183 0 : if (!desDayInput.tempRangeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
6184 0 : Sched::ShowSevereBadMinMax(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0, Clusive::In, 1.0);
6185 0 : ErrorsFound = true;
6186 : }
6187 0 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) { // delta, must be > 0.0
6188 0 : if (!desDayInput.tempRangeSched->checkMinVal(state, Clusive::In, 0.0)) {
6189 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0);
6190 0 : ErrorsFound = true;
6191 : }
6192 : }
6193 :
6194 0 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6195 0 : Real64 testval = std::numeric_limits<Real64>::min();
6196 0 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
6197 0 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
6198 0 : if (desDayModsEnvrn(iTS, iHr).OutDryBulbTemp > testval) {
6199 0 : testval = desDayModsEnvrn(iTS, iHr).OutDryBulbTemp;
6200 : }
6201 : }
6202 : }
6203 :
6204 0 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
6205 0 : if (MaxDryBulbEntered) {
6206 0 : ShowWarningError(state, format("{}=\"{}\", data override.", ipsc->cCurrentModuleObject, desDayInput.Title));
6207 0 : ShowContinueError(state, format("..{}=[{:.2R}] will be overwritten.", ipsc->cNumericFieldNames(3), desDayInput.MaxDryBulb));
6208 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3)));
6209 0 : ShowContinueError(state, format("..with max value=[{:.2R}].", testval));
6210 : }
6211 0 : desDayInput.MaxDryBulb = testval;
6212 : }
6213 :
6214 0 : testval = desDayInput.MaxDryBulb - testval;
6215 0 : if (testval < -90.0 || testval > 70.0) {
6216 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6217 : // should this be cNumericFieldNames?
6218 0 : ShowContinueError(state, format("{} = ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(4), testval));
6219 0 : ErrorsFound = true;
6220 : }
6221 : }
6222 :
6223 : // A5, \field Humidity Condition Type
6224 182 : desDayInput.HumIndType = static_cast<DesDayHumIndType>(getEnumValue(DesDayHumIndTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
6225 :
6226 182 : switch (desDayInput.HumIndType) {
6227 174 : case DesDayHumIndType::WetBulb: {
6228 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
6229 174 : if (ipsc->lNumericFieldBlanks(5)) {
6230 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6231 0 : ErrorsFound = true;
6232 : } else {
6233 174 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6234 : }
6235 :
6236 174 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6237 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6238 0 : ShowContinueError(
6239 : state,
6240 0 : format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - WetBulb", desDayInput.HumIndValue));
6241 0 : ErrorsFound = true;
6242 : }
6243 174 : } break;
6244 :
6245 0 : case DesDayHumIndType::DewPoint: {
6246 0 : if (ipsc->lNumericFieldBlanks(5)) {
6247 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6248 0 : ErrorsFound = true;
6249 : } else {
6250 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6251 : }
6252 :
6253 0 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6254 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6255 0 : ShowContinueError(
6256 : state,
6257 0 : format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - DewPoint", desDayInput.HumIndValue));
6258 0 : ErrorsFound = true;
6259 : }
6260 0 : } break;
6261 :
6262 0 : case DesDayHumIndType::HumRatio: {
6263 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
6264 0 : if (ipsc->lNumericFieldBlanks(6)) {
6265 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(6), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6266 0 : ErrorsFound = true;
6267 : } else {
6268 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(6); // Humidity Indicating Conditions at Max Dry-Bulb
6269 : }
6270 :
6271 0 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 0.03) {
6272 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6273 0 : ShowContinueError(
6274 : state,
6275 0 : format("{} = {:.2R} is out of range [0.0, 0.03]", ipsc->cAlphaFieldNames(5) + " - Humidity-Ratio", desDayInput.HumIndValue));
6276 0 : ErrorsFound = true;
6277 : }
6278 0 : } break;
6279 :
6280 1 : case DesDayHumIndType::Enthalpy: {
6281 : // N7, \field Enthalpy at Maximum Dry-Bulb {J/kg}.
6282 1 : if (ipsc->lNumericFieldBlanks(7)) {
6283 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(7), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6284 0 : ErrorsFound = true;
6285 : } else {
6286 1 : desDayInput.HumIndValue = ipsc->rNumericArgs(7); // Humidity Indicating Conditions at Max Dry-Bulb
6287 : }
6288 :
6289 1 : desDayInput.HumIndType = DesDayHumIndType::Enthalpy;
6290 1 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 130000.0) {
6291 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6292 0 : ShowContinueError(
6293 : state,
6294 0 : format("{} = {.0R} is out of range [0.0, 130000.0]", ipsc->cAlphaFieldNames(5) + " - Enthalpy", desDayInput.HumIndValue));
6295 0 : ErrorsFound = true;
6296 : }
6297 1 : } break;
6298 :
6299 0 : case DesDayHumIndType::RelHumSch: {
6300 : // units = "[%]";
6301 0 : unitType = Constant::Units::Perc;
6302 0 : } break;
6303 :
6304 0 : case DesDayHumIndType::WBProfMul: {
6305 : // units = "[]";
6306 0 : unitType = Constant::Units::None;
6307 0 : if (ipsc->lNumericFieldBlanks(5)) {
6308 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6309 0 : ErrorsFound = true;
6310 : } else {
6311 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6312 : }
6313 0 : } break;
6314 :
6315 0 : case DesDayHumIndType::WBProfDif: {
6316 : // units = "[]";
6317 0 : unitType = Constant::Units::None;
6318 0 : if (ipsc->lNumericFieldBlanks(5)) {
6319 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6320 0 : ErrorsFound = true;
6321 : } else {
6322 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6323 : }
6324 0 : } break;
6325 :
6326 7 : case DesDayHumIndType::WBProfDef: {
6327 7 : if (ipsc->lNumericFieldBlanks(5)) {
6328 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6329 0 : ErrorsFound = true;
6330 : } else {
6331 7 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6332 : }
6333 7 : } break;
6334 :
6335 0 : default: {
6336 0 : ShowWarningError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6337 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6338 0 : ShowContinueError(state, "WetBulb will be used. Maximum Dry Bulb will be used as WetBulb at Maximum Dry Bulb.");
6339 0 : desDayInput.HumIndType = DesDayHumIndType::WetBulb;
6340 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(3);
6341 0 : } break;
6342 : } // switch (desDayInput.HumIndType)
6343 :
6344 : // resolve humidity schedule if needed
6345 : // A6, \field Humidity Condition Day Schedule Name
6346 182 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch || desDayInput.HumIndType == DesDayHumIndType::WBProfMul ||
6347 182 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6348 0 : if (ipsc->lAlphaFieldBlanks(6)) {
6349 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6350 0 : ErrorsFound = true;
6351 0 : } else if ((desDayInput.humIndSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(6))) == nullptr) {
6352 0 : ShowWarningItemNotFound(state,
6353 : eoh,
6354 0 : ipsc->cAlphaFieldNames(6),
6355 0 : ipsc->cAlphaArgs(6),
6356 : "Default Humidity (constant for day using Humidity Indicator Temp).");
6357 : // reset HumIndType ?
6358 : } else {
6359 0 : std::vector<Real64> const &dayVals = desDayInput.humIndSched->getDayVals(state);
6360 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6361 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6362 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6363 0 : desDayModsEnvrn(ts + 1, hr + 1).OutRelHum = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6364 : }
6365 : }
6366 :
6367 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6368 0 : state.dataWeather->spSiteSchedNums.end(),
6369 0 : desDayInput.humIndSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6370 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.humIndSched->Num);
6371 0 : SetupOutputVariable(state,
6372 : "Sizing Period Site Humidity Condition Schedule Value",
6373 : unitType,
6374 0 : state.dataWeather->spSiteSchedules(EnvrnNum).OutRelHum,
6375 : OutputProcessor::TimeStepType::Zone,
6376 : OutputProcessor::StoreType::Average,
6377 0 : ipsc->cAlphaArgs(6));
6378 : }
6379 :
6380 0 : switch (desDayInput.HumIndType) {
6381 0 : case DesDayHumIndType::RelHumSch: {
6382 0 : if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 100.0)) {
6383 0 : Sched::ShowSevereBadMinMax(
6384 0 : state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 100.0);
6385 0 : ErrorsFound = true;
6386 : }
6387 0 : } break;
6388 0 : case DesDayHumIndType::WBProfMul: {
6389 : // multiplier: use schedule value, check 0 <= v <= 1
6390 0 : if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
6391 0 : Sched::ShowSevereBadMinMax(
6392 0 : state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 1.0);
6393 0 : ErrorsFound = true;
6394 : }
6395 0 : } break;
6396 0 : case DesDayHumIndType::WBProfDif: {
6397 0 : if (!desDayInput.humIndSched->checkMinVal(state, Clusive::In, 0.0)) {
6398 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0);
6399 0 : ErrorsFound = true;
6400 : }
6401 0 : } break;
6402 0 : default: {
6403 0 : } break;
6404 : } // switch (desDayInput.HumIndType)
6405 : } // if (desDayInput.HumIndSchPtr == 0)
6406 :
6407 182 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
6408 : // re WetBulbProfileDefaultMultipliers
6409 7 : Real64 LastHrValue = DefaultTempRangeMult[23];
6410 175 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
6411 456 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
6412 288 : Real64 WNow = state.dataWeather->Interpolation(ts);
6413 288 : Real64 WPrev = 1.0 - WNow;
6414 288 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutRelHum = LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
6415 : }
6416 168 : LastHrValue = DefaultTempRangeMult[hour - 1];
6417 : }
6418 : }
6419 :
6420 : // verify that design WB or DP <= design DB
6421 182 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint || desDayInput.HumIndType == DesDayHumIndType::WetBulb ||
6422 8 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::WBProfDef ||
6423 1 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6424 181 : if (desDayInput.HumIndValue > desDayInput.MaxDryBulb) {
6425 0 : ShowWarningError(state, format("{}=\"{}\", range check data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6426 0 : ShowContinueError(state,
6427 0 : format("..Humidity Indicator Temperature at Max Temperature={:.1R} > Max DryBulb={:.1R}",
6428 0 : desDayInput.HumIndValue,
6429 0 : desDayInput.MaxDryBulb));
6430 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6431 0 : ShowContinueError(state, "..Conditions for day will be set to Relative Humidity = 100%");
6432 0 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint) {
6433 0 : desDayInput.DewPointNeedsSet = true;
6434 : } else {
6435 : // wet-bulb
6436 0 : desDayInput.HumIndValue = desDayInput.MaxDryBulb;
6437 : }
6438 : }
6439 : }
6440 :
6441 : // A10, \field Solar Model Indicator
6442 182 : if (ipsc->lAlphaFieldBlanks(10)) {
6443 0 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6444 364 : } else if ((desDayInput.solarModel = static_cast<DesDaySolarModel>(
6445 182 : getEnumValue(DesDaySolarModelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(10))))) != DesDaySolarModel::Invalid) {
6446 : } else {
6447 0 : ShowWarningInvalidKey(state, eoh, ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "ASHRAE ClearSky");
6448 0 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6449 : }
6450 :
6451 182 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
6452 : // A11, \field Beam Solar Day Schedule Name
6453 0 : if (ipsc->lAlphaFieldBlanks(11)) {
6454 : // should have entered beam schedule
6455 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(11));
6456 0 : ErrorsFound = true;
6457 0 : } else if ((desDayInput.beamSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(11))) == nullptr) {
6458 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11));
6459 0 : ErrorsFound = true;
6460 : } else {
6461 0 : std::vector<Real64> const &dayVals = desDayInput.beamSolarSched->getDayVals(state);
6462 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6463 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6464 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6465 0 : desDayModsEnvrn(ts + 1, hr + 1).BeamSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6466 : }
6467 : }
6468 :
6469 0 : unitType = Constant::Units::W_m2;
6470 : // units = "[W/m2]";
6471 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6472 0 : state.dataWeather->spSiteSchedNums.end(),
6473 0 : desDayInput.beamSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6474 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.beamSolarSched->Num);
6475 0 : SetupOutputVariable(state,
6476 : "Sizing Period Site Beam Solar Schedule Value",
6477 : unitType,
6478 0 : state.dataWeather->spSiteSchedules(EnvrnNum).BeamSolarRad,
6479 : OutputProcessor::TimeStepType::Zone,
6480 : OutputProcessor::StoreType::Average,
6481 0 : ipsc->cAlphaArgs(11));
6482 : }
6483 :
6484 0 : if (!desDayInput.beamSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
6485 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11), Clusive::In, 0.0);
6486 0 : ErrorsFound = true;
6487 : }
6488 : }
6489 :
6490 : // A12, \field Diffuse Solar Day Schedule Name
6491 0 : if (ipsc->lAlphaFieldBlanks(12)) {
6492 : // should have entered diffuse schedule
6493 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(12));
6494 0 : ErrorsFound = true;
6495 0 : } else if ((desDayInput.diffuseSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(12))) == nullptr) {
6496 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12));
6497 0 : ErrorsFound = true;
6498 : } else {
6499 0 : std::vector<Real64> const &dayVals = desDayInput.diffuseSolarSched->getDayVals(state);
6500 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6501 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6502 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6503 0 : desDayModsEnvrn(ts + 1, hr + 1).DifSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6504 : }
6505 : }
6506 :
6507 : // units = "[W/m2]";
6508 0 : unitType = Constant::Units::W_m2;
6509 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6510 0 : state.dataWeather->spSiteSchedNums.end(),
6511 0 : desDayInput.diffuseSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6512 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.diffuseSolarSched->Num);
6513 0 : SetupOutputVariable(state,
6514 : "Sizing Period Site Diffuse Solar Schedule Value",
6515 : unitType,
6516 0 : state.dataWeather->spSiteSchedules(EnvrnNum).DifSolarRad,
6517 : OutputProcessor::TimeStepType::Zone,
6518 : OutputProcessor::StoreType::Average,
6519 0 : ipsc->cAlphaArgs(12));
6520 : }
6521 0 : if (!desDayInput.diffuseSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
6522 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12), Clusive::In, 0.0);
6523 0 : ErrorsFound = true;
6524 : }
6525 : }
6526 :
6527 182 : } else if (desDayInput.solarModel == DesDaySolarModel::ASHRAE_ClearSky) {
6528 167 : if (ipsc->lNumericFieldBlanks(14)) {
6529 0 : ShowWarningEmptyField(
6530 0 : state, eoh, ipsc->cNumericFieldNames(14), ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "Zero clear sky (no solar)");
6531 : }
6532 : }
6533 :
6534 : // Validate Design Day Month
6535 :
6536 182 : switch (desDayInput.Month) {
6537 179 : case 1:
6538 : case 3:
6539 : case 5:
6540 : case 7:
6541 : case 8:
6542 : case 10:
6543 : case 12: {
6544 179 : if (desDayInput.DayOfMonth > 31) {
6545 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6546 0 : ShowContinueError(
6547 : state,
6548 0 : format(".. invalid field: {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6549 0 : ErrorsFound = true;
6550 : }
6551 179 : } break;
6552 1 : case 4:
6553 : case 6:
6554 : case 9:
6555 : case 11: {
6556 1 : if (desDayInput.DayOfMonth > 30) {
6557 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6558 0 : ShowContinueError(
6559 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6560 0 : ErrorsFound = true;
6561 : }
6562 1 : } break;
6563 2 : case 2: {
6564 2 : if (desDayInput.DayOfMonth > 28) {
6565 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6566 0 : ShowContinueError(
6567 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6568 0 : ErrorsFound = true;
6569 : }
6570 2 : } break;
6571 0 : default: {
6572 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6573 0 : ShowContinueError(state, format(".. invalid {} invalid (Month) [{}].", ipsc->cNumericFieldNames(1), desDayInput.Month));
6574 0 : ErrorsFound = true;
6575 0 : } break;
6576 : } // switch (desDayInput.Month)
6577 :
6578 : // A9, \field Daylight Saving Time Indicator
6579 182 : if (ipsc->lAlphaFieldBlanks(9)) {
6580 0 : desDayInput.DSTIndicator = 0;
6581 182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(9)))) != BooleanSwitch::Invalid) {
6582 182 : desDayInput.DSTIndicator = (int)b;
6583 : } else {
6584 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(9), ipsc->cAlphaArgs(9), "No");
6585 0 : desDayInput.DSTIndicator = 0;
6586 : }
6587 :
6588 : // A2, \field Day Type
6589 182 : desDayInput.DayType = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
6590 182 : if (desDayInput.DayType <= 0) {
6591 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
6592 0 : ErrorsFound = true;
6593 : }
6594 :
6595 182 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
6596 182 : envCurr.Title = desDayInput.Title;
6597 182 : envCurr.KindOfEnvrn = Constant::KindOfSim::DesignDay;
6598 182 : envCurr.DesignDayNum = EnvrnNum;
6599 182 : envCurr.RunPeriodDesignNum = 0;
6600 182 : envCurr.TotalDays = 1;
6601 182 : envCurr.StartMonth = desDayInput.Month;
6602 182 : envCurr.StartDay = desDayInput.DayOfMonth;
6603 182 : envCurr.EndMonth = envCurr.StartMonth;
6604 182 : envCurr.EndDay = envCurr.StartDay;
6605 182 : envCurr.DayOfWeek = 0;
6606 182 : envCurr.UseDST = false;
6607 182 : envCurr.UseHolidays = false;
6608 182 : envCurr.StartJDay = designDay.DayOfYear;
6609 182 : envCurr.EndJDay = envCurr.StartJDay;
6610 :
6611 : // create predefined report on design day
6612 182 : std::string envTitle = desDayInput.Title;
6613 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDmaxDB, envTitle, desDayInput.MaxDryBulb);
6614 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDrange, envTitle, desDayInput.DailyDBRange);
6615 182 : if (desDayInput.HumIndType != DesDayHumIndType::RelHumSch) {
6616 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, desDayInput.HumIndValue);
6617 : } else {
6618 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, "N/A");
6619 : }
6620 364 : OutputReportPredefined::PreDefTableEntry(
6621 364 : state, state.dataOutRptPredefined->pdchDDhumTyp, envTitle, DesDayHumIndTypeStringRep[(int)desDayInput.HumIndType]);
6622 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindSp, envTitle, desDayInput.WindSpeed);
6623 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindDr, envTitle, desDayInput.WindDir);
6624 : }
6625 106 : }
6626 :
6627 115 : void GetLocationInfo(EnergyPlusData &state, bool &ErrorsFound)
6628 : {
6629 :
6630 : // SUBROUTINE INFORMATION:
6631 : // AUTHOR Richard Liesen
6632 : // DATE WRITTEN October 1997
6633 :
6634 : // PURPOSE OF THIS SUBROUTINE:
6635 : // This subroutine gets the location info from the IDF file; latitude,
6636 : // longitude and time zone number.
6637 :
6638 115 : auto const &ipsc = state.dataIPShortCut;
6639 115 : ipsc->cCurrentModuleObject = "Site:Location";
6640 115 : int const NumLocations = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6641 :
6642 115 : if (NumLocations > 1) {
6643 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6644 0 : ErrorsFound = true;
6645 : }
6646 :
6647 115 : if (NumLocations == 1) {
6648 : int LocNumAlpha; // Number of alpha names being passed
6649 : int LocNumProp; // Number of properties being passed
6650 : int IOStat; // IO Status when calling get input subroutine
6651 112 : Array1D_string LocAlphas(2); // Temporary array to transfer location info (non-numerics)
6652 112 : Array1D<Real64> LocProps(4); // Temporary array to transfer location info (numerics)
6653 : // Call Input Get routine to retrieve Location information
6654 224 : state.dataInputProcessing->inputProcessor->getObjectItem(
6655 112 : state, ipsc->cCurrentModuleObject, 1, LocAlphas, LocNumAlpha, LocProps, LocNumProp, IOStat);
6656 :
6657 : // set latitude, longitude, and time zone number variables
6658 112 : state.dataWeather->LocationTitle = LocAlphas(1);
6659 112 : state.dataEnvrn->Latitude = LocProps(1);
6660 112 : state.dataEnvrn->Longitude = LocProps(2);
6661 112 : state.dataEnvrn->TimeZoneNumber = LocProps(3);
6662 112 : state.dataEnvrn->Elevation = LocProps(4);
6663 112 : if (Util::SameString(LocAlphas(2), "Yes")) {
6664 1 : state.dataWeather->keepUserSiteLocationDefinition = true;
6665 : }
6666 112 : state.dataWeather->LocationGathered = true;
6667 112 : }
6668 115 : }
6669 :
6670 111 : void GetWeatherProperties(EnergyPlusData &state, bool &ErrorsFound)
6671 : {
6672 :
6673 : // SUBROUTINE INFORMATION:
6674 : // AUTHOR Linda Lawrie
6675 : // DATE WRITTEN July 2009
6676 :
6677 : // PURPOSE OF THIS SUBROUTINE:
6678 : // Weather properties are an advanced concept for simulation. Primarily, these properties are
6679 : // used in the test suite runs that have specific requirements for certain properties (such as
6680 : // sky temperature).
6681 :
6682 : // REFERENCES:
6683 : // WeatherProperty:SkyTemperature,
6684 : // \memo This object is used to override internal sky temperature calculations.
6685 : // A1, \field Name
6686 : // \reference DesignDays
6687 : // \note leave blank for RunPeriods (until we name them)
6688 : // \note This field references the applicable design day or runperiod(s) if left blank.
6689 : // A2, \field Calculation Type
6690 : // \type choice
6691 : // \key ScheduleValue
6692 : // \key DifferenceScheduleDryBulbValue
6693 : // \key DifferenceScheduleDewPointValue
6694 : // \key AlgorithmA
6695 : // A3; \field Schedule Name
6696 : // \type object-list
6697 : // \object-list DayScheduleNames
6698 : // \object-list ScheduleNames
6699 :
6700 : static constexpr std::string_view routineName = "GetWeatherProperties";
6701 :
6702 : int Found;
6703 : int envFound;
6704 :
6705 111 : auto const &ipsc = state.dataIPShortCut;
6706 111 : ipsc->cCurrentModuleObject = "WeatherProperty:SkyTemperature";
6707 111 : state.dataWeather->NumWPSkyTemperatures = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6708 :
6709 111 : state.dataWeather->WPSkyTemperature.allocate(state.dataWeather->NumWPSkyTemperatures); // by default, not used.
6710 :
6711 111 : for (int i = 1; i <= state.dataWeather->NumWPSkyTemperatures; ++i) {
6712 : int IOStat;
6713 : int NumAlpha;
6714 : int NumNumerics;
6715 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6716 0 : ipsc->cCurrentModuleObject,
6717 : i,
6718 0 : ipsc->cAlphaArgs,
6719 : NumAlpha,
6720 0 : ipsc->rNumericArgs,
6721 : NumNumerics,
6722 : IOStat,
6723 0 : ipsc->lNumericFieldBlanks,
6724 0 : ipsc->lAlphaFieldBlanks,
6725 0 : ipsc->cAlphaFieldNames,
6726 0 : ipsc->cNumericFieldNames);
6727 :
6728 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
6729 0 : auto &wpSkyTemp = state.dataWeather->WPSkyTemperature(i);
6730 0 : if (ipsc->cAlphaArgs(1).empty()) {
6731 0 : Found = 0;
6732 0 : for (int j = 1; j <= state.dataWeather->NumOfEnvrn; ++j) {
6733 0 : auto &environJ = state.dataWeather->Environment(j);
6734 0 : if (environJ.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
6735 0 : continue;
6736 : }
6737 0 : if (environJ.WP_Type1 != 0) {
6738 0 : ShowSevereError(state,
6739 0 : format("{}: {}=\"{}\", indicated Environment Name already assigned.",
6740 : routineName,
6741 0 : ipsc->cCurrentModuleObject,
6742 0 : ipsc->cAlphaArgs(1)));
6743 0 : if (!environJ.Title.empty()) {
6744 0 : ShowContinueError(state,
6745 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6746 0 : environJ.Title,
6747 0 : ipsc->cCurrentModuleObject,
6748 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6749 : } else {
6750 0 : ShowContinueError(state,
6751 0 : format("... Runperiod Environment, already using {}=\"{}\".",
6752 0 : ipsc->cCurrentModuleObject,
6753 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6754 : }
6755 0 : ErrorsFound = true;
6756 : } else {
6757 0 : environJ.WP_Type1 = i;
6758 0 : Found = j;
6759 : }
6760 : }
6761 0 : if (Found == 0) {
6762 0 : ShowWarningError(state, "GetWeatherProperties: WeatherProperty:SkyTemperature=blank, no run periods found.");
6763 0 : ShowContinueError(state, "...SkyTemperature will not be applied.");
6764 0 : continue;
6765 : }
6766 : } else { // really a name
6767 0 : Found = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataWeather->Environment, &EnvironmentData::Title);
6768 0 : envFound = Found;
6769 0 : if (Found == 0) {
6770 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
6771 0 : ErrorsFound = true;
6772 0 : continue;
6773 : }
6774 :
6775 0 : auto &envrnFound = state.dataWeather->Environment(Found);
6776 0 : if (envrnFound.WP_Type1 != 0) {
6777 0 : ShowSevereError(state,
6778 0 : format("{}:{}=\"{}\", indicated Environment Name already assigned.",
6779 : routineName,
6780 0 : ipsc->cCurrentModuleObject,
6781 0 : ipsc->cAlphaArgs(1)));
6782 0 : ShowContinueError(state,
6783 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6784 0 : envrnFound.Title,
6785 0 : ipsc->cCurrentModuleObject,
6786 0 : state.dataWeather->WPSkyTemperature(envrnFound.WP_Type1).Name));
6787 0 : ErrorsFound = true;
6788 : } else {
6789 0 : state.dataWeather->Environment(Found).WP_Type1 = i;
6790 : }
6791 : }
6792 :
6793 0 : wpSkyTemp.Name = !ipsc->lAlphaFieldBlanks(1) ? ipsc->cAlphaArgs(1) : "All RunPeriods";
6794 :
6795 : // Validate Calculation Type.
6796 : // std::string units;
6797 : Constant::Units unitType;
6798 0 : wpSkyTemp.skyTempModel = static_cast<SkyTempModel>(getEnumValue(Weather::SkyTempModelNamesUC, ipsc->cAlphaArgs(2)));
6799 :
6800 0 : switch (wpSkyTemp.skyTempModel) {
6801 0 : case SkyTempModel::ScheduleValue: {
6802 0 : wpSkyTemp.IsSchedule = true;
6803 : // units = "[C]";
6804 0 : unitType = Constant::Units::C;
6805 0 : } break;
6806 0 : case SkyTempModel::DryBulbDelta:
6807 : case SkyTempModel::DewPointDelta: {
6808 0 : wpSkyTemp.IsSchedule = true;
6809 : // units = "[deltaC]";
6810 0 : unitType = Constant::Units::deltaC;
6811 0 : } break;
6812 0 : case SkyTempModel::Brunt:
6813 : case SkyTempModel::Idso:
6814 : case SkyTempModel::BerdahlMartin:
6815 : case SkyTempModel::ClarkAllen: {
6816 0 : wpSkyTemp.IsSchedule = false;
6817 0 : } break;
6818 0 : default: {
6819 : // Bad inputs are trapped by input processor
6820 0 : assert(false);
6821 : } break;
6822 : } // switch (skyTempModel)
6823 :
6824 0 : if (wpSkyTemp.IsSchedule) {
6825 0 : if (state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather ||
6826 0 : state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
6827 : // See if it's a schedule.
6828 0 : if ((wpSkyTemp.sched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
6829 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6830 0 : ErrorsFound = true;
6831 : } else {
6832 0 : wpSkyTemp.IsSchedule = true;
6833 : }
6834 : } else { // See if it's a valid schedule.
6835 : // How can a schedule be either a yearly schedule or a day schedule?
6836 0 : if ((wpSkyTemp.sched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
6837 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6838 0 : ErrorsFound = true;
6839 : } else {
6840 0 : if (envFound != 0) {
6841 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6842 0 : state.dataWeather->spSiteSchedNums.end(),
6843 0 : wpSkyTemp.sched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6844 0 : state.dataWeather->spSiteSchedNums.emplace_back(wpSkyTemp.sched->Num);
6845 0 : SetupOutputVariable(state,
6846 : "Sizing Period Site Sky Temperature Schedule Value",
6847 : unitType,
6848 0 : state.dataWeather->spSiteSchedules(envFound).SkyTemp,
6849 : OutputProcessor::TimeStepType::Zone,
6850 : OutputProcessor::StoreType::Average,
6851 0 : ipsc->cAlphaArgs(3));
6852 : }
6853 0 : wpSkyTemp.IsSchedule = true;
6854 : }
6855 : }
6856 : }
6857 : }
6858 :
6859 0 : if (!wpSkyTemp.IsSchedule && !ipsc->lAlphaFieldBlanks(4)) {
6860 0 : if (BooleanSwitch b = getYesNoValue(ipsc->cAlphaArgs(4)); b != BooleanSwitch::Invalid) {
6861 0 : wpSkyTemp.UseWeatherFileHorizontalIR = static_cast<bool>(b);
6862 : } else {
6863 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
6864 0 : ErrorsFound = true;
6865 : }
6866 : } else {
6867 0 : wpSkyTemp.UseWeatherFileHorizontalIR = true;
6868 : }
6869 : }
6870 341 : for (auto &envCurr : state.dataWeather->Environment) {
6871 230 : if (envCurr.WP_Type1 != 0 && state.dataWeather->NumWPSkyTemperatures >= envCurr.WP_Type1) {
6872 0 : envCurr.skyTempModel = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel;
6873 0 : envCurr.UseWeatherFileHorizontalIR = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).UseWeatherFileHorizontalIR;
6874 : }
6875 : }
6876 111 : }
6877 :
6878 111 : void GetGroundTemps(EnergyPlusData &state)
6879 : {
6880 :
6881 : // SUBROUTINE INFORMATION:
6882 : // AUTHOR Richard Liesen
6883 : // DATE WRITTEN October 1997
6884 :
6885 : // PURPOSE OF THIS SUBROUTINE:
6886 : // This file reads the Ground Temps from the input file and puts them
6887 : // in a new variable.
6888 :
6889 : // Initialize Site:GroundTemperature:BuildingSurface object
6890 222 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr =
6891 222 : GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteBuildingSurface, "");
6892 :
6893 : // Initialize Site:GroundTemperature:FCFactorMethod object
6894 222 : state.dataWeather->siteFCFactorMethodGroundTempsPtr =
6895 222 : GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteFCFactorMethod, "");
6896 :
6897 : // Initialize Site:GroundTemperature:Shallow object
6898 222 : state.dataWeather->siteShallowGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteShallow, "");
6899 :
6900 : // Initialize Site:GroundTemperature:Deep object
6901 111 : state.dataWeather->siteDeepGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteDeep, "");
6902 111 : }
6903 :
6904 111 : void GetGroundReflectances(EnergyPlusData &state, bool &ErrorsFound)
6905 : {
6906 :
6907 : // SUBROUTINE INFORMATION:
6908 : // AUTHOR Linda Lawrie
6909 : // DATE WRITTEN March 2002
6910 :
6911 : // PURPOSE OF THIS SUBROUTINE:
6912 : // This file reads the Ground Reflectances from the input file (optional) and
6913 : // places them in the monthly array.
6914 :
6915 111 : auto const &ipsc = state.dataIPShortCut;
6916 111 : ipsc->cCurrentModuleObject = "Site:GroundReflectance";
6917 111 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6918 111 : if (nObjs != 0) {
6919 0 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6920 0 : Array1D<Real64> GndProps(12); // Temporary array to transfer ground reflectances
6921 0 : if (nObjs == 1) {
6922 : int GndNumAlpha; // Number of construction alpha names being passed
6923 : int GndNumProp; // dummy variable for properties being passed
6924 : int IOStat; // IO Status when calling get input subroutine
6925 : // Get the object names for each construction from the input processor
6926 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
6927 0 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6928 :
6929 0 : if (GndNumProp < 12) {
6930 0 : ShowSevereError(state, format("{}: Less than 12 values entered.", ipsc->cCurrentModuleObject));
6931 0 : ErrorsFound = true;
6932 : }
6933 :
6934 : // Assign the ground reflectances to the variable
6935 0 : state.dataWeather->GroundReflectances({1, 12}) = GndProps({1, 12});
6936 :
6937 : } else {
6938 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6939 0 : ErrorsFound = true;
6940 : }
6941 0 : }
6942 :
6943 : // Write Final Ground Reflectance Information to the initialization output file
6944 111 : print(state.files.eio,
6945 : "{}\n",
6946 : "! "
6947 : "<Site:GroundReflectance>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{dimensionless},"
6948 : "May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{dimensionless},Oct{"
6949 : "dimensionless},Nov{dimensionless},Dec{dimensionless}");
6950 :
6951 111 : print(state.files.eio, " Site:GroundReflectance");
6952 1443 : for (int i = 1; i <= 12; ++i) {
6953 1332 : print(state.files.eio, ", {:5.2F}", state.dataWeather->GroundReflectances(i));
6954 : }
6955 111 : print(state.files.eio, "\n");
6956 111 : }
6957 :
6958 111 : void GetSnowGroundRefModifiers(EnergyPlusData &state, bool &ErrorsFound)
6959 : {
6960 :
6961 : // SUBROUTINE INFORMATION:
6962 : // AUTHOR Linda Lawrie
6963 : // DATE WRITTEN March 2002
6964 :
6965 : // PURPOSE OF THIS SUBROUTINE:
6966 : // This file reads the Snow Ground Reflectance Modifiers from the input file (optional) and
6967 : // places them in the variables.
6968 :
6969 111 : auto const &ipsc = state.dataIPShortCut;
6970 111 : ipsc->cCurrentModuleObject = "Site:GroundReflectance:SnowModifier";
6971 111 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6972 111 : if (nObjs != 0) {
6973 0 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6974 0 : Array1D<Real64> GndProps(2); // Temporary array to transfer ground reflectances
6975 0 : if (nObjs == 1) {
6976 : int GndNumAlpha; // Number of construction alpha names being passed
6977 : int GndNumProp; // dummy variable for properties being passed
6978 : int IOStat; // IO Status when calling get input subroutine
6979 : // Get the object names for each construction from the input processor
6980 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
6981 0 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6982 :
6983 : // Assign the ground reflectances to the variable
6984 0 : state.dataWeather->SnowGndRefModifier = GndProps(1);
6985 0 : state.dataWeather->SnowGndRefModifierForDayltg = GndProps(2);
6986 :
6987 : } else {
6988 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6989 0 : ErrorsFound = true;
6990 : }
6991 0 : }
6992 :
6993 : // Write Final Ground Reflectance Modifier Information to the initialization output file
6994 111 : print(state.files.eio, "{}\n", "! <Site:GroundReflectance:SnowModifier>, Normal, Daylighting {dimensionless}");
6995 : static constexpr std::string_view Format_720(" Site:GroundReflectance:SnowModifier, {:7.3F}, {:7.3F}\n");
6996 111 : print(state.files.eio, Format_720, state.dataWeather->SnowGndRefModifier, state.dataWeather->SnowGndRefModifierForDayltg);
6997 :
6998 111 : print(state.files.eio,
6999 : "{}\n",
7000 : "! "
7001 : "<Site:GroundReflectance:Snow>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
7002 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
7003 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
7004 111 : print(state.files.eio, "{}", " Site:GroundReflectance:Snow");
7005 1443 : for (int i = 1; i <= 12; ++i) {
7006 1332 : print(state.files.eio, ", {:5.2F}", max(min(state.dataWeather->GroundReflectances(i) * state.dataWeather->SnowGndRefModifier, 1.0), 0.0));
7007 : }
7008 111 : print(state.files.eio, "\n");
7009 111 : print(state.files.eio,
7010 : "{}\n",
7011 : "! "
7012 : "<Site:GroundReflectance:Snow:Daylighting>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
7013 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
7014 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
7015 111 : print(state.files.eio, " Site:GroundReflectance:Snow:Daylighting");
7016 1443 : for (nObjs = 1; nObjs <= 12; ++nObjs) {
7017 1332 : print(state.files.eio,
7018 : ", {:5.2F}",
7019 2664 : max(min(state.dataWeather->GroundReflectances(nObjs) * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0));
7020 : }
7021 111 : print(state.files.eio, "\n");
7022 111 : }
7023 :
7024 117 : void GetWaterMainsTemperatures(EnergyPlusData &state, bool &ErrorsFound)
7025 : {
7026 :
7027 : // SUBROUTINE INFORMATION:
7028 : // AUTHOR Peter Graham Ellis
7029 : // DATE WRITTEN January 2005
7030 :
7031 : // PURPOSE OF THIS SUBROUTINE:
7032 : // Reads the input data for the WATER MAINS TEMPERATURES object.
7033 :
7034 117 : constexpr std::string_view routineName = "GetWaterMainsTemperatures";
7035 :
7036 117 : auto const &ipsc = state.dataIPShortCut;
7037 117 : ipsc->cCurrentModuleObject = "Site:WaterMainsTemperature";
7038 117 : int NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
7039 :
7040 117 : if (NumObjects == 1) {
7041 : int NumAlphas; // Number of elements in the alpha array
7042 : int NumNums; // Number of elements in the numeric array
7043 : int IOStat; // IO Status when calling get input subroutine
7044 12 : Array1D_string AlphArray(2); // Character string data
7045 12 : Array1D<Real64> NumArray(4); // Numeric data
7046 36 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
7047 12 : ipsc->cCurrentModuleObject,
7048 : 1,
7049 : AlphArray,
7050 : NumAlphas,
7051 : NumArray,
7052 : NumNums,
7053 : IOStat,
7054 12 : ipsc->lNumericFieldBlanks,
7055 12 : ipsc->lAlphaFieldBlanks,
7056 12 : ipsc->cAlphaFieldNames,
7057 12 : ipsc->cNumericFieldNames);
7058 :
7059 12 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
7060 :
7061 24 : state.dataWeather->WaterMainsTempsMethod =
7062 12 : static_cast<Weather::WaterMainsTempCalcMethod>(getEnumValue(waterMainsCalcMethodNamesUC, AlphArray(1)));
7063 :
7064 12 : switch (state.dataWeather->WaterMainsTempsMethod) {
7065 0 : case WaterMainsTempCalcMethod::Schedule: {
7066 0 : if (ipsc->lAlphaFieldBlanks(2)) {
7067 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
7068 0 : ErrorsFound = true;
7069 0 : } else if ((state.dataWeather->waterMainsTempSched = Sched::GetSchedule(state, AlphArray(2))) == nullptr) {
7070 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
7071 0 : ErrorsFound = true;
7072 : }
7073 0 : } break;
7074 :
7075 6 : case WaterMainsTempCalcMethod::Correlation: {
7076 6 : if (NumNums == 0) {
7077 0 : ShowSevereError(state, format("{}: Missing Annual Average and Maximum Difference fields.", ipsc->cCurrentModuleObject));
7078 0 : ErrorsFound = true;
7079 6 : } else if (NumNums == 1) {
7080 0 : ShowSevereError(state, format("{}: Missing Maximum Difference field.", ipsc->cCurrentModuleObject));
7081 0 : ErrorsFound = true;
7082 : } else {
7083 6 : state.dataWeather->WaterMainsTempsAnnualAvgAirTemp = NumArray(1);
7084 6 : state.dataWeather->WaterMainsTempsMaxDiffAirTemp = NumArray(2);
7085 : }
7086 6 : } break;
7087 6 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile: {
7088 : // No action
7089 6 : } break;
7090 0 : default: {
7091 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), AlphArray(1));
7092 0 : ErrorsFound = true;
7093 0 : } break;
7094 : } // switch
7095 :
7096 12 : state.dataWeather->WaterMainsTempsMultiplier = NumArray(3);
7097 12 : state.dataWeather->WaterMainsTempsOffset = NumArray(4);
7098 :
7099 117 : } else if (NumObjects > 1) {
7100 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
7101 0 : ErrorsFound = true;
7102 : }
7103 117 : }
7104 :
7105 326130 : void CalcWaterMainsTemp(EnergyPlusData &state)
7106 : {
7107 :
7108 : // SUBROUTINE INFORMATION:
7109 : // AUTHOR Peter Graham Ellis
7110 : // DATE WRITTEN January 2005
7111 : // MODIFIED June 2018, B. Nigusse
7112 :
7113 : // PURPOSE OF THIS SUBROUTINE:
7114 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
7115 :
7116 : // METHODOLOGY EMPLOYED:
7117 : // Water mains temperature is either taken from a schedule or calculated by a correlation. The correlation
7118 : // is fit to Fahrenheit units, so the air temperature values are first convert to F, then mains temperature
7119 : // is calculated and converted back to C.
7120 :
7121 326130 : switch (state.dataWeather->WaterMainsTempsMethod) {
7122 0 : case WaterMainsTempCalcMethod::Schedule:
7123 0 : state.dataEnvrn->WaterMainsTemp = state.dataWeather->waterMainsTempSched->getCurrentVal();
7124 0 : break;
7125 19948 : case WaterMainsTempCalcMethod::Correlation:
7126 19948 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
7127 19948 : state.dataWeather->WaterMainsTempsAnnualAvgAirTemp,
7128 19948 : state.dataWeather->WaterMainsTempsMaxDiffAirTemp,
7129 19948 : state.dataWeather->WaterMainsTempsMultiplier,
7130 19948 : state.dataWeather->WaterMainsTempsOffset);
7131 19948 : break;
7132 6 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
7133 6 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
7134 6 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
7135 6 : state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp,
7136 6 : state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff,
7137 6 : state.dataWeather->WaterMainsTempsMultiplier,
7138 6 : state.dataWeather->WaterMainsTempsOffset);
7139 : } else {
7140 0 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
7141 : }
7142 6 : break;
7143 306176 : default:
7144 306176 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
7145 306176 : break;
7146 : }
7147 326130 : }
7148 :
7149 19954 : Real64 WaterMainsTempFromCorrelation(EnergyPlusData const &state,
7150 : Real64 const AnnualOAAvgDryBulbTemp,
7151 : Real64 const MonthlyOAAvgDryBulbTempMaxDiff,
7152 : Real64 const TemperatureMultiplier,
7153 : Real64 const TemperatureOffset)
7154 : {
7155 :
7156 : // SUBROUTINE INFORMATION:
7157 : // AUTHOR Peter Graham Ellis
7158 : // DATE WRITTEN January 2005
7159 : // MODIFIED B Nigusse June 2018 (Refactored)
7160 :
7161 : // PURPOSE OF THIS SUBROUTINE:
7162 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
7163 :
7164 : // METHODOLOGY EMPLOYED:
7165 : // Water mains temperature calculated by a correlation. The correlation is fit to Fahrenheit units, so the
7166 : // air temperature values are first convert to F, then mains temperature is calculated and converted back to C.
7167 : // used for Calculated Method: 'Correlation' and 'CorrelationFromWeatherFile'.
7168 :
7169 : // REFERENCES:
7170 : // Correlation developed by Jay Burch and Craig Christensen at NREL, described in:
7171 : // Hendron, R., Anderson, R., Christensen, C., Eastment, M., and Reeves, P. 2004. "Development of an Energy
7172 : // Savings Benchmark for All Residential End-Uses", Proceedings of SimBuild 2004, IBPSA-USA National Conference,
7173 : // Boulder, CO, August 4 - 6, 2004.
7174 :
7175 : // Annual Average Outdoor Air Temperature (F)
7176 19954 : Real64 const Tavg = AnnualOAAvgDryBulbTemp * (9.0 / 5.0) + 32.0;
7177 : // Maximum difference in monthly average outdoor air temperatures (deltaF)
7178 19954 : Real64 const Tdiff = MonthlyOAAvgDryBulbTempMaxDiff * (9.0 / 5.0);
7179 :
7180 19954 : Real64 const Ratio = 0.4 + 0.01 * (Tavg - 44.0);
7181 19954 : Real64 const Lag = 35.0 - 1.0 * (Tavg - 44.0);
7182 19954 : Real64 constexpr Offset = 6.0;
7183 19954 : int const latitude_sign = (state.dataEnvrn->Latitude >= 0) ? 1 : -1;
7184 :
7185 : // calculated water main temp (F)
7186 : Real64 CurrentWaterMainsTemp =
7187 19954 : Tavg + Offset +
7188 19954 : Ratio * (Tdiff / 2.0) * latitude_sign * std::sin((0.986 * (state.dataEnvrn->DayOfYear - 15.0 - Lag) - 90) * Constant::DegToRad);
7189 :
7190 19954 : if (CurrentWaterMainsTemp < 32.0) {
7191 2166 : CurrentWaterMainsTemp = 32.0;
7192 : }
7193 :
7194 : // Convert F to C
7195 19954 : CurrentWaterMainsTemp = (CurrentWaterMainsTemp - 32.0) * (5.0 / 9.0);
7196 :
7197 : // apply temperature multiplier and offset
7198 19954 : CurrentWaterMainsTemp = CurrentWaterMainsTemp * TemperatureMultiplier + TemperatureOffset;
7199 :
7200 19954 : return CurrentWaterMainsTemp;
7201 : }
7202 111 : void GetWeatherStation(EnergyPlusData &state, bool &ErrorsFound)
7203 : {
7204 :
7205 : // SUBROUTINE INFORMATION:
7206 : // AUTHOR Peter Graham Ellis
7207 : // DATE WRITTEN January 2006
7208 :
7209 : // PURPOSE OF THIS SUBROUTINE:
7210 : // Reads the input data for the WEATHER STATION object.
7211 :
7212 111 : auto const &ipsc = state.dataIPShortCut;
7213 111 : ipsc->cCurrentModuleObject = "Site:WeatherStation";
7214 111 : int const NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
7215 :
7216 : // Default conditions for a weather station in an open field at a height of 10 m. (These should match the IDD defaults.)
7217 111 : Real64 WeatherFileWindSensorHeight = 10.0; // Height of the wind sensor at the weather station, i.e., weather file
7218 111 : Real64 WeatherFileWindExp = 0.14; // Exponent for the wind velocity profile at the weather station
7219 111 : Real64 WeatherFileWindBLHeight = 270.0; // Boundary layer height for the wind velocity profile at the weather station (m)
7220 111 : Real64 WeatherFileTempSensorHeight = 1.5; // Height of the air temperature sensor at the weather station (m)
7221 :
7222 111 : if (NumObjects == 1) {
7223 : int NumAlphas; // Number of elements in the alpha array
7224 : int NumNums; // Number of elements in the numeric array
7225 : int IOStat; // IO Status when calling get input subroutine
7226 0 : Array1D_string AlphArray(1); // Character string data
7227 0 : Array1D<Real64> NumArray(4); // Numeric data
7228 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
7229 0 : state, ipsc->cCurrentModuleObject, 1, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
7230 :
7231 0 : if (NumNums > 0) {
7232 0 : WeatherFileWindSensorHeight = NumArray(1);
7233 : }
7234 0 : if (NumNums > 1) {
7235 0 : WeatherFileWindExp = NumArray(2);
7236 : }
7237 0 : if (NumNums > 2) {
7238 0 : WeatherFileWindBLHeight = NumArray(3);
7239 : }
7240 0 : if (NumNums > 3) {
7241 0 : WeatherFileTempSensorHeight = NumArray(4);
7242 : }
7243 :
7244 111 : } else if (NumObjects > 1) {
7245 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
7246 0 : ErrorsFound = true;
7247 : }
7248 :
7249 111 : state.dataEnvrn->WeatherFileWindModCoeff = std::pow(WeatherFileWindBLHeight / WeatherFileWindSensorHeight, WeatherFileWindExp);
7250 222 : state.dataEnvrn->WeatherFileTempModCoeff = DataEnvironment::AtmosphericTempGradient * DataEnvironment::EarthRadius *
7251 111 : WeatherFileTempSensorHeight / (DataEnvironment::EarthRadius + WeatherFileTempSensorHeight);
7252 :
7253 : // Write to the initialization output file
7254 111 : print(state.files.eio,
7255 : "{}\n",
7256 : "! <Environment:Weather Station>,Wind Sensor Height Above Ground {m},Wind Speed Profile Exponent "
7257 : "{},Wind Speed Profile Boundary Layer Thickness {m},Air Temperature Sensor Height Above Ground {m},Wind "
7258 : "Speed Modifier Coefficient-Internal,Temperature Modifier Coefficient-Internal");
7259 :
7260 : // Formats
7261 : static constexpr std::string_view Format_720("Environment:Weather Station,{:.3R},{:.3R},{:.3R},{:.3R},{:.3R},{:.3R}\n");
7262 111 : print(state.files.eio,
7263 : Format_720,
7264 : WeatherFileWindSensorHeight,
7265 : WeatherFileWindExp,
7266 : WeatherFileWindBLHeight,
7267 : WeatherFileTempSensorHeight,
7268 111 : state.dataEnvrn->WeatherFileWindModCoeff,
7269 111 : state.dataEnvrn->WeatherFileTempModCoeff);
7270 111 : }
7271 :
7272 326120 : void DayltgCurrentExtHorizIllum(EnergyPlusData &state)
7273 : {
7274 :
7275 : // SUBROUTINE INFORMATION:
7276 : // AUTHOR Fred Winkelmann
7277 : // DATE WRITTEN July 1997
7278 : // MODIFIED Nov98 (FW); Nov 2000 (FW)
7279 :
7280 : // PURPOSE OF THIS SUBROUTINE:
7281 : // CALCULATES EXTERIOR DAYLIGHT ILLUMINANCE AND LUMINOUS EFFICACY
7282 :
7283 : // METHODOLOGY EMPLOYED:
7284 : // CALLED by SetCurrentWeather.
7285 : // CALCULATES THE CURRENT-TIME-STEP
7286 : // ILLUMINANCE ON AN UNOBSTRUCTED HORIZONTAL SURFACE FROM THE
7287 : // THE SKY AND FROM DIRECT SUN.
7288 :
7289 : // REFERENCES:
7290 : // Based on DOE-2.1E subroutine DEXTIL.
7291 :
7292 : // SOLCOS(3), below, is the cosine of the solar zenith angle.
7293 326120 : if (state.dataEnvrn->SunIsUp) {
7294 : // Exterior horizontal beam irradiance (W/m2)
7295 159691 : Real64 SDIRH = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z;
7296 : // Exterior horizontal sky diffuse irradiance (W/m2)
7297 159691 : Real64 SDIFH = state.dataEnvrn->DifSolarRad;
7298 : // Fraction of sky covered by clouds
7299 159691 : state.dataEnvrn->CloudFraction = pow_2(SDIFH / (SDIRH + SDIFH + 0.0001));
7300 : // Luminous efficacy of sky diffuse solar and beam solar (lumens/W);
7301 : // Horizontal illuminance from sky and horizontal beam illuminance (lux)
7302 : // obtained from solar quantities on weather file and luminous efficacy.
7303 :
7304 159691 : DayltgLuminousEfficacy(state, state.dataEnvrn->PDIFLW, state.dataEnvrn->PDIRLW);
7305 159691 : state.dataEnvrn->HISKF = SDIFH * state.dataEnvrn->PDIFLW;
7306 159691 : state.dataEnvrn->HISUNF = SDIRH * state.dataEnvrn->PDIRLW;
7307 159691 : state.dataEnvrn->HISUNFnorm = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW;
7308 : } else {
7309 166429 : state.dataEnvrn->CloudFraction = 0.0;
7310 166429 : state.dataEnvrn->PDIFLW = 0.0;
7311 166429 : state.dataEnvrn->PDIRLW = 0.0;
7312 166429 : state.dataEnvrn->HISKF = 0.0;
7313 166429 : state.dataEnvrn->HISUNF = 0.0;
7314 166429 : state.dataEnvrn->HISUNFnorm = 0.0;
7315 166429 : state.dataEnvrn->SkyClearness = 0.0;
7316 166429 : state.dataEnvrn->SkyBrightness = 0.0;
7317 : }
7318 326120 : }
7319 :
7320 159691 : void DayltgLuminousEfficacy(EnergyPlusData &state,
7321 : Real64 &DiffLumEff, // Luminous efficacy of sky diffuse solar radiation (lum/W)
7322 : Real64 &DirLumEff // Luminous efficacy of beam solar radiation (lum/W)
7323 : )
7324 : {
7325 : // SUBROUTINE INFORMATION:
7326 : // AUTHOR Fred Winkelmann
7327 : // DATE WRITTEN July 1997
7328 : // MODIFIED August 2009, BG fixed upper bound for sky clearness bin 7
7329 :
7330 : // PURPOSE OF THIS SUBROUTINE:
7331 : // Uses diffuse horizontal solar irradiance, direct normal solar
7332 : // irradiance, atmospheric moisture and sun position
7333 : // to determine the luminous efficacy in lumens/watt
7334 : // of sky diffuse solar radiation and direct normal solar radiation.
7335 : // Based on an empirical method described in
7336 : // R. Perez, P. Ineichen, R. Seals, J. Michalsky and R. Stewart,
7337 : // "Modeling daylight availability and irradiance components from direct
7338 : // global irradiance components from direct and global irradiance,"
7339 : // Solar Energy 44 (1990) 271-289.
7340 :
7341 : // Diffuse luminous efficacy coefficients
7342 : static constexpr std::array<Real64, 8> ADiffLumEff = {97.24, 107.22, 104.97, 102.39, 100.71, 106.42, 141.88, 152.23};
7343 : static constexpr std::array<Real64, 8> BDiffLumEff = {-0.46, 1.15, 2.96, 5.59, 5.94, 3.83, 1.90, 0.35};
7344 : static constexpr std::array<Real64, 8> CDiffLumEff = {12.00, 0.59, -5.53, -13.95, -22.75, -36.15, -53.24, -45.27};
7345 : static constexpr std::array<Real64, 8> DDiffLumEff = {-8.91, -3.95, -8.77, -13.90, -23.74, -28.83, -14.03, -7.98};
7346 : // Direct luminous efficacy coefficients
7347 : static constexpr std::array<Real64, 8> ADirLumEff = {57.20, 98.99, 109.83, 110.34, 106.36, 107.19, 105.75, 101.18};
7348 : static constexpr std::array<Real64, 8> BDirLumEff = {-4.55, -3.46, -4.90, -5.84, -3.97, -1.25, 0.77, 1.58};
7349 : static constexpr std::array<Real64, 8> CDirLumEff = {-2.98, -1.21, -1.71, -1.99, -1.75, -1.51, -1.26, -1.10};
7350 : static constexpr std::array<Real64, 8> DDirLumEff = {117.12, 12.38, -8.81, -4.56, -6.16, -26.73, -34.44, -8.29};
7351 : // Monthly exterrestrial direct normal illuminance (lum/m2)
7352 : static constexpr std::array<Real64, 12> ExtraDirNormIll = {
7353 : 131153.0, 130613.0, 128992.0, 126816.0, 124731.0, 123240.0, 122652.0, 123120.0, 124576.0, 126658.0, 128814.0, 130471.0};
7354 :
7355 159691 : Real64 const SunZenith = std::acos(state.dataEnvrn->SOLCOS.z); // Solar zenith angle (radians)
7356 159691 : Real64 const SunAltitude = Constant::PiOvr2 - SunZenith; // Solar altitude angle (radians)
7357 159691 : Real64 const SinSunAltitude = std::sin(SunAltitude);
7358 : // Clearness of sky. SkyClearness close to 1.0 corresponds to an overcast sky.
7359 : // SkyClearness > 6 is a clear sky.
7360 : // DifSolarRad is the diffuse horizontal irradiance.
7361 : // BeamSolarRad is the direct normal irradiance.
7362 159691 : Real64 const Zeta = 1.041 * pow_3(SunZenith);
7363 319382 : state.dataEnvrn->SkyClearness =
7364 159691 : ((state.dataEnvrn->DifSolarRad + state.dataEnvrn->BeamSolarRad) / (state.dataEnvrn->DifSolarRad + 0.0001) + Zeta) / (1.0 + Zeta);
7365 : // Relative optical air mass
7366 : Real64 const relAirMass =
7367 159691 : (1.0 - 0.1 * state.dataEnvrn->Elevation / 1000.0) / (SinSunAltitude + 0.15 / std::pow(SunAltitude / Constant::DegToRad + 3.885, 1.253));
7368 : // In the following, 93.73 is the extraterrestrial luminous efficacy
7369 159691 : state.dataEnvrn->SkyBrightness = (state.dataEnvrn->DifSolarRad * 93.73) * relAirMass / ExtraDirNormIll[state.dataEnvrn->Month - 1];
7370 : int ISkyClearness; // Sky clearness bin
7371 159691 : if (state.dataEnvrn->SkyClearness <= 1.065) {
7372 62364 : ISkyClearness = 0;
7373 97327 : } else if (state.dataEnvrn->SkyClearness <= 1.23) {
7374 2739 : ISkyClearness = 1;
7375 94588 : } else if (state.dataEnvrn->SkyClearness <= 1.50) {
7376 3360 : ISkyClearness = 2;
7377 91228 : } else if (state.dataEnvrn->SkyClearness <= 1.95) {
7378 4314 : ISkyClearness = 3;
7379 86914 : } else if (state.dataEnvrn->SkyClearness <= 2.80) {
7380 12286 : ISkyClearness = 4;
7381 74628 : } else if (state.dataEnvrn->SkyClearness <= 4.50) {
7382 28260 : ISkyClearness = 5;
7383 46368 : } else if (state.dataEnvrn->SkyClearness <= 6.20) {
7384 17161 : ISkyClearness = 6;
7385 : } else {
7386 29207 : ISkyClearness = 7;
7387 : }
7388 :
7389 : // Atmospheric moisture (cm of precipitable water)
7390 159691 : Real64 const AtmosMoisture = std::exp(0.07 * state.dataEnvrn->OutDewPointTemp - 0.075);
7391 : // Sky diffuse luminous efficacy
7392 159691 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7393 51559 : DiffLumEff = 0.0;
7394 : } else {
7395 108132 : DiffLumEff = ADiffLumEff[ISkyClearness] + BDiffLumEff[ISkyClearness] * AtmosMoisture +
7396 108132 : CDiffLumEff[ISkyClearness] * state.dataEnvrn->SOLCOS.z +
7397 108132 : DDiffLumEff[ISkyClearness] * std::log(state.dataEnvrn->SkyBrightness);
7398 : }
7399 : // Direct normal luminous efficacy
7400 159691 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7401 51559 : DirLumEff = 0.0;
7402 : } else {
7403 108132 : DirLumEff =
7404 108132 : max(0.0,
7405 108132 : ADirLumEff[ISkyClearness] + BDirLumEff[ISkyClearness] * AtmosMoisture +
7406 108132 : CDirLumEff[ISkyClearness] * std::exp(5.73 * SunZenith - 5.0) + DDirLumEff[ISkyClearness] * state.dataEnvrn->SkyBrightness);
7407 : }
7408 159691 : }
7409 :
7410 113 : Real64 GetSTM(Real64 const Longitude) // Longitude from user input
7411 : {
7412 : // FUNCTION INFORMATION:
7413 : // AUTHOR Linda K. Lawrie
7414 : // DATE WRITTEN August 2003
7415 :
7416 : // PURPOSE OF THIS FUNCTION:
7417 : // This function determines the "standard time meridian" from the input
7418 : // longitude. Calculates the proper Meridian from Longitude. This
7419 : // value is needed for weather calculations so that the sun comes
7420 : // up and goes down at the right times.
7421 :
7422 : Real64 GetSTM;
7423 :
7424 113 : Array1D<Real64> longl({-12, 12}); // Lower Longitude value for a Time Zone
7425 113 : Array1D<Real64> longh({-12, 12}); // Upper Longitude value for a Time Zone
7426 :
7427 113 : GetSTM = 0.0;
7428 :
7429 113 : longl(0) = -7.5;
7430 113 : longh(0) = 7.5;
7431 1469 : for (int i = 1; i <= 12; ++i) {
7432 1356 : longl(i) = longl(i - 1) + 15.0;
7433 1356 : longh(i) = longh(i - 1) + 15.0;
7434 : }
7435 1469 : for (int i = 1; i <= 12; ++i) {
7436 1356 : longl(-i) = longl(-i + 1) - 15.0;
7437 1356 : longh(-i) = longh(-i + 1) - 15.0;
7438 : }
7439 113 : Real64 temp = mod(Longitude, 360.0);
7440 113 : if (temp > 180.0) {
7441 0 : temp -= 180.0;
7442 : }
7443 : Real64 tz; // resultant tz meridian
7444 808 : for (int i = -12; i <= 12; ++i) {
7445 808 : if (temp > longl(i) && temp <= longh(i)) {
7446 113 : tz = mod(i, 24.0);
7447 113 : GetSTM = tz;
7448 113 : break;
7449 : }
7450 : }
7451 :
7452 113 : return GetSTM;
7453 113 : }
7454 :
7455 241 : void ProcessEPWHeader(EnergyPlusData &state, EpwHeaderType const headerType, std::string &Line, bool &ErrorsFound)
7456 : {
7457 :
7458 : // SUBROUTINE INFORMATION:
7459 : // AUTHOR Linda Lawrie
7460 : // DATE WRITTEN December 1999
7461 :
7462 : // PURPOSE OF THIS SUBROUTINE:
7463 : // This subroutine processes each header line in the EPW weather file.
7464 :
7465 : // METHODOLOGY EMPLOYED:
7466 : // File is positioned to the correct line, then backspaced. This routine
7467 : // reads in the line and processes as appropriate.
7468 :
7469 : Weather::DateType dateType;
7470 : int NumHdArgs;
7471 :
7472 : // Strip off Header value from Line
7473 241 : std::string::size_type Pos = index(Line, ',');
7474 241 : if ((Pos == std::string::npos) && !((headerType == EpwHeaderType::Comments1) || (headerType == EpwHeaderType::Comments2))) {
7475 0 : ShowSevereError(state, "Invalid Header line in in.epw -- no commas");
7476 0 : ShowContinueError(state, format("Line={}", Line));
7477 0 : ShowFatalError(state, "Previous conditions cause termination.");
7478 : }
7479 241 : if (Pos != std::string::npos) {
7480 241 : Line.erase(0, Pos + 1);
7481 : }
7482 :
7483 241 : switch (headerType) {
7484 30 : case Weather::EpwHeaderType::Location: {
7485 :
7486 : // LOCATION, A1 [City], A2 [State/Province/Region], A3 [Country],
7487 : // A4 [Source], N1 [WMO], N2 [Latitude],
7488 : // N3 [Longitude], N4 [Time Zone], N5 [Elevation {m}]
7489 :
7490 30 : NumHdArgs = 9;
7491 300 : for (int i = 1; i <= NumHdArgs; ++i) {
7492 270 : strip(Line);
7493 270 : Pos = index(Line, ',');
7494 270 : if (Pos == std::string::npos) {
7495 29 : if (len(Line) == 0) {
7496 0 : while (Pos == std::string::npos) {
7497 0 : Line = state.files.inputWeatherFile.readLine().data;
7498 0 : strip(Line);
7499 0 : uppercase(Line);
7500 0 : Pos = index(Line, ',');
7501 : }
7502 : } else {
7503 29 : Pos = len(Line);
7504 : }
7505 : }
7506 :
7507 270 : switch (i) {
7508 30 : case 1:
7509 30 : state.dataWeather->EPWHeaderTitle = stripped(Line.substr(0, Pos));
7510 30 : break;
7511 90 : case 2:
7512 : case 3:
7513 : case 4:
7514 90 : state.dataWeather->EPWHeaderTitle = strip(state.dataWeather->EPWHeaderTitle) + ' ' + stripped(Line.substr(0, Pos));
7515 90 : break;
7516 30 : case 5:
7517 30 : state.dataWeather->EPWHeaderTitle += " WMO#=" + stripped(Line.substr(0, Pos));
7518 30 : break;
7519 120 : case 6:
7520 : case 7:
7521 : case 8:
7522 : case 9: {
7523 : bool errFlag;
7524 120 : Real64 const Number = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7525 120 : if (!errFlag) {
7526 120 : switch (i) {
7527 30 : case 6:
7528 30 : state.dataWeather->WeatherFileLatitude = Number;
7529 30 : break;
7530 30 : case 7:
7531 30 : state.dataWeather->WeatherFileLongitude = Number;
7532 30 : break;
7533 30 : case 8:
7534 30 : state.dataWeather->WeatherFileTimeZone = Number;
7535 30 : break;
7536 30 : case 9:
7537 30 : state.dataWeather->WeatherFileElevation = Number;
7538 30 : break;
7539 0 : default:
7540 0 : break;
7541 : }
7542 : }
7543 120 : } break;
7544 0 : default:
7545 0 : ShowSevereError(state, format("GetEPWHeader:LOCATION, invalid numeric={}", Line.substr(0, Pos)));
7546 0 : ErrorsFound = true;
7547 0 : break;
7548 : }
7549 270 : Line.erase(0, Pos + 1);
7550 : }
7551 30 : state.dataEnvrn->WeatherFileLocationTitle = stripped(state.dataWeather->EPWHeaderTitle);
7552 30 : } break;
7553 30 : case Weather::EpwHeaderType::TypicalExtremePeriods: {
7554 30 : strip(Line);
7555 30 : Pos = index(Line, ',');
7556 30 : if (Pos == std::string::npos) {
7557 0 : if (len(Line) == 0) {
7558 0 : while (Pos == std::string::npos && len(Line) == 0) {
7559 0 : Line = state.files.inputWeatherFile.readLine().data;
7560 0 : strip(Line);
7561 0 : Pos = index(Line, ',');
7562 : }
7563 : } else {
7564 0 : Pos = len(Line);
7565 : }
7566 : }
7567 : bool IOStatus;
7568 30 : state.dataWeather->NumEPWTypExtSets = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7569 30 : Line.erase(0, Pos + 1);
7570 30 : state.dataWeather->TypicalExtremePeriods.allocate(state.dataWeather->NumEPWTypExtSets);
7571 30 : int TropExtremeCount = 0;
7572 207 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7573 177 : strip(Line);
7574 177 : Pos = index(Line, ',');
7575 177 : if (Pos != std::string::npos) {
7576 177 : state.dataWeather->TypicalExtremePeriods(i).Title = Line.substr(0, Pos);
7577 177 : Line.erase(0, Pos + 1);
7578 : } else {
7579 0 : ShowWarningError(state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={}", Line.substr(0, Pos)));
7580 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7581 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7582 0 : break;
7583 : }
7584 177 : Pos = index(Line, ',');
7585 177 : if (Pos != std::string::npos) {
7586 177 : state.dataWeather->TypicalExtremePeriods(i).TEType = Line.substr(0, Pos);
7587 177 : Line.erase(0, Pos + 1);
7588 177 : if (Util::SameString(state.dataWeather->TypicalExtremePeriods(i).TEType, "EXTREME")) {
7589 60 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MAX")) {
7590 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7591 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MIN")) {
7592 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7593 58 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MAX")) {
7594 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMax";
7595 58 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MIN")) {
7596 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMin";
7597 : // to account for problems earlier in weather files:
7598 58 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7599 0 : if (TropExtremeCount == 0) {
7600 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Max";
7601 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7602 0 : ++TropExtremeCount;
7603 0 : } else if (TropExtremeCount == 1) {
7604 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Min";
7605 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7606 0 : ++TropExtremeCount;
7607 : }
7608 : } else { // make new short titles
7609 58 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7610 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7611 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7612 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7613 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7614 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7615 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7616 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7617 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7618 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7619 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7620 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7621 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7622 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7623 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7624 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7625 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7626 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7627 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7628 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7629 : }
7630 : }
7631 : } else { // not extreme
7632 117 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7633 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7634 88 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7635 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7636 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7637 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7638 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7639 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7640 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7641 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7642 30 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7643 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7644 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7645 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7646 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7647 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7648 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7649 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7650 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7651 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7652 : }
7653 : }
7654 : } else {
7655 0 : ShowWarningError(state,
7656 0 : format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={} {}",
7657 0 : state.dataWeather->TypicalExtremePeriods(i).Title,
7658 0 : Line.substr(0, Pos)));
7659 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7660 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7661 0 : break;
7662 : }
7663 : int PMonth;
7664 : int PDay;
7665 : int PWeekDay;
7666 177 : Pos = index(Line, ',');
7667 177 : if (Pos != std::string::npos) {
7668 177 : std::string dateStringUC = Line.substr(0, Pos);
7669 177 : dateStringUC = uppercase(dateStringUC);
7670 177 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7671 177 : if (dateType != DateType::Invalid) {
7672 177 : if (PMonth != 0 && PDay != 0) {
7673 177 : state.dataWeather->TypicalExtremePeriods(i).StartMonth = PMonth;
7674 177 : state.dataWeather->TypicalExtremePeriods(i).StartDay = PDay;
7675 : }
7676 : } else {
7677 0 : ShowSevereError(
7678 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7679 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7680 0 : ErrorsFound = true;
7681 : }
7682 177 : Line.erase(0, Pos + 1);
7683 177 : }
7684 177 : Pos = index(Line, ',');
7685 177 : if (Pos != std::string::npos) {
7686 148 : std::string dateStringUC = Line.substr(0, Pos);
7687 148 : dateStringUC = uppercase(dateStringUC);
7688 148 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7689 148 : if (dateType != DateType::Invalid) {
7690 148 : if (PMonth != 0 && PDay != 0) {
7691 148 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7692 148 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7693 : }
7694 : } else {
7695 0 : ShowSevereError(
7696 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7697 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7698 0 : ErrorsFound = true;
7699 : }
7700 148 : Line.erase(0, Pos + 1);
7701 148 : } else { // Pos=0, probably last one
7702 29 : std::string const dateStringUC = uppercase(Line);
7703 29 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7704 29 : if (dateType != DateType::Invalid) {
7705 29 : if (PMonth != 0 && PDay != 0) {
7706 29 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7707 29 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7708 : }
7709 : } else {
7710 0 : ShowSevereError(
7711 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7712 0 : ErrorsFound = true;
7713 : }
7714 29 : }
7715 : }
7716 : // Process periods to set up other values.
7717 207 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7718 177 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7719 : // JulianDay (Month,Day,LeapYearValue)
7720 177 : std::string const ExtremePeriodTitle = Util::makeUPPER(typicalExtPer.ShortTitle);
7721 177 : if (ExtremePeriodTitle == "SUMMER") {
7722 58 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7723 29 : typicalExtPer.MatchValue = "SummerExtreme";
7724 29 : typicalExtPer.MatchValue1 = "TropicalHot";
7725 29 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7726 : } else {
7727 29 : typicalExtPer.MatchValue = "SummerTypical";
7728 : }
7729 :
7730 119 : } else if (ExtremePeriodTitle == "WINTER") {
7731 58 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7732 29 : typicalExtPer.MatchValue = "WinterExtreme";
7733 29 : typicalExtPer.MatchValue1 = "TropicalCold";
7734 29 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7735 : } else {
7736 29 : typicalExtPer.MatchValue = "WinterTypical";
7737 : }
7738 :
7739 61 : } else if (ExtremePeriodTitle == "AUTUMN") {
7740 29 : typicalExtPer.MatchValue = "AutumnTypical";
7741 :
7742 32 : } else if (ExtremePeriodTitle == "SPRING") {
7743 29 : typicalExtPer.MatchValue = "SpringTypical";
7744 :
7745 3 : } else if (ExtremePeriodTitle == "WETSEASON") {
7746 0 : typicalExtPer.MatchValue = "WetSeason";
7747 :
7748 3 : } else if (ExtremePeriodTitle == "DRYSEASON") {
7749 0 : typicalExtPer.MatchValue = "DrySeason";
7750 :
7751 3 : } else if (ExtremePeriodTitle == "NOWETSEASON") {
7752 0 : typicalExtPer.MatchValue = "NoWetSeason";
7753 :
7754 3 : } else if (ExtremePeriodTitle == "NODRYSEASON") {
7755 1 : typicalExtPer.MatchValue = "NoDrySeason";
7756 :
7757 2 : } else if ((ExtremePeriodTitle == "NODRYSEASONMAX") || (ExtremePeriodTitle == "NOWETSEASONMAX")) {
7758 1 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7759 1 : typicalExtPer.MatchValue1 = "TropicalHot";
7760 1 : typicalExtPer.MatchValue2 = "SummerExtreme";
7761 :
7762 1 : } else if ((ExtremePeriodTitle == "NODRYSEASONMIN") || (ExtremePeriodTitle == "NOWETSEASONMIN")) {
7763 1 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7764 1 : typicalExtPer.MatchValue1 = "TropicalCold";
7765 1 : typicalExtPer.MatchValue2 = "WinterExtreme";
7766 :
7767 0 : } else if (ExtremePeriodTitle == "TROPICALHOT") {
7768 0 : typicalExtPer.MatchValue = "TropicalHot";
7769 0 : typicalExtPer.MatchValue1 = "SummerExtreme";
7770 0 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7771 :
7772 0 : } else if (ExtremePeriodTitle == "TROPICALCOLD") {
7773 0 : typicalExtPer.MatchValue = "TropicalCold";
7774 0 : typicalExtPer.MatchValue1 = "WinterExtreme";
7775 0 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7776 :
7777 : } else {
7778 0 : typicalExtPer.MatchValue = "Invalid - no match";
7779 : }
7780 177 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, 0);
7781 177 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, 0);
7782 177 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7783 176 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7784 : } else {
7785 1 : typicalExtPer.TotalDays =
7786 1 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7787 : }
7788 177 : }
7789 30 : } break;
7790 30 : case Weather::EpwHeaderType::GroundTemperatures: {
7791 : // Added for ground surfaces defined with F or c factor method. TH 7/2009
7792 : // Assume the 0.5 m set of ground temperatures
7793 : // or first set on a weather file, if any.
7794 30 : Pos = index(Line, ',');
7795 30 : if (Pos != std::string::npos) {
7796 : bool errFlag;
7797 30 : int NumGrndTemps = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7798 30 : if (!errFlag && NumGrndTemps >= 1) {
7799 30 : Line.erase(0, Pos + 1);
7800 : // skip depth, soil conductivity, soil density, soil specific heat
7801 150 : for (int i = 1; i <= 4; ++i) {
7802 120 : Pos = index(Line, ',');
7803 120 : if (Pos == std::string::npos) {
7804 0 : Line.clear();
7805 0 : break;
7806 : }
7807 120 : Line.erase(0, Pos + 1);
7808 : }
7809 30 : state.dataWeather->GroundTempsFCFromEPWHeader = 0.0;
7810 30 : int actcount = 0;
7811 390 : for (int i = 1; i <= 12; ++i) { // take the first set of ground temperatures.
7812 360 : Pos = index(Line, ',');
7813 360 : if (Pos != std::string::npos) {
7814 360 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7815 360 : ++actcount;
7816 : } else {
7817 0 : if (len(Line) > 0) {
7818 0 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7819 0 : ++actcount;
7820 : }
7821 0 : break;
7822 : }
7823 360 : Line.erase(0, Pos + 1);
7824 : }
7825 30 : if (actcount == 12) {
7826 30 : state.dataWeather->wthFCGroundTemps = true;
7827 : }
7828 : }
7829 : }
7830 30 : } break;
7831 31 : case Weather::EpwHeaderType::HolidaysDST: {
7832 : // A1, \field LeapYear Observed
7833 : // \type choice
7834 : // \key Yes
7835 : // \key No
7836 : // \note Yes if Leap Year will be observed for this file
7837 : // \note No if Leap Year days (29 Feb) should be ignored in this file
7838 : // A2, \field Daylight Saving Start Day
7839 : // A3, \field Daylight Saving End Day
7840 : // N1, \field Number of Holidays
7841 : // A4, \field Holiday 1 Name
7842 : // A5, \field Holiday 1 Day
7843 : // etc.
7844 : // Start with Minimum number of NumHdArgs
7845 31 : uppercase(Line);
7846 31 : NumHdArgs = 4;
7847 31 : int CurCount = 0;
7848 155 : for (int i = 1; i <= NumHdArgs; ++i) {
7849 124 : strip(Line);
7850 124 : Pos = index(Line, ',');
7851 124 : if (Pos == std::string::npos) {
7852 30 : if (len(Line) == 0) {
7853 0 : while (Pos == std::string::npos) {
7854 0 : Line = state.files.inputWeatherFile.readLine().data;
7855 0 : strip(Line);
7856 0 : uppercase(Line);
7857 0 : Pos = index(Line, ',');
7858 : }
7859 : } else {
7860 30 : Pos = len(Line);
7861 : }
7862 : }
7863 :
7864 : int PMonth;
7865 : int PDay;
7866 : int PWeekDay;
7867 : bool IOStatus;
7868 124 : if (i == 1) {
7869 31 : state.dataWeather->WFAllowsLeapYears = (Line[0] == 'Y');
7870 93 : } else if (i == 2) {
7871 : // In this section, we call ProcessDateString, and if that fails, we can recover from it
7872 : // by setting DST to false, so we don't affect ErrorsFound
7873 :
7874 : // call ProcessDateString with local bool (unused)
7875 : bool errflag1;
7876 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, errflag1);
7877 31 : if (dateType != DateType::Invalid) {
7878 : // ErrorsFound is still false after ProcessDateString
7879 31 : if (PMonth == 0 && PDay == 0) {
7880 30 : state.dataWeather->EPWDaylightSaving = false;
7881 : } else {
7882 1 : state.dataWeather->EPWDaylightSaving = true;
7883 1 : state.dataWeather->EPWDST.StDateType = dateType;
7884 1 : state.dataWeather->EPWDST.StMon = PMonth;
7885 1 : state.dataWeather->EPWDST.StDay = PDay;
7886 1 : state.dataWeather->EPWDST.StWeekDay = PWeekDay;
7887 : }
7888 : } else {
7889 : // ErrorsFound is untouched
7890 0 : ShowContinueError(
7891 0 : state, format("ProcessEPWHeader: Invalid Daylight Saving Period Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7892 0 : ShowContinueError(state, format("...invalid header={}", epwHeaders[static_cast<int>(headerType)]));
7893 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7894 0 : state.dataWeather->EPWDaylightSaving = false;
7895 : }
7896 :
7897 62 : } else if (i == 3) {
7898 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7899 31 : if (state.dataWeather->EPWDaylightSaving) {
7900 1 : if (dateType != DateType::Invalid) {
7901 1 : state.dataWeather->EPWDST.EnDateType = dateType;
7902 1 : state.dataWeather->EPWDST.EnMon = PMonth;
7903 1 : state.dataWeather->EPWDST.EnDay = PDay;
7904 1 : state.dataWeather->EPWDST.EnWeekDay = PWeekDay;
7905 : } else {
7906 0 : ShowWarningError(
7907 : state,
7908 0 : format("ProcessEPWHeader: Invalid Daylight Saving Period End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7909 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7910 0 : state.dataWeather->EPWDaylightSaving = false;
7911 : }
7912 1 : state.dataWeather->DST = state.dataWeather->EPWDST;
7913 : }
7914 :
7915 31 : } else if (i == 4) {
7916 31 : int NumEPWHolidays = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7917 62 : state.dataWeather->NumSpecialDays =
7918 31 : NumEPWHolidays + state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriodControl:SpecialDays");
7919 31 : state.dataWeather->SpecialDays.allocate(state.dataWeather->NumSpecialDays);
7920 31 : NumHdArgs = 4 + NumEPWHolidays * 2;
7921 :
7922 0 : } else if ((i >= 5)) {
7923 0 : if (mod(i, 2) != 0) {
7924 0 : ++CurCount;
7925 0 : if (CurCount > state.dataWeather->NumSpecialDays) {
7926 0 : ShowSevereError(state, "Too many SpecialDays");
7927 0 : ErrorsFound = true;
7928 : } else {
7929 0 : state.dataWeather->SpecialDays(CurCount).Name = Line.substr(0, Pos);
7930 : }
7931 : // Process name
7932 : } else {
7933 0 : if (CurCount <= state.dataWeather->NumSpecialDays) {
7934 0 : auto &specialDay = state.dataWeather->SpecialDays(CurCount);
7935 : // Process date
7936 0 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7937 0 : if (dateType == DateType::MonthDay) {
7938 0 : specialDay.dateType = dateType;
7939 0 : specialDay.Month = PMonth;
7940 0 : specialDay.Day = PDay;
7941 0 : specialDay.WeekDay = 0;
7942 0 : specialDay.CompDate = PMonth * 32 + PDay;
7943 0 : specialDay.Duration = 1;
7944 0 : specialDay.DayType = 1;
7945 0 : specialDay.WthrFile = true;
7946 0 : } else if (dateType != DateType::Invalid) {
7947 0 : specialDay.dateType = dateType;
7948 0 : specialDay.Month = PMonth;
7949 0 : specialDay.Day = PDay;
7950 0 : specialDay.WeekDay = PWeekDay;
7951 0 : specialDay.CompDate = 0;
7952 0 : specialDay.Duration = 1;
7953 0 : specialDay.DayType = 1;
7954 0 : specialDay.WthrFile = true;
7955 : } else {
7956 0 : ShowSevereError(state, format("Invalid SpecialDay Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7957 0 : ErrorsFound = true;
7958 : }
7959 : }
7960 : }
7961 : }
7962 124 : Line.erase(0, Pos + 1);
7963 : }
7964 211 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7965 : // General::OrdinalDay (Month,Day,LeapYearValue)
7966 180 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7967 180 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, state.dataWeather->LeapYearAdd);
7968 180 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, state.dataWeather->LeapYearAdd);
7969 180 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7970 179 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7971 : } else {
7972 1 : typicalExtPer.TotalDays =
7973 1 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7974 : }
7975 : }
7976 31 : } break;
7977 90 : case Weather::EpwHeaderType::Comments1:
7978 : case Weather::EpwHeaderType::Comments2:
7979 : case Weather::EpwHeaderType::DesignConditions: {
7980 : // no action
7981 90 : } break;
7982 30 : case Weather::EpwHeaderType::DataPeriods: {
7983 : // N1, \field Number of Data Periods
7984 : // N2, \field Number of Records per hour
7985 : // A1, \field Data Period 1 Name/Description
7986 : // A2, \field Data Period 1 Start Day of Week
7987 : // \type choice
7988 : // \key Sunday
7989 : // \key Monday
7990 : // \key Tuesday
7991 : // \key Wednesday
7992 : // \key Thursday
7993 : // \key Friday
7994 : // \key Saturday
7995 : // A3, \field Data Period 1 Start Day
7996 : // A4, \field Data Period 1 End Day
7997 30 : uppercase(Line);
7998 30 : NumHdArgs = 2;
7999 30 : int CurCount = 0;
8000 214 : for (int i = 1; i <= NumHdArgs; ++i) {
8001 184 : strip(Line);
8002 184 : Pos = index(Line, ',');
8003 184 : if (Pos == std::string::npos) {
8004 29 : if (len(Line) == 0) {
8005 0 : while (Pos == std::string::npos) {
8006 0 : Line = state.files.inputWeatherFile.readLine().data;
8007 0 : strip(Line);
8008 0 : uppercase(Line);
8009 0 : Pos = index(Line, ',');
8010 : }
8011 : } else {
8012 29 : Pos = len(Line);
8013 : }
8014 : }
8015 :
8016 : bool IOStatus;
8017 184 : if (i == 1) {
8018 30 : state.dataWeather->NumDataPeriods = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
8019 30 : state.dataWeather->DataPeriods.allocate(state.dataWeather->NumDataPeriods);
8020 30 : NumHdArgs += 4 * state.dataWeather->NumDataPeriods;
8021 30 : if (state.dataWeather->NumDataPeriods > 0) {
8022 61 : for (auto &e : state.dataWeather->DataPeriods) {
8023 31 : e.NumDays = 0;
8024 30 : }
8025 : }
8026 :
8027 154 : } else if (i == 2) {
8028 30 : state.dataWeather->NumIntervalsPerHour = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
8029 124 : } else if (i >= 3) {
8030 124 : int const CurOne = mod(i - 3, 4);
8031 : int PMonth;
8032 : int PDay;
8033 : int PWeekDay;
8034 : int PYear;
8035 124 : if (CurOne == 0) {
8036 : // Description of Data Period
8037 31 : ++CurCount;
8038 31 : if (CurCount > state.dataWeather->NumDataPeriods) {
8039 0 : ShowSevereError(state, "Too many data periods");
8040 0 : ErrorsFound = true;
8041 : } else {
8042 31 : state.dataWeather->DataPeriods(CurCount).Name = Line.substr(0, Pos);
8043 : }
8044 :
8045 93 : } else if (CurOne == 1) {
8046 : // Start Day of Week
8047 31 : if (CurCount <= state.dataWeather->NumDataPeriods) {
8048 31 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
8049 31 : dataPeriod.DayOfWeek = Line.substr(0, Pos);
8050 31 : dataPeriod.WeekDay = getEnumValue(Sched::dayTypeNamesUC, dataPeriod.DayOfWeek);
8051 31 : if (dataPeriod.WeekDay < 1 || dataPeriod.WeekDay > 7) {
8052 0 : ShowSevereError(state,
8053 0 : fmt::format("Weather File -- Invalid Start Day of Week for Data Period #{}, Invalid day={}",
8054 : CurCount,
8055 0 : dataPeriod.DayOfWeek));
8056 0 : ErrorsFound = true;
8057 : }
8058 : }
8059 :
8060 62 : } else if (CurOne == 2) {
8061 : // DataPeriod Start Day
8062 31 : if (CurCount <= state.dataWeather->NumDataPeriods) {
8063 31 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
8064 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
8065 31 : if (dateType == DateType::MonthDay) {
8066 31 : dataPeriod.StMon = PMonth;
8067 31 : dataPeriod.StDay = PDay;
8068 31 : dataPeriod.StYear = PYear;
8069 31 : if (PYear != 0) {
8070 1 : dataPeriod.HasYearData = true;
8071 : }
8072 : } else {
8073 0 : ShowSevereError(state,
8074 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day> (WeatherFile), found={}",
8075 0 : Line.substr(0, Pos)));
8076 0 : ErrorsFound = true;
8077 : }
8078 : }
8079 :
8080 31 : } else if (CurOne == 3) {
8081 31 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
8082 31 : if (CurCount <= state.dataWeather->NumDataPeriods) {
8083 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
8084 31 : if (dateType == DateType::MonthDay) {
8085 31 : dataPeriod.EnMon = PMonth;
8086 31 : dataPeriod.EnDay = PDay;
8087 31 : dataPeriod.EnYear = PYear;
8088 31 : if (PYear == 0 && dataPeriod.HasYearData) {
8089 0 : ShowWarningError(state, "Data Period (WeatherFile) - Start Date contains year. End Date does not.");
8090 0 : ShowContinueError(state, "...Assuming same year as Start Date for this data.");
8091 0 : dataPeriod.EnYear = dataPeriod.StYear;
8092 : }
8093 : } else {
8094 0 : ShowSevereError(state,
8095 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day>, (WeatherFile) found={}",
8096 0 : Line.substr(0, Pos)));
8097 0 : ErrorsFound = true;
8098 : }
8099 : }
8100 31 : if (dataPeriod.StYear == 0 || dataPeriod.EnYear == 0) {
8101 30 : dataPeriod.DataStJDay = General::OrdinalDay(dataPeriod.StMon, dataPeriod.StDay, state.dataWeather->LeapYearAdd);
8102 30 : dataPeriod.DataEnJDay = General::OrdinalDay(dataPeriod.EnMon, dataPeriod.EnDay, state.dataWeather->LeapYearAdd);
8103 30 : if (dataPeriod.DataStJDay <= dataPeriod.DataEnJDay) {
8104 30 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
8105 : } else {
8106 0 : dataPeriod.NumDays = (365 - dataPeriod.DataStJDay + 1) + (dataPeriod.DataEnJDay - 1 + 1);
8107 : }
8108 : } else { // weather file has actual year(s)
8109 1 : dataPeriod.DataStJDay = computeJulianDate(dataPeriod.StYear, dataPeriod.StMon, dataPeriod.StDay);
8110 1 : dataPeriod.DataEnJDay = computeJulianDate(dataPeriod.EnYear, dataPeriod.EnMon, dataPeriod.EnDay);
8111 1 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
8112 : }
8113 : // Have processed the last item for this, can set up Weekdays for months
8114 31 : dataPeriod.MonWeekDay = 0;
8115 31 : if (!ErrorsFound) {
8116 93 : SetupWeekDaysByMonth(state,
8117 31 : state.dataWeather->DataPeriods(CurCount).StMon,
8118 31 : state.dataWeather->DataPeriods(CurCount).StDay,
8119 31 : state.dataWeather->DataPeriods(CurCount).WeekDay,
8120 31 : state.dataWeather->DataPeriods(CurCount).MonWeekDay);
8121 : }
8122 : }
8123 : }
8124 184 : Line.erase(0, Pos + 1);
8125 : }
8126 30 : } break;
8127 0 : default: {
8128 : // Invalid header type
8129 0 : assert(false);
8130 : } break;
8131 : }
8132 241 : }
8133 :
8134 67 : void SkipEPlusWFHeader(EnergyPlusData &state)
8135 : {
8136 :
8137 : // SUBROUTINE INFORMATION:
8138 : // AUTHOR Linda K. Lawrie
8139 : // DATE WRITTEN August 2000
8140 :
8141 : // PURPOSE OF THIS SUBROUTINE:
8142 : // This subroutine skips the initial header records on the EnergyPlus Weather File (in.epw).
8143 :
8144 : static constexpr std::string_view Header("DATA PERIODS");
8145 :
8146 : // Read in Header Information
8147 134 : InputFile::ReadResult<std::string> Line{"", true, false};
8148 :
8149 : // Headers should come in order
8150 : while (true) {
8151 536 : Line = state.files.inputWeatherFile.readLine();
8152 536 : if (Line.eof) {
8153 0 : ShowFatalError(state,
8154 0 : format("Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}", Header),
8155 0 : OptionalOutputFileRef{state.files.eso});
8156 : }
8157 536 : uppercase(Line.data);
8158 536 : if (has(Line.data, Header)) {
8159 67 : break;
8160 : }
8161 : }
8162 :
8163 : // Dummy process Data Periods line
8164 : // 'DATA PERIODS'
8165 : // N1, \field Number of Data Periods
8166 : // N2, \field Number of Records per hour
8167 : // A1, \field Data Period 1 Name/Description
8168 : // A2, \field Data Period 1 Start Day of Week
8169 : // \type choice
8170 : // \key Sunday
8171 : // \key Monday
8172 : // \key Tuesday
8173 : // \key Wednesday
8174 : // \key Thursday
8175 : // \key Friday
8176 : // \key Saturday
8177 : // A3, \field Data Period 1 Start Day
8178 : // A4, \field Data Period 1 End Day
8179 :
8180 67 : int NumHdArgs = 2;
8181 67 : int CurCount = 0;
8182 201 : for (int i = 1; i <= NumHdArgs; ++i) {
8183 134 : strip(Line.data);
8184 134 : std::string::size_type Pos = index(Line.data, ',');
8185 134 : if (Pos == std::string::npos) {
8186 0 : if (len(Line.data) == 0) {
8187 0 : while (Pos == std::string::npos) {
8188 0 : Line = state.files.inputWeatherFile.readLine();
8189 0 : strip(Line.data);
8190 0 : uppercase(Line.data);
8191 0 : Pos = index(Line.data, ',');
8192 : }
8193 : } else {
8194 0 : Pos = len(Line.data);
8195 : }
8196 : }
8197 :
8198 134 : if (i == 1) {
8199 : bool IOStatus;
8200 67 : int const NumPeriods = Util::ProcessNumber(Line.data.substr(0, Pos), IOStatus);
8201 67 : NumHdArgs += 4 * NumPeriods;
8202 67 : } else if ((i >= 3)) {
8203 0 : if (mod(i - 3, 4) == 0) {
8204 0 : ++CurCount;
8205 : }
8206 : }
8207 134 : Line.data.erase(0, Pos + 1);
8208 : }
8209 67 : }
8210 :
8211 22 : void ReportMissing_RangeData(EnergyPlusData &state)
8212 : {
8213 :
8214 : // SUBROUTINE INFORMATION:
8215 : // AUTHOR Linda Lawrie
8216 : // DATE WRITTEN January 2002
8217 :
8218 : // PURPOSE OF THIS SUBROUTINE:
8219 : // This subroutine reports the counts of missing/out of range data
8220 : // for weather file environments.
8221 :
8222 : static constexpr std::string_view MissString("Missing Data Found on Weather Data File");
8223 : static constexpr std::string_view msFmt("Missing {}, Number of items={:5}");
8224 : static constexpr std::string_view InvString("Invalid Data Found on Weather Data File");
8225 : static constexpr std::string_view ivFmt("Invalid {}, Number of items={:5}");
8226 : static constexpr std::string_view RangeString("Out of Range Data Found on Weather Data File");
8227 : static constexpr std::string_view rgFmt("Out of Range {} [{},{}], Number of items={:5}");
8228 :
8229 22 : if (!state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
8230 22 : return;
8231 : }
8232 :
8233 0 : bool MissedHeader = false;
8234 0 : auto missedHeaderCheck = [&](Real64 const value, std::string const &description) {
8235 0 : if (value > 0) {
8236 0 : if (!MissedHeader) {
8237 0 : ShowWarningError(state, std::string{MissString});
8238 0 : MissedHeader = true;
8239 : }
8240 0 : ShowMessage(state, format(msFmt, "\"" + description + "\"", value));
8241 : }
8242 0 : };
8243 :
8244 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDryBulbTemp, "Dry Bulb Temperature");
8245 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutBaroPress, "Atmospheric Pressure");
8246 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutRelHum, "Relative Humidity");
8247 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDewPointTemp, "Dew Point Temperatures");
8248 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindSpeed, "Wind Speed");
8249 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindDir, "Wind Direction");
8250 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.BeamSolarRad, "Direct Radiation");
8251 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.DifSolarRad, "Diffuse Radiation");
8252 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.TotalSkyCover, "Total Sky Cover");
8253 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OpaqueSkyCover, "Opaque Sky Cover");
8254 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.SnowDepth, "Snow Depth");
8255 0 : if (state.dataWeather->wvarsMissedCounts.WeathCodes > 0) {
8256 0 : ShowWarningError(state, std::string{InvString});
8257 0 : ShowMessage(state, format(ivFmt, "\"Weather Codes\" (not equal 9 digits)", state.dataWeather->wvarsMissedCounts.WeathCodes));
8258 : }
8259 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.LiquidPrecip, "Liquid Precipitation Depth");
8260 :
8261 0 : bool OutOfRangeHeader = false;
8262 : auto outOfRangeHeaderCheck = // (AUTO_OK_LAMBDA)
8263 0 : [&](Real64 const value, std::string_view description, std::string_view rangeLow, std::string_view rangeHigh, std::string_view extraMsg) {
8264 0 : if (value > 0) {
8265 0 : if (!OutOfRangeHeader) {
8266 0 : ShowWarningError(state, std::string{RangeString});
8267 0 : OutOfRangeHeader = true;
8268 : }
8269 0 : ShowMessage(state, EnergyPlus::format(rgFmt, description, rangeLow, rangeHigh, value));
8270 0 : if (!extraMsg.empty()) {
8271 0 : ShowMessage(state, std::string{extraMsg});
8272 : }
8273 : }
8274 0 : };
8275 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp, "Dry Bulb Temperatures", ">=-90", "<=70", "");
8276 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress,
8277 : "Atmospheric Pressure",
8278 : ">31000",
8279 : "<=120000",
8280 : "Out of Range values set to last good value");
8281 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutRelHum, "Relative Humidity", ">=0", "<=110", "");
8282 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp, "Dew Point Temperatures", ">=-90", "<=70", "");
8283 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindSpeed, "Wind Speed", ">=0", "<=40", "");
8284 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindDir, "Wind Direction", ">=0", "<=360", "");
8285 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad, "Direct Radiation", ">=0", "NoLimit", "");
8286 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad, "Diffuse Radiation", ">=0", "NoLimit", "");
8287 : }
8288 :
8289 112 : void SetupInterpolationValues(EnergyPlusData &state)
8290 : {
8291 :
8292 : // SUBROUTINE INFORMATION:
8293 : // AUTHOR Linda Lawrie
8294 : // DATE WRITTEN November 2002
8295 :
8296 : // PURPOSE OF THIS SUBROUTINE:
8297 : // This subroutine creates the "interpolation" values / weights that are used for
8298 : // interpolating weather data from hourly down to the time step level.
8299 :
8300 : // METHODOLOGY EMPLOYED:
8301 : // Create arrays (InterpolationValues, SolarInterpolationValues) dependent on
8302 : // Number of Time Steps in Hour. This will be used in the "SetCurrentWeather" procedure.
8303 :
8304 112 : state.dataWeather->Interpolation.allocate(state.dataGlobal->TimeStepsInHour);
8305 112 : state.dataWeather->SolarInterpolation.allocate(state.dataGlobal->TimeStepsInHour);
8306 112 : state.dataWeather->Interpolation = 0.0;
8307 112 : state.dataWeather->SolarInterpolation = 0.0;
8308 :
8309 716 : for (int tloop = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop) {
8310 604 : state.dataWeather->Interpolation(tloop) =
8311 604 : (state.dataGlobal->TimeStepsInHour == 1) ? 1.0 : min(1.0, (double(tloop) / double(state.dataGlobal->TimeStepsInHour)));
8312 : }
8313 :
8314 112 : if (mod(state.dataGlobal->TimeStepsInHour, 2) == 0) {
8315 : // even number of time steps.
8316 102 : int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
8317 :
8318 102 : state.dataWeather->SolarInterpolation(halfpoint) = 1.0;
8319 102 : Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
8320 399 : for (int tloop = halfpoint + 1, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
8321 297 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8322 : }
8323 297 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8324 195 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8325 : }
8326 : } else { // odd number of time steps
8327 10 : if (state.dataGlobal->TimeStepsInHour == 1) {
8328 10 : state.dataWeather->SolarInterpolation(1) = 0.5;
8329 0 : } else if (state.dataGlobal->TimeStepsInHour == 3) {
8330 0 : state.dataWeather->SolarInterpolation(1) = 5.0 / 6.0;
8331 0 : state.dataWeather->SolarInterpolation(2) = 5.0 / 6.0;
8332 0 : state.dataWeather->SolarInterpolation(3) = 0.5;
8333 : } else {
8334 0 : Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
8335 0 : int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
8336 0 : Real64 tweight1 = 1.0 - tweight / 2.0;
8337 0 : state.dataWeather->SolarInterpolation(halfpoint) = tweight1;
8338 0 : state.dataWeather->SolarInterpolation(halfpoint + 1) = tweight1;
8339 0 : for (int tloop = halfpoint + 2, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
8340 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8341 : }
8342 0 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8343 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8344 : }
8345 : }
8346 : }
8347 112 : }
8348 :
8349 114 : void SetupEnvironmentTypes(EnergyPlusData &state)
8350 : {
8351 :
8352 : // SUBROUTINE INFORMATION:
8353 : // AUTHOR Linda Lawrie
8354 : // DATE WRITTEN October 2010
8355 :
8356 : // PURPOSE OF THIS SUBROUTINE:
8357 : // Make sure Environment derived type is set prior to getting
8358 : // Weather Properties
8359 :
8360 : // Transfer weather file information to the Environment derived type
8361 114 : state.dataWeather->Envrn = state.dataEnvrn->TotDesDays + 1;
8362 :
8363 : // Sizing Periods from Weather File
8364 114 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunDesPers; ++iRunPer, ++state.dataWeather->Envrn) {
8365 0 : auto const &runPer = state.dataWeather->RunPeriodDesignInput(iRunPer);
8366 0 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8367 :
8368 0 : envCurr.StartMonth = runPer.startMonth;
8369 0 : envCurr.StartDay = runPer.startDay;
8370 0 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, state.dataWeather->LeapYearAdd);
8371 0 : envCurr.TotalDays = runPer.totalDays;
8372 0 : envCurr.EndMonth = runPer.endMonth;
8373 0 : envCurr.EndDay = runPer.endDay;
8374 0 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, state.dataWeather->LeapYearAdd);
8375 0 : envCurr.NumSimYears = runPer.numSimYears;
8376 0 : if (envCurr.StartJDay <= envCurr.EndJDay) {
8377 0 : envCurr.TotalDays = (envCurr.EndJDay - envCurr.StartJDay + 1) * envCurr.NumSimYears;
8378 : } else {
8379 0 : envCurr.TotalDays =
8380 0 : (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - envCurr.StartJDay + 1 + envCurr.EndJDay) * envCurr.NumSimYears;
8381 : }
8382 0 : state.dataEnvrn->TotRunDesPersDays += envCurr.TotalDays;
8383 0 : envCurr.UseDST = runPer.useDST;
8384 0 : envCurr.UseHolidays = runPer.useHolidays;
8385 0 : envCurr.Title = runPer.title;
8386 0 : envCurr.cKindOfEnvrn = runPer.periodType;
8387 0 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
8388 0 : envCurr.DesignDayNum = 0;
8389 0 : envCurr.RunPeriodDesignNum = iRunPer;
8390 0 : envCurr.DayOfWeek = runPer.dayOfWeek;
8391 0 : envCurr.MonWeekDay = runPer.monWeekDay;
8392 0 : envCurr.SetWeekDays = false;
8393 0 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8394 0 : envCurr.UseRain = runPer.useRain;
8395 0 : envCurr.UseSnow = runPer.useSnow;
8396 0 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // this will just the default
8397 : }
8398 :
8399 : // RunPeriods from weather file
8400 172 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunPers; ++iRunPer, ++state.dataWeather->Envrn) {
8401 58 : auto const &runPer = state.dataWeather->RunPeriodInput(iRunPer);
8402 58 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8403 :
8404 58 : envCurr.StartMonth = runPer.startMonth;
8405 58 : envCurr.StartDay = runPer.startDay;
8406 58 : envCurr.StartYear = runPer.startYear;
8407 58 : envCurr.EndMonth = runPer.endMonth;
8408 58 : envCurr.EndDay = runPer.endDay;
8409 58 : envCurr.EndYear = runPer.endYear;
8410 58 : envCurr.NumSimYears = runPer.numSimYears;
8411 58 : envCurr.CurrentYear = runPer.startYear;
8412 58 : envCurr.IsLeapYear = runPer.isLeapYear;
8413 58 : envCurr.TreatYearsAsConsecutive = true;
8414 58 : if (runPer.actualWeather) {
8415 : // This will require leap years to be present, thus Julian days can be used for all the calculations
8416 0 : envCurr.StartJDay = envCurr.StartDate = runPer.startJulianDate;
8417 0 : envCurr.EndJDay = envCurr.EndDate = runPer.endJulianDate;
8418 0 : envCurr.TotalDays = envCurr.EndDate - envCurr.StartDate + 1;
8419 0 : envCurr.RawSimDays = envCurr.EndDate - envCurr.StartDate + 1;
8420 0 : envCurr.MatchYear = true;
8421 0 : envCurr.ActualWeather = true;
8422 : } else { // std RunPeriod
8423 58 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8424 58 : if (envCurr.StartYear == envCurr.EndYear) {
8425 : // Short-circuit all the calculations, we're in a single year
8426 :
8427 58 : envCurr.IsLeapYear = isLeapYear(envCurr.StartYear) && state.dataWeather->WFAllowsLeapYears;
8428 58 : int LocalLeapYearAdd = (int)envCurr.IsLeapYear;
8429 :
8430 58 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, LocalLeapYearAdd);
8431 58 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, LocalLeapYearAdd);
8432 58 : envCurr.RawSimDays = (envCurr.EndJDay - envCurr.StartJDay + 1);
8433 58 : envCurr.TotalDays = envCurr.RawSimDays;
8434 : } else {
8435 : // Environment crosses year boundaries
8436 0 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8437 0 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, (int)runPer.isLeapYear);
8438 0 : envCurr.EndJDay = General::OrdinalDay(
8439 0 : runPer.endMonth, runPer.endDay, (int)(isLeapYear(runPer.endYear) && state.dataWeather->WFAllowsLeapYears));
8440 0 : envCurr.TotalDays = 366 - envCurr.StartJDay + envCurr.EndJDay + 365 * std::max(envCurr.NumSimYears - 2, 0);
8441 0 : if (state.dataWeather->WFAllowsLeapYears) {
8442 : // First year
8443 0 : if (envCurr.StartJDay < 59) {
8444 0 : if (isLeapYear(envCurr.StartYear)) {
8445 0 : ++envCurr.TotalDays;
8446 : }
8447 : }
8448 : // Middle years
8449 0 : for (int yr = envCurr.StartYear + 1; yr < envCurr.EndYear; ++yr) {
8450 0 : if (isLeapYear(yr)) {
8451 0 : ++envCurr.TotalDays;
8452 : }
8453 : }
8454 : // Last year not needed, the end ordinal date will take this into account
8455 : }
8456 0 : envCurr.RawSimDays = envCurr.TotalDays;
8457 : }
8458 : }
8459 58 : envCurr.UseDST = runPer.useDST;
8460 58 : envCurr.UseHolidays = runPer.useHolidays;
8461 58 : if (runPer.title.empty()) {
8462 2 : envCurr.Title = state.dataEnvrn->WeatherFileLocationTitle;
8463 : } else {
8464 56 : envCurr.Title = runPer.title;
8465 : }
8466 58 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::ReadAllWeatherData) {
8467 2 : envCurr.cKindOfEnvrn = "ReadAllWeatherDataRunPeriod";
8468 : } else {
8469 56 : envCurr.cKindOfEnvrn = "WeatherFileRunPeriod";
8470 56 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
8471 : }
8472 58 : envCurr.DayOfWeek = runPer.dayOfWeek;
8473 58 : envCurr.MonWeekDay = runPer.monWeekDay;
8474 58 : envCurr.SetWeekDays = false;
8475 58 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8476 58 : envCurr.UseRain = runPer.useRain;
8477 58 : envCurr.UseSnow = runPer.useSnow;
8478 58 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // first hour interpolation choice
8479 : } // for (i)
8480 114 : }
8481 :
8482 124 : bool isLeapYear(int const Year)
8483 : {
8484 : // true if it's a leap year, false if not.
8485 :
8486 124 : if (mod(Year, 4) == 0) { // Potential Leap Year
8487 5 : if (!(mod(Year, 100) == 0 && mod(Year, 400) != 0)) {
8488 5 : return true;
8489 : }
8490 : }
8491 119 : return false;
8492 : }
8493 :
8494 320 : int computeJulianDate(int const gyyyy, // input/output gregorian year, should be specified as 4 digits
8495 : int const gmm, // input/output gregorian month
8496 : int const gdd // input/output gregorian day
8497 : )
8498 : {
8499 : // SUBROUTINE INFORMATION:
8500 : // AUTHOR Jason DeGraw
8501 : // DATE WRITTEN 10/25/2017
8502 :
8503 : // PURPOSE OF THIS SUBROUTINE:
8504 : // Split the former JGDate function in two. Convert a gregorian
8505 : // date to actual julian date. the advantage of storing a julian date
8506 : // in the jdate format rather than a 5 digit format is that any
8507 : // number of days can be add or subtracted to jdate and
8508 : // that result is a proper julian date.
8509 :
8510 : // REFERENCES:
8511 : // for discussion of this algorithm,
8512 : // see cacm, vol 11, no 10, oct 1968, page 657
8513 :
8514 320 : int tyyyy = gyyyy;
8515 320 : int tmm = gmm;
8516 320 : int tdd = gdd;
8517 320 : int l = (tmm - 14) / 12;
8518 320 : return tdd - 32075 + 1461 * (tyyyy + 4800 + l) / 4 + 367 * (tmm - 2 - l * 12) / 12 - 3 * ((tyyyy + 4900 + l) / 100) / 4;
8519 : }
8520 :
8521 4 : int computeJulianDate(GregorianDate const &gdate)
8522 : {
8523 4 : return computeJulianDate(gdate.year, gdate.month, gdate.day);
8524 : }
8525 :
8526 4 : GregorianDate computeGregorianDate(int const jdate)
8527 : {
8528 4 : int tdate = jdate;
8529 4 : int l = tdate + 68569;
8530 4 : int n = 4 * l / 146097;
8531 4 : l -= (146097 * n + 3) / 4;
8532 4 : int tyyyy = 4000 * (l + 1) / 1461001;
8533 4 : l = l - 1461 * tyyyy / 4 + 31;
8534 4 : int tmm = 80 * l / 2447;
8535 4 : int tdd = l - 2447 * tmm / 80;
8536 4 : l = tmm / 11;
8537 4 : tmm += 2 - 12 * l;
8538 4 : tyyyy += 100 * (n - 49) + l;
8539 4 : return {tyyyy, tmm, tdd};
8540 : }
8541 :
8542 6 : Sched::DayType calculateDayOfWeek(EnergyPlusData &state, int const year, int const month, int const day)
8543 : {
8544 :
8545 : // FUNCTION INFORMATION:
8546 : // AUTHOR Linda Lawrie
8547 : // DATE WRITTEN March 2012
8548 : // MODIFIED October 2017, Jason DeGraw
8549 :
8550 : // PURPOSE OF THIS FUNCTION:
8551 : // Calculate the correct day of week.
8552 :
8553 : // METHODOLOGY EMPLOYED:
8554 : // Zeller's algorithm.
8555 :
8556 : // REFERENCES:
8557 : // http://en.wikipedia.org/wiki/Zeller%27s_congruence
8558 : // and other references around the web.
8559 :
8560 6 : int Gyyyy(year); // Gregorian yyyy
8561 6 : int Gmm(month); // Gregorian mm
8562 :
8563 : // Jan, Feb are 13, 14 months of previous year
8564 6 : if (Gmm < 3) {
8565 6 : Gmm += 12;
8566 6 : --Gyyyy;
8567 : }
8568 :
8569 6 : state.dataEnvrn->DayOfWeek = mod(day + (13 * (Gmm + 1) / 5) + Gyyyy + (Gyyyy / 4) + 6 * (Gyyyy / 100) + (Gyyyy / 400), 7);
8570 6 : if (state.dataEnvrn->DayOfWeek == 0) {
8571 1 : state.dataEnvrn->DayOfWeek = 7;
8572 : }
8573 :
8574 6 : return static_cast<Sched::DayType>(state.dataEnvrn->DayOfWeek);
8575 : }
8576 :
8577 57 : int calculateDayOfYear(int const Month, int const Day, bool const leapYear)
8578 : {
8579 :
8580 : // FUNCTION INFORMATION:
8581 : // AUTHOR Jason DeGraw
8582 : // DATE WRITTEN October 10, 2017
8583 :
8584 : // PURPOSE OF THIS FUNCTION:
8585 : // Compute the day of the year for leap and non-leap years.
8586 :
8587 : static std::array<int, 12> const daysbefore{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}};
8588 : static std::array<int, 12> const daysbeforeleap{{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
8589 :
8590 : // Could probably do some bounds checking here, but for now assume the month is in [1, 12]
8591 57 : if (leapYear) {
8592 2 : return daysbeforeleap[Month - 1] + Day;
8593 : } else {
8594 55 : return daysbefore[Month - 1] + Day;
8595 : }
8596 : }
8597 :
8598 124 : bool validMonthDay(int const month, int const day, int const leapYearAdd)
8599 : {
8600 :
8601 : // FUNCTION INFORMATION:
8602 : // AUTHOR Jason DeGraw
8603 : // DATE WRITTEN October 31, 2017
8604 :
8605 : // PURPOSE OF THIS FUNCTION:
8606 : // Determine if a month/day+leapyear combination is valid.
8607 :
8608 124 : switch (month) {
8609 123 : case 1:
8610 : case 3:
8611 : case 5:
8612 : case 7:
8613 : case 8:
8614 : case 10:
8615 : case 12:
8616 123 : if (day > 31) {
8617 0 : return false;
8618 : }
8619 123 : break;
8620 0 : case 4:
8621 : case 6:
8622 : case 9:
8623 : case 11:
8624 0 : if (day > 30) {
8625 0 : return false;
8626 : }
8627 0 : break;
8628 1 : case 2:
8629 1 : if (day > 28 + leapYearAdd) {
8630 0 : return false;
8631 : }
8632 1 : break;
8633 0 : default:
8634 0 : return false;
8635 : }
8636 124 : return true;
8637 : }
8638 :
8639 3 : void AnnualMonthlyDryBulbWeatherData::CalcAnnualAndMonthlyDryBulbTemp(EnergyPlusData &state)
8640 : {
8641 :
8642 : // PURPOSE OF THIS SUBROUTINE:
8643 : // Calculates monthly daily average outdoor air drybulb temperature from
8644 : // either weather (*.EPW) file or reads monthly daily average outdoor air
8645 : // drybulb temperature from STAT (*.stat) for use to autosize main water
8646 : // temperature.
8647 :
8648 3 : Real64 MonthlyDailyDryBulbMin(200.0); // monthly-daily minimum outside air dry-bulb temperature
8649 3 : Real64 MonthlyDailyDryBulbMax(-200.0); // monthly-daily maximum outside air dry-bulb temperature
8650 3 : Real64 AnnualDailyAverageDryBulbTempSum(0.0); // annual sum of daily average outside air dry-bulb temperature
8651 3 : Array1D<Real64> MonthlyAverageDryBulbTemp(12, 0.0); // monthly-daily average outside air temperature
8652 :
8653 3 : if (!this->OADryBulbWeatherDataProcessed) {
8654 3 : const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
8655 3 : const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
8656 3 : if (statFileExists) {
8657 2 : auto statFile = state.files.inStatFilePath.try_open();
8658 2 : if (!statFile.good()) {
8659 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", statFile.filePath));
8660 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8661 0 : return;
8662 : }
8663 :
8664 2 : std::string lineAvg;
8665 136 : while (statFile.good()) {
8666 135 : auto lineIn = statFile.readLine();
8667 135 : if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
8668 8 : for (int i = 1; i <= 7; ++i) {
8669 7 : lineIn = statFile.readLine();
8670 : }
8671 1 : lineIn = statFile.readLine();
8672 1 : lineAvg = lineIn.data;
8673 1 : break;
8674 : }
8675 135 : }
8676 2 : if (lineAvg.empty()) {
8677 2 : ShowSevereError(
8678 : state,
8679 2 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have Monthly Statistics for Dry Bulb temperatures.",
8680 : statFile.filePath));
8681 2 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8682 1 : return;
8683 1 : } else if (lineAvg.find("Daily Avg") == std::string::npos) {
8684 0 : ShowSevereError(state,
8685 0 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have the 'Daily Avg' line in the Monthly "
8686 : "Statistics for Dry Bulb temperatures.",
8687 : statFile.filePath));
8688 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8689 0 : return;
8690 : } else {
8691 1 : int AnnualNumberOfDays = 0;
8692 13 : for (int i = 1; i <= 12; ++i) {
8693 12 : MonthlyAverageDryBulbTemp(i) = OutputReportTabular::StrToReal(OutputReportTabular::GetColumnUsingTabs(lineAvg, i + 2));
8694 12 : AnnualDailyAverageDryBulbTempSum += MonthlyAverageDryBulbTemp(i) * state.dataWeather->EndDayOfMonth(i);
8695 12 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyAverageDryBulbTemp(i));
8696 12 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyAverageDryBulbTemp(i));
8697 12 : AnnualNumberOfDays += state.dataWeather->EndDayOfMonth(i);
8698 : }
8699 1 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8700 1 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8701 1 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8702 1 : this->OADryBulbWeatherDataProcessed = true;
8703 : }
8704 4 : } else if (epwFileExists) {
8705 1 : auto epwFile = state.files.inputWeatherFilePath.try_open();
8706 1 : bool epwHasLeapYear(false);
8707 1 : if (!epwFile.good()) {
8708 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", epwFile.filePath));
8709 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8710 0 : return;
8711 : }
8712 9 : for (int i = 1; i <= 8; ++i) { // Headers
8713 8 : auto epwLine = epwFile.readLine();
8714 :
8715 8 : if (i == 5) {
8716 : // HOLIDAYS/DAYLIGHT SAVINGS,Yes,0,0,0
8717 1 : std::string::size_type pos = index(epwLine.data, ',');
8718 1 : epwLine.data.erase(0, pos + 1);
8719 1 : pos = index(epwLine.data, ',');
8720 1 : std::string LeapYear = Util::makeUPPER(epwLine.data.substr(0, pos));
8721 1 : if (LeapYear[0] == 'Y') {
8722 0 : epwHasLeapYear = true;
8723 : }
8724 1 : }
8725 8 : }
8726 1 : Array1D<int> EndDayOfMonthLocal;
8727 1 : EndDayOfMonthLocal = state.dataWeather->EndDayOfMonth;
8728 1 : if (epwHasLeapYear) {
8729 : // increase number of days for february by one day if weather data has leap year
8730 0 : EndDayOfMonthLocal(2) = EndDayOfMonthLocal(2) + 1;
8731 : }
8732 13 : for (int i = 1; i <= 12; ++i) {
8733 12 : Real64 MonthlyDailyDryBulbAvg = 0.0;
8734 12 : int DaysCountOfMonth = EndDayOfMonthLocal(i);
8735 377 : for (int DayNum = 1; DayNum <= DaysCountOfMonth; ++DayNum) {
8736 365 : Real64 DailyAverageDryBulbTemp = 0.0;
8737 : std::string::size_type pos;
8738 9125 : for (int j = 1; j <= 24; ++j) {
8739 8760 : auto epwLine = epwFile.readLine();
8740 61320 : for (int ind = 1; ind <= 6; ++ind) {
8741 52560 : pos = index(epwLine.data, ',');
8742 52560 : epwLine.data.erase(0, pos + 1);
8743 : }
8744 8760 : pos = index(epwLine.data, ',');
8745 8760 : Real64 HourlyDryBulbTemp = OutputReportTabular::StrToReal(epwLine.data.substr(0, pos));
8746 8760 : DailyAverageDryBulbTemp += (HourlyDryBulbTemp / 24.0);
8747 8760 : }
8748 365 : AnnualDailyAverageDryBulbTempSum += DailyAverageDryBulbTemp;
8749 365 : MonthlyDailyDryBulbAvg += (DailyAverageDryBulbTemp / DaysCountOfMonth);
8750 : }
8751 12 : MonthlyAverageDryBulbTemp(i) = MonthlyDailyDryBulbAvg;
8752 12 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyDailyDryBulbAvg);
8753 12 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyDailyDryBulbAvg);
8754 : }
8755 : // calculate annual average outdoor air dry-bulb temperature and monthly daily average
8756 : // outdoor air temperature maximum difference
8757 1 : int AnnualNumberOfDays = 365;
8758 1 : if (epwHasLeapYear) {
8759 0 : AnnualNumberOfDays++;
8760 : }
8761 1 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8762 1 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8763 1 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8764 1 : this->OADryBulbWeatherDataProcessed = true;
8765 1 : } else {
8766 0 : ShowSevereError(state, "CalcAnnualAndMonthlyDryBulbTemp: weather file or stat file does not exist.");
8767 0 : ShowContinueError(state, format("Weather file: {}.", state.files.inputWeatherFilePath.filePath));
8768 0 : ShowContinueError(state, format("Stat file: {}.", state.files.inStatFilePath.filePath));
8769 0 : ShowContinueError(state, "Water Mains Monthly Temperature cannot be calculated using CorrelationFromWeatherFile method.");
8770 0 : ShowContinueError(state, "Instead a fixed default value of 10.0 C will be used.");
8771 : }
8772 : }
8773 3 : }
8774 :
8775 104 : void ReportWaterMainsTempParameters(EnergyPlusData &state)
8776 : {
8777 : // PURPOSE OF THIS SUBROUTINE:
8778 : // report site water mains temperature object user inputs and/or parameters calculated
8779 : // from weather or stat file
8780 :
8781 104 : if (!state.files.eio.good()) {
8782 0 : return;
8783 : }
8784 :
8785 104 : std::stringstream ss;
8786 104 : auto *eiostream = &ss;
8787 :
8788 : // Write annual average OA temperature and maximum difference in monthly-daily average outdoor air temperature
8789 : *eiostream << "! <Site Water Mains Temperature Information>"
8790 : ",Calculation Method{}"
8791 : ",Water Mains Temperature Schedule Name{}"
8792 : ",Annual Average Outdoor Air Temperature{C}"
8793 : ",Maximum Difference In Monthly Average Outdoor Air Temperatures{deltaC}"
8794 104 : ",Fixed Default Water Mains Temperature{C}\n";
8795 :
8796 104 : switch (state.dataWeather->WaterMainsTempsMethod) {
8797 0 : case WaterMainsTempCalcMethod::Schedule:
8798 0 : *eiostream << "Site Water Mains Temperature Information,";
8799 0 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8800 0 : << state.dataWeather->waterMainsTempSched->Name << ",";
8801 0 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8802 0 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8803 0 : *eiostream << "NA\n";
8804 0 : break;
8805 6 : case WaterMainsTempCalcMethod::Correlation:
8806 6 : *eiostream << "Site Water Mains Temperature Information,";
8807 6 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << "," << "NA" << ",";
8808 6 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8809 12 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8810 6 : *eiostream << "NA\n";
8811 6 : break;
8812 1 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
8813 1 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
8814 1 : *eiostream << "Site Water Mains Temperature Information,";
8815 1 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << "," << "NA" << ",";
8816 2 : *eiostream << format("{:.2R}", state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp) << ","
8817 2 : << format("{:.2R}", state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff) << "," << "NA\n";
8818 : } else {
8819 0 : *eiostream << "Site Water Mains Temperature Information,";
8820 0 : *eiostream << "FixedDefault" << "," << "NA" << "," << "NA" << "," << "NA" << "," << format("{:.1R}", 10.0) << '\n';
8821 : }
8822 1 : break;
8823 97 : default:
8824 97 : *eiostream << "Site Water Mains Temperature Information,";
8825 97 : *eiostream << "FixedDefault" << "," << "NA" << "," << "NA" << "," << "NA" << "," << format("{:.1R}", 10.0) << '\n';
8826 97 : break;
8827 : }
8828 :
8829 104 : print(state.files.eio, "{}", ss.str());
8830 104 : }
8831 :
8832 90504 : void calcSky(EnergyPlusData &state,
8833 : Real64 &HorizIRSky,
8834 : Real64 &SkyTemp,
8835 : Real64 OpaqueSkyCover,
8836 : Real64 DryBulb,
8837 : Real64 DewPoint,
8838 : Real64 RelHum,
8839 : Real64 IRHoriz)
8840 : {
8841 90504 : if (IRHoriz <= 0.0) {
8842 0 : IRHoriz = 9999.0;
8843 : }
8844 :
8845 90504 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8846 90504 : if (!envCurr.UseWeatherFileHorizontalIR || IRHoriz >= 9999.0) {
8847 : // Missing or user defined to not use IRHoriz from weather, using sky cover and clear sky emissivity
8848 384 : Real64 ESky = CalcSkyEmissivity(state, envCurr.skyTempModel, OpaqueSkyCover, DryBulb, DewPoint, RelHum);
8849 384 : HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
8850 384 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8851 384 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8852 384 : SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
8853 : } else {
8854 0 : SkyTemp = 0.0; // dealt with later
8855 : }
8856 384 : } else {
8857 : // Valid IR from weather files
8858 90120 : HorizIRSky = IRHoriz;
8859 90120 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8860 90120 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8861 90120 : SkyTemp = root_4(IRHoriz / Constant::StefanBoltzmann) - Constant::Kelvin;
8862 : } else {
8863 0 : SkyTemp = 0.0; // dealt with later
8864 : }
8865 : }
8866 90504 : }
8867 :
8868 : } // namespace Weather
8869 :
8870 : } // namespace EnergyPlus
|