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) break;
215 : }
216 67 : if (errorsFound) {
217 0 : ShowFatalError(state, "Previous input problems cause program termination");
218 : }
219 67 : return (Num > 0);
220 : }
221 :
222 : Real64
223 5 : calculateWaterBoundaryConvectionCoefficient(Real64 const curWaterTemp, Real64 const freeStreamVelocity, Real64 const distanceFromLeadingEdge)
224 : {
225 5 : Real64 constexpr waterKinematicViscosity = 1e-6; // m2/s
226 5 : Real64 constexpr waterPrandtlNumber = 6; // -
227 5 : Real64 constexpr waterThermalConductivity = 0.6; // W/mK
228 : // do some calculation for forced convection from the leading edge of the ship
229 5 : Real64 const localReynoldsNumber = freeStreamVelocity * distanceFromLeadingEdge / waterKinematicViscosity;
230 5 : Real64 const localNusseltNumber = 0.0296 * pow(localReynoldsNumber, 0.8) * pow(waterPrandtlNumber, 1.0 / 3.0);
231 5 : Real64 const localConvectionCoeff = localNusseltNumber * waterThermalConductivity / distanceFromLeadingEdge;
232 :
233 : // do some calculations for natural convection from the bottom of the ship
234 5 : Real64 constexpr distanceFromBottomOfHull = 12; // meters, assumed for now
235 : // this Prandtl correction is from Incropera & Dewitt, Intro to HT, eq 9.20
236 5 : Real64 const prandtlCorrection =
237 : (0.75 * pow(waterPrandtlNumber, 0.5)) / pow(0.609 + 1.221 * pow(waterPrandtlNumber, 0.5) + 1.238 * waterPrandtlNumber, 0.25);
238 : // calculate the Grashof number
239 5 : Real64 constexpr gravity = 9.81; // m/s2
240 5 : Real64 constexpr beta = 0.000214; // water thermal expansion coefficient, from engineeringtoolbox.com, 1/C
241 5 : Real64 constexpr assumedSurfaceTemp = 25; // Grashof requires a surface temp, this should suffice
242 : Real64 const localGrashofNumber =
243 5 : (gravity * beta * std::abs(assumedSurfaceTemp - curWaterTemp) * pow(distanceFromBottomOfHull, 3)) / pow(waterKinematicViscosity, 2);
244 5 : Real64 const localNusseltFreeConvection = pow(localGrashofNumber / 4, 0.25) * prandtlCorrection;
245 5 : Real64 const localConvectionCoeffFreeConv = localNusseltFreeConvection * waterThermalConductivity / distanceFromBottomOfHull;
246 5 : return max(localConvectionCoeff, localConvectionCoeffFreeConv);
247 : }
248 :
249 0 : void UpdateUnderwaterBoundaries(EnergyPlusData &state)
250 : {
251 0 : for (auto &thisBoundary : state.dataWeather->underwaterBoundaries) {
252 0 : Real64 const curWaterTemp = thisBoundary.waterTempSched->getCurrentVal(); // C
253 0 : Real64 freeStreamVelocity = 0;
254 0 : if (thisBoundary.velocitySched != nullptr) {
255 0 : freeStreamVelocity = thisBoundary.velocitySched->getCurrentVal(); // m/s
256 : }
257 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TConv = curWaterTemp;
258 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HConv =
259 0 : Weather::calculateWaterBoundaryConvectionCoefficient(curWaterTemp, freeStreamVelocity, thisBoundary.distanceFromLeadingEdge);
260 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).TRad = curWaterTemp;
261 0 : state.dataSurface->OSCM(thisBoundary.OSCMIndex).HRad = 0.0;
262 : }
263 0 : }
264 :
265 73 : void ReadVariableLocationOrientation(EnergyPlusData &state)
266 : {
267 : static constexpr std::string_view routineName = "ReadVariableLocationOrientation";
268 :
269 73 : int NumAlpha = 0, NumNumber = 0, IOStat = 0;
270 73 : auto const &ipsc = state.dataIPShortCut;
271 :
272 73 : ipsc->cCurrentModuleObject = "Site:VariableLocation";
273 73 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject) == 0) return;
274 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
275 0 : ipsc->cCurrentModuleObject,
276 : 1,
277 0 : ipsc->cAlphaArgs,
278 : NumAlpha,
279 0 : ipsc->rNumericArgs,
280 : NumNumber,
281 : IOStat,
282 0 : ipsc->lNumericFieldBlanks,
283 0 : ipsc->lAlphaFieldBlanks,
284 0 : ipsc->cAlphaFieldNames,
285 0 : ipsc->cNumericFieldNames);
286 :
287 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
288 :
289 0 : if (ipsc->lAlphaFieldBlanks(1)) {
290 0 : } else if ((state.dataEnvrn->varyingLocationLatSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(1))) == nullptr) {
291 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
292 : }
293 :
294 0 : if (ipsc->lAlphaFieldBlanks(2)) {
295 0 : } else if ((state.dataEnvrn->varyingLocationLongSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
296 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
297 : }
298 :
299 0 : if (ipsc->lAlphaFieldBlanks(3)) {
300 0 : } else if ((state.dataEnvrn->varyingOrientationSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
301 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
302 : }
303 : }
304 :
305 0 : void UpdateLocationAndOrientation(EnergyPlusData &state)
306 : {
307 0 : if (state.dataEnvrn->varyingLocationLatSched != nullptr) {
308 0 : state.dataEnvrn->Latitude = state.dataEnvrn->varyingLocationLatSched->getCurrentVal();
309 : }
310 0 : if (state.dataEnvrn->varyingLocationLongSched != nullptr) {
311 0 : state.dataEnvrn->Longitude = state.dataEnvrn->varyingLocationLongSched->getCurrentVal();
312 : }
313 :
314 0 : CheckLocationValidity(state);
315 0 : if (state.dataEnvrn->varyingOrientationSched != nullptr) {
316 0 : state.dataHeatBal->BuildingAzimuth = mod(state.dataEnvrn->varyingOrientationSched->getCurrentVal(), 360.0);
317 0 : state.dataSurfaceGeometry->CosBldgRelNorth =
318 0 : std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
319 0 : state.dataSurfaceGeometry->SinBldgRelNorth =
320 0 : std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
321 0 : for (size_t SurfNum = 1; SurfNum < state.dataSurface->Surface.size(); ++SurfNum) {
322 0 : auto &surf = state.dataSurface->Surface(SurfNum);
323 0 : for (int n = 1; n <= surf.Sides; ++n) {
324 0 : Real64 Xb = surf.Vertex(n).x;
325 0 : Real64 Yb = surf.Vertex(n).y;
326 0 : surf.NewVertex(n).x = Xb * state.dataSurfaceGeometry->CosBldgRelNorth - Yb * state.dataSurfaceGeometry->SinBldgRelNorth;
327 0 : surf.NewVertex(n).y = Xb * state.dataSurfaceGeometry->SinBldgRelNorth + Yb * state.dataSurfaceGeometry->CosBldgRelNorth;
328 0 : surf.NewVertex(n).z = surf.Vertex(n).z;
329 : }
330 0 : Vectors::CreateNewellSurfaceNormalVector(surf.NewVertex, surf.Sides, surf.NewellSurfaceNormalVector);
331 0 : Real64 SurfWorldAz = 0.0;
332 0 : Real64 SurfTilt = 0.0;
333 0 : Vectors::DetermineAzimuthAndTilt(
334 0 : surf.NewVertex, SurfWorldAz, SurfTilt, surf.lcsx, surf.lcsy, surf.lcsz, surf.NewellSurfaceNormalVector);
335 0 : surf.Azimuth = SurfWorldAz;
336 0 : surf.SinAzim = std::sin(SurfWorldAz * Constant::DegToRad);
337 0 : surf.CosAzim = std::cos(SurfWorldAz * Constant::DegToRad);
338 0 : surf.OutNormVec = surf.NewellSurfaceNormalVector;
339 : }
340 : }
341 0 : }
342 :
343 851 : bool GetNextEnvironment(EnergyPlusData &state, bool &Available, bool &ErrorsFound)
344 : {
345 :
346 : // SUBROUTINE INFORMATION:
347 : // AUTHOR Linda Lawrie
348 : // DATE WRITTEN August 2000
349 :
350 : // PURPOSE OF THIS SUBROUTINE:
351 : // This subroutine is called from the outer simulation manager and determines
352 : // if another environment is available in the "run list" or if the end has been
353 : // reached.
354 :
355 : static constexpr std::string_view RoutineName("GetNextEnvironment: ");
356 : static constexpr std::string_view EnvNameFormat("Environment,{},{},{},{},{},{},{},{},{},{},{},{},{}\n");
357 : static constexpr std::string_view EnvDSTNFormat("Environment:Daylight Saving,No,{}\n");
358 : static constexpr std::string_view EnvDSTYFormat("Environment:Daylight Saving,Yes,{},{},{}\n");
359 : static constexpr std::string_view DateFormat("{:02}/{:02}");
360 : static constexpr std::string_view DateFormatWithYear("{:02}/{:02}/{:04}");
361 :
362 851 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->GetEnvironmentFirstCall) {
363 :
364 107 : state.dataReportFlag->PrintEndDataDictionary = true;
365 :
366 107 : ReportOutputFileHeaders(state); // Write the output file header information
367 :
368 : // Setup Output Variables, CurrentModuleObject='All Simulations'
369 :
370 428 : SetupOutputVariable(state,
371 : "Site Outdoor Air Drybulb Temperature",
372 : Constant::Units::C,
373 107 : state.dataEnvrn->OutDryBulbTemp,
374 : OutputProcessor::TimeStepType::Zone,
375 : OutputProcessor::StoreType::Average,
376 : "Environment");
377 428 : SetupOutputVariable(state,
378 : "Site Outdoor Air Dewpoint Temperature",
379 : Constant::Units::C,
380 107 : state.dataEnvrn->OutDewPointTemp,
381 : OutputProcessor::TimeStepType::Zone,
382 : OutputProcessor::StoreType::Average,
383 : "Environment");
384 428 : SetupOutputVariable(state,
385 : "Site Outdoor Air Wetbulb Temperature",
386 : Constant::Units::C,
387 107 : state.dataEnvrn->OutWetBulbTemp,
388 : OutputProcessor::TimeStepType::Zone,
389 : OutputProcessor::StoreType::Average,
390 : "Environment");
391 428 : SetupOutputVariable(state,
392 : "Site Outdoor Air Humidity Ratio",
393 : Constant::Units::kgWater_kgDryAir,
394 107 : state.dataEnvrn->OutHumRat,
395 : OutputProcessor::TimeStepType::Zone,
396 : OutputProcessor::StoreType::Average,
397 : "Environment");
398 428 : SetupOutputVariable(state,
399 : "Site Outdoor Air Relative Humidity",
400 : Constant::Units::Perc,
401 107 : state.dataEnvrn->OutRelHum,
402 : OutputProcessor::TimeStepType::Zone,
403 : OutputProcessor::StoreType::Average,
404 : "Environment");
405 428 : SetupOutputVariable(state,
406 : "Site Outdoor Air Barometric Pressure",
407 : Constant::Units::Pa,
408 107 : state.dataEnvrn->OutBaroPress,
409 : OutputProcessor::TimeStepType::Zone,
410 : OutputProcessor::StoreType::Average,
411 : "Environment");
412 428 : SetupOutputVariable(state,
413 : "Site Wind Speed",
414 : Constant::Units::m_s,
415 107 : state.dataEnvrn->WindSpeed,
416 : OutputProcessor::TimeStepType::Zone,
417 : OutputProcessor::StoreType::Average,
418 : "Environment");
419 428 : SetupOutputVariable(state,
420 : "Site Wind Direction",
421 : Constant::Units::deg,
422 107 : state.dataEnvrn->WindDir,
423 : OutputProcessor::TimeStepType::Zone,
424 : OutputProcessor::StoreType::Average,
425 : "Environment");
426 428 : SetupOutputVariable(state,
427 : "Site Sky Temperature",
428 : Constant::Units::C,
429 107 : state.dataEnvrn->SkyTemp,
430 : OutputProcessor::TimeStepType::Zone,
431 : OutputProcessor::StoreType::Average,
432 : "Environment");
433 428 : SetupOutputVariable(state,
434 : "Site Horizontal Infrared Radiation Rate per Area",
435 : Constant::Units::W_m2,
436 107 : state.dataWeather->HorizIRSky,
437 : OutputProcessor::TimeStepType::Zone,
438 : OutputProcessor::StoreType::Average,
439 : "Environment");
440 428 : SetupOutputVariable(state,
441 : "Site Diffuse Solar Radiation Rate per Area",
442 : Constant::Units::W_m2,
443 107 : state.dataEnvrn->DifSolarRad,
444 : OutputProcessor::TimeStepType::Zone,
445 : OutputProcessor::StoreType::Average,
446 : "Environment");
447 428 : SetupOutputVariable(state,
448 : "Site Direct Solar Radiation Rate per Area",
449 : Constant::Units::W_m2,
450 107 : state.dataEnvrn->BeamSolarRad,
451 : OutputProcessor::TimeStepType::Zone,
452 : OutputProcessor::StoreType::Average,
453 : "Environment");
454 428 : SetupOutputVariable(state,
455 : "Liquid Precipitation Depth",
456 : Constant::Units::m,
457 107 : state.dataEnvrn->LiquidPrecipitation,
458 : OutputProcessor::TimeStepType::Zone,
459 : OutputProcessor::StoreType::Sum,
460 : "Environment");
461 428 : SetupOutputVariable(state,
462 : "Site Precipitation Rate",
463 : Constant::Units::m_s,
464 107 : state.dataWaterData->RainFall.CurrentRate,
465 : OutputProcessor::TimeStepType::Zone,
466 : OutputProcessor::StoreType::Average,
467 : "Environment");
468 428 : SetupOutputVariable(state,
469 : "Site Precipitation Depth",
470 : Constant::Units::m,
471 107 : state.dataWaterData->RainFall.CurrentAmount,
472 : OutputProcessor::TimeStepType::Zone,
473 : OutputProcessor::StoreType::Sum,
474 : "Environment");
475 428 : SetupOutputVariable(state,
476 : "Site Ground Reflected Solar Radiation Rate per Area",
477 : Constant::Units::W_m2,
478 107 : state.dataEnvrn->GndSolarRad,
479 : OutputProcessor::TimeStepType::Zone,
480 : OutputProcessor::StoreType::Average,
481 : "Environment");
482 428 : SetupOutputVariable(state,
483 : "Site Ground Temperature",
484 : Constant::Units::C,
485 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface],
486 : OutputProcessor::TimeStepType::Zone,
487 : OutputProcessor::StoreType::Average,
488 : "Environment");
489 428 : SetupOutputVariable(state,
490 : "Site Surface Ground Temperature",
491 : Constant::Units::C,
492 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow],
493 : OutputProcessor::TimeStepType::Zone,
494 : OutputProcessor::StoreType::Average,
495 : "Environment");
496 428 : SetupOutputVariable(state,
497 : "Site Deep Ground Temperature",
498 : Constant::Units::C,
499 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep],
500 : OutputProcessor::TimeStepType::Zone,
501 : OutputProcessor::StoreType::Average,
502 : "Environment");
503 428 : SetupOutputVariable(state,
504 : "Site Simple Factor Model Ground Temperature",
505 : Constant::Units::C,
506 107 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod],
507 : OutputProcessor::TimeStepType::Zone,
508 : OutputProcessor::StoreType::Average,
509 : "Environment");
510 428 : SetupOutputVariable(state,
511 : "Site Total Sky Cover",
512 : Constant::Units::None,
513 107 : state.dataEnvrn->TotalCloudCover,
514 : OutputProcessor::TimeStepType::Zone,
515 : OutputProcessor::StoreType::Average,
516 : "Environment");
517 428 : SetupOutputVariable(state,
518 : "Site Opaque Sky Cover",
519 : Constant::Units::None,
520 107 : state.dataEnvrn->OpaqueCloudCover,
521 : OutputProcessor::TimeStepType::Zone,
522 : OutputProcessor::StoreType::Average,
523 : "Environment");
524 428 : SetupOutputVariable(state,
525 : "Site Outdoor Air Enthalpy",
526 : Constant::Units::J_kg,
527 107 : state.dataEnvrn->OutEnthalpy,
528 : OutputProcessor::TimeStepType::Zone,
529 : OutputProcessor::StoreType::Average,
530 : "Environment");
531 428 : SetupOutputVariable(state,
532 : "Site Outdoor Air Density",
533 : Constant::Units::kg_m3,
534 107 : state.dataEnvrn->OutAirDensity,
535 : OutputProcessor::TimeStepType::Zone,
536 : OutputProcessor::StoreType::Average,
537 : "Environment");
538 428 : SetupOutputVariable(state,
539 : "Site Solar Azimuth Angle",
540 : Constant::Units::deg,
541 107 : state.dataWeather->SolarAzimuthAngle,
542 : OutputProcessor::TimeStepType::Zone,
543 : OutputProcessor::StoreType::Average,
544 : "Environment");
545 428 : SetupOutputVariable(state,
546 : "Site Solar Altitude Angle",
547 : Constant::Units::deg,
548 107 : state.dataWeather->SolarAltitudeAngle,
549 : OutputProcessor::TimeStepType::Zone,
550 : OutputProcessor::StoreType::Average,
551 : "Environment");
552 428 : SetupOutputVariable(state,
553 : "Site Solar Hour Angle",
554 : Constant::Units::deg,
555 107 : state.dataWeather->HrAngle,
556 : OutputProcessor::TimeStepType::Zone,
557 : OutputProcessor::StoreType::Average,
558 : "Environment");
559 321 : SetupOutputVariable(state,
560 : "Site Rain Status",
561 : Constant::Units::None,
562 107 : state.dataWeather->RptIsRain,
563 : OutputProcessor::TimeStepType::Zone,
564 : OutputProcessor::StoreType::Average,
565 : "Environment");
566 214 : SetupOutputVariable(state,
567 : "Site Snow on Ground Status",
568 : Constant::Units::None,
569 107 : state.dataWeather->RptIsSnow,
570 : OutputProcessor::TimeStepType::Zone,
571 : OutputProcessor::StoreType::Average,
572 : "Environment");
573 428 : SetupOutputVariable(state,
574 : "Site Exterior Horizontal Sky Illuminance",
575 : Constant::Units::lux,
576 107 : state.dataEnvrn->HISKF,
577 : OutputProcessor::TimeStepType::Zone,
578 : OutputProcessor::StoreType::Average,
579 : "Environment");
580 428 : SetupOutputVariable(state,
581 : "Site Exterior Horizontal Beam Illuminance",
582 : Constant::Units::lux,
583 107 : state.dataEnvrn->HISUNF,
584 : OutputProcessor::TimeStepType::Zone,
585 : OutputProcessor::StoreType::Average,
586 : "Environment");
587 428 : SetupOutputVariable(state,
588 : "Site Exterior Beam Normal Illuminance",
589 : Constant::Units::lux,
590 107 : state.dataEnvrn->HISUNFnorm,
591 : OutputProcessor::TimeStepType::Zone,
592 : OutputProcessor::StoreType::Average,
593 : "Environment");
594 428 : SetupOutputVariable(state,
595 : "Site Sky Diffuse Solar Radiation Luminous Efficacy",
596 : Constant::Units::lum_W,
597 107 : state.dataEnvrn->PDIFLW,
598 : OutputProcessor::TimeStepType::Zone,
599 : OutputProcessor::StoreType::Average,
600 : "Environment");
601 428 : SetupOutputVariable(state,
602 : "Site Beam Solar Radiation Luminous Efficacy",
603 : Constant::Units::lum_W,
604 107 : state.dataEnvrn->PDIRLW,
605 : OutputProcessor::TimeStepType::Zone,
606 : OutputProcessor::StoreType::Average,
607 : "Environment");
608 428 : SetupOutputVariable(state,
609 : "Site Daylighting Model Sky Clearness",
610 : Constant::Units::None,
611 107 : state.dataEnvrn->SkyClearness,
612 : OutputProcessor::TimeStepType::Zone,
613 : OutputProcessor::StoreType::Average,
614 : "Environment");
615 428 : SetupOutputVariable(state,
616 : "Site Daylighting Model Sky Brightness",
617 : Constant::Units::None,
618 107 : state.dataEnvrn->SkyBrightness,
619 : OutputProcessor::TimeStepType::Zone,
620 : OutputProcessor::StoreType::Average,
621 : "Environment");
622 321 : SetupOutputVariable(state,
623 : "Site Daylight Saving Time Status",
624 : Constant::Units::None,
625 107 : state.dataEnvrn->DSTIndicator,
626 : OutputProcessor::TimeStepType::Zone,
627 : OutputProcessor::StoreType::Average,
628 : "Environment");
629 214 : SetupOutputVariable(state,
630 : "Site Day Type Index",
631 : Constant::Units::None,
632 107 : state.dataWeather->RptDayType,
633 : OutputProcessor::TimeStepType::Zone,
634 : OutputProcessor::StoreType::Average,
635 : "Environment");
636 428 : SetupOutputVariable(state,
637 : "Site Mains Water Temperature",
638 : Constant::Units::C,
639 107 : state.dataEnvrn->WaterMainsTemp,
640 : OutputProcessor::TimeStepType::Zone,
641 : OutputProcessor::StoreType::Average,
642 : "Environment");
643 :
644 107 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
645 27 : SetupEMSActuator(state,
646 : "Weather Data",
647 : "Environment",
648 : "Outdoor Dry Bulb",
649 : "[C]",
650 27 : state.dataEnvrn->EMSOutDryBulbOverrideOn,
651 27 : state.dataEnvrn->EMSOutDryBulbOverrideValue);
652 27 : SetupEMSActuator(state,
653 : "Weather Data",
654 : "Environment",
655 : "Outdoor Dew Point",
656 : "[C]",
657 27 : state.dataEnvrn->EMSOutDewPointTempOverrideOn,
658 27 : state.dataEnvrn->EMSOutDewPointTempOverrideValue);
659 27 : SetupEMSActuator(state,
660 : "Weather Data",
661 : "Environment",
662 : "Outdoor Relative Humidity",
663 : "[%]",
664 27 : state.dataEnvrn->EMSOutRelHumOverrideOn,
665 27 : state.dataEnvrn->EMSOutRelHumOverrideValue);
666 27 : SetupEMSActuator(state,
667 : "Weather Data",
668 : "Environment",
669 : "Diffuse Solar",
670 : "[W/m2]",
671 27 : state.dataEnvrn->EMSDifSolarRadOverrideOn,
672 27 : state.dataEnvrn->EMSDifSolarRadOverrideValue);
673 27 : SetupEMSActuator(state,
674 : "Weather Data",
675 : "Environment",
676 : "Direct Solar",
677 : "[W/m2]",
678 27 : state.dataEnvrn->EMSBeamSolarRadOverrideOn,
679 27 : state.dataEnvrn->EMSBeamSolarRadOverrideValue);
680 27 : SetupEMSActuator(state,
681 : "Weather Data",
682 : "Environment",
683 : "Wind Speed",
684 : "[m/s]",
685 27 : state.dataEnvrn->EMSWindSpeedOverrideOn,
686 27 : state.dataEnvrn->EMSWindSpeedOverrideValue);
687 27 : SetupEMSActuator(state,
688 : "Weather Data",
689 : "Environment",
690 : "Wind Direction",
691 : "[deg]",
692 27 : state.dataEnvrn->EMSWindDirOverrideOn,
693 27 : state.dataEnvrn->EMSWindDirOverrideValue);
694 : }
695 107 : state.dataWeather->GetEnvironmentFirstCall = false;
696 :
697 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
698 :
699 851 : if (state.dataWeather->GetBranchInputOneTimeFlag) {
700 :
701 111 : SetupInterpolationValues(state);
702 111 : state.dataWeather->TimeStepFraction = 1.0 / double(state.dataGlobal->TimeStepsInHour);
703 111 : state.dataEnvrn->rhoAirSTP = Psychrometrics::PsyRhoAirFnPbTdbW(
704 : state, DataEnvironment::StdPressureSeaLevel, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
705 111 : OpenWeatherFile(state, ErrorsFound); // moved here because of possibility of special days on EPW file
706 111 : CloseWeatherFile(state);
707 111 : ReadUserWeatherInput(state);
708 111 : AllocateWeatherData(state);
709 111 : if (state.dataWeather->NumIntervalsPerHour != 1) {
710 0 : if (state.dataWeather->NumIntervalsPerHour != state.dataGlobal->TimeStepsInHour) {
711 0 : ShowSevereError(
712 : state,
713 0 : format("{}Number of intervals per hour on Weather file does not match specified number of Time Steps Per Hour", RoutineName));
714 0 : ErrorsFound = true;
715 : }
716 : }
717 111 : state.dataWeather->GetBranchInputOneTimeFlag = false;
718 111 : state.dataWeather->Envrn = 0;
719 111 : if (state.dataWeather->NumOfEnvrn > 0) {
720 110 : ResolveLocationInformation(state, ErrorsFound); // Obtain weather related info from input file
721 110 : CheckLocationValidity(state);
722 160 : if ((state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
723 50 : (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
724 50 : CheckWeatherFileValidity(state);
725 : }
726 109 : if (ErrorsFound) {
727 1 : ShowSevereError(state, format("{}No location specified, program will terminate.", RoutineName));
728 : }
729 : } else {
730 1 : ErrorsFound = true;
731 1 : ShowSevereError(state, format("{}No Design Days or Run Period(s) specified, program will terminate.", RoutineName));
732 : }
733 110 : if (state.dataSysVars->DDOnly && state.dataEnvrn->TotDesDays == 0) {
734 1 : ErrorsFound = true;
735 2 : ShowSevereError(
736 : state,
737 2 : format("{}Requested Design Days only (DataSystemVariables::DDOnly) but no Design Days specified, program will terminate.",
738 : RoutineName));
739 : }
740 110 : if (state.dataSysVars->ReverseDD && state.dataEnvrn->TotDesDays == 1) {
741 0 : ErrorsFound = true;
742 0 : ShowSevereError(
743 : state,
744 0 : format(
745 : "{}Requested Reverse Design Days (DataSystemVariables::ReverseDD) but only 1 Design Day specified, program will terminate.",
746 : RoutineName));
747 : }
748 :
749 : // Throw a Fatal now that we have said it'll terminalte
750 110 : if (ErrorsFound) {
751 2 : CloseWeatherFile(state); // will only close if opened.
752 4 : ShowFatalError(state, format("{}Errors found in Weather Data Input. Program terminates.", RoutineName));
753 : }
754 :
755 108 : state.dataEnvrn->CurrentOverallSimDay = 0;
756 108 : state.dataEnvrn->TotalOverallSimDays = 0;
757 108 : state.dataEnvrn->MaxNumberSimYears = 1;
758 336 : for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
759 228 : state.dataEnvrn->TotalOverallSimDays += state.dataWeather->Environment(i).TotalDays;
760 228 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
761 52 : state.dataEnvrn->MaxNumberSimYears = max(state.dataEnvrn->MaxNumberSimYears, state.dataWeather->Environment(i).NumSimYears);
762 : }
763 : }
764 108 : DisplaySimDaysProgress(state, state.dataEnvrn->CurrentOverallSimDay, state.dataEnvrn->TotalOverallSimDays);
765 : }
766 :
767 848 : CloseWeatherFile(state); // will only close if opened.
768 848 : ++state.dataWeather->Envrn;
769 848 : state.dataWeather->DatesShouldBeReset = false;
770 848 : if (state.dataWeather->Envrn > state.dataWeather->NumOfEnvrn) {
771 208 : Available = false;
772 208 : state.dataWeather->Envrn = 0;
773 208 : state.dataEnvrn->CurEnvirNum = 0;
774 : } else {
775 640 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
776 640 : state.dataGlobal->KindOfSim = envCurr.KindOfEnvrn;
777 640 : state.dataEnvrn->DayOfYear = envCurr.StartJDay;
778 640 : state.dataEnvrn->DayOfMonth = envCurr.StartDay;
779 640 : state.dataGlobal->CalendarYear = envCurr.StartYear;
780 640 : state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
781 640 : state.dataEnvrn->Month = envCurr.StartMonth;
782 640 : state.dataGlobal->NumOfDayInEnvrn = envCurr.TotalDays; // Set day loop maximum from DataGlobals
783 :
784 865 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
785 225 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
786 0 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) {
787 0 : if (state.dataGlobal->DoDesDaySim) {
788 0 : ShowWarningError(state, format("{}Adaptive Comfort being reported during design day.", RoutineName));
789 0 : Real64 GrossApproxAvgDryBulb = (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb +
790 0 : (state.dataWeather->DesDayInput(state.dataWeather->Envrn).MaxDryBulb -
791 0 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).DailyDBRange)) /
792 0 : 2.0;
793 0 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55)
794 0 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, false, GrossApproxAvgDryBulb);
795 0 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251)
796 0 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, false, GrossApproxAvgDryBulb);
797 : }
798 : } else {
799 0 : if (state.dataGlobal->DoWeathSim || state.dataGlobal->DoDesDaySim) {
800 0 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55)
801 0 : ThermalComfort::CalcThermalComfortAdaptiveASH55(state, true, true, 0.0);
802 0 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251)
803 0 : ThermalComfort::CalcThermalComfortAdaptiveCEN15251(state, true, true, 0.0);
804 : }
805 : }
806 : }
807 :
808 640 : if (state.dataWeather->Envrn > state.dataEnvrn->TotDesDays && state.dataWeather->WeatherFileExists) {
809 44 : OpenEPlusWeatherFile(state, ErrorsFound, false);
810 : }
811 640 : Available = true;
812 762 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
813 122 : (!state.dataWeather->WeatherFileExists && state.dataGlobal->DoWeathSim)) {
814 0 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
815 0 : ShowSevereError(state, "Weather Simulation requested, but no weather file attached.");
816 0 : ErrorsFound = true;
817 : }
818 0 : if (!state.dataGlobal->DoingHVACSizingSimulations) state.dataWeather->Envrn = 0;
819 0 : Available = false;
820 762 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) &&
821 122 : (!state.dataWeather->WeatherFileExists && !state.dataGlobal->DoWeathSim)) {
822 80 : Available = false;
823 80 : if (!state.dataGlobal->DoingHVACSizingSimulations) state.dataWeather->Envrn = 0;
824 560 : } else if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoingSizing) {
825 0 : Available = false;
826 0 : state.dataWeather->Envrn = 0;
827 : }
828 :
829 640 : if (!ErrorsFound && Available && state.dataWeather->Envrn > 0) {
830 560 : state.dataEnvrn->EnvironmentName = envCurr.Title;
831 560 : state.dataEnvrn->CurEnvirNum = state.dataWeather->Envrn;
832 560 : state.dataEnvrn->RunPeriodStartDayOfWeek = 0;
833 665 : if ((state.dataGlobal->DoDesDaySim && (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather)) ||
834 105 : ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) && state.dataGlobal->DoWeathSim)) {
835 458 : if (state.dataWeather->PrntEnvHeaders && state.dataReportFlag->DoWeatherInitReporting) {
836 : static constexpr std::string_view EnvironFormat(
837 : "! <Environment>,Environment Name,Environment Type, Start Date, End Date, Start DayOfWeek, Duration {#days}, "
838 : "Source:Start DayOfWeek, Use Daylight Saving, Use Holidays, Apply Weekend Holiday Rule, Use Rain Values, Use Snow "
839 : "Values, Sky Temperature Model\n! <Environment:Special Days>, Special Day Name, Special Day Type, Source, Start Date, "
840 : "Duration {#days}\n! "
841 : "<Environment:Daylight Saving>, Daylight Saving Indicator, Source, Start Date, End Date\n! <Environment:WarmupDays>, "
842 : "NumberofWarmupDays");
843 65 : print(state.files.eio, "{}\n", EnvironFormat);
844 65 : state.dataWeather->PrntEnvHeaders = false;
845 : }
846 :
847 458 : std::string StDate;
848 458 : std::string EnDate;
849 913 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) ||
850 455 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodDesign)) {
851 3 : int DSTActStMon = 0;
852 3 : int DSTActStDay = 0;
853 3 : int DSTActEnMon = 0;
854 3 : int DSTActEnDay = 0;
855 3 : std::string kindOfRunPeriod = envCurr.cKindOfEnvrn;
856 3 : state.dataEnvrn->RunPeriodEnvironment = state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather;
857 3 : state.dataEnvrn->CurrentYearIsLeapYear = state.dataWeather->Environment(state.dataWeather->Envrn).IsLeapYear;
858 3 : if (state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears) {
859 0 : state.dataWeather->LeapYearAdd = 1;
860 : } else {
861 3 : state.dataWeather->LeapYearAdd = 0;
862 : }
863 3 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
864 0 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
865 : }
866 3 : state.dataWeather->UseDaylightSaving = envCurr.UseDST;
867 3 : state.dataWeather->UseSpecialDays = envCurr.UseHolidays;
868 3 : state.dataWeather->UseRainValues = envCurr.UseRain;
869 3 : state.dataWeather->UseSnowValues = envCurr.UseSnow;
870 :
871 3 : bool missingLeap(false); // Defer acting on anything found here until after the other range checks (see below)
872 :
873 3 : if (envCurr.ActualWeather && !state.dataWeather->WFAllowsLeapYears) {
874 0 : for (int year = envCurr.StartYear; year <= envCurr.EndYear; year++) {
875 0 : if (!isLeapYear(year)) continue;
876 :
877 0 : ShowSevereError(
878 : state,
879 0 : format("{}Weatherfile does not support leap years but runperiod includes a leap year ({})", RoutineName, year));
880 0 : missingLeap = true;
881 : }
882 : }
883 :
884 3 : bool OkRun = false;
885 :
886 3 : if (envCurr.ActualWeather) {
887 : // Actual weather
888 0 : for (auto const &dataperiod : state.dataWeather->DataPeriods) {
889 0 : int runStartJulian = dataperiod.DataStJDay;
890 0 : int runEndJulian = dataperiod.DataEnJDay;
891 0 : if (!dataperiod.HasYearData) {
892 0 : ShowSevereError(state,
893 0 : format("{}Actual weather runperiod has been entered but weatherfile DATA PERIOD does not have "
894 : "year included in start/end date.",
895 : RoutineName));
896 0 : ShowContinueError(state, "...to match the RunPeriod, the DATA PERIOD should be mm/dd/yyyy for both, or");
897 0 : ShowContinueError(state, "(...set \"Treat Weather as Actual\" to \"No\".)");
898 : }
899 0 : if (!General::BetweenDates(envCurr.StartDate, runStartJulian, runEndJulian)) continue;
900 0 : if (!General::BetweenDates(envCurr.EndDate, runStartJulian, runEndJulian)) continue;
901 0 : OkRun = true;
902 0 : break;
903 : }
904 : } else {
905 : // Typical (or just non-actual) weather
906 3 : for (auto &dataperiod : state.dataWeather->DataPeriods) {
907 : // Since this is not actual weather, there may be issues with this calculation
908 : // Assume the weather data starts the same year as the simulation, so LeapYearAdd is what
909 : // should be used.
910 3 : int runStartOrdinal = General::OrdinalDay(dataperiod.StMon, dataperiod.StDay, state.dataWeather->LeapYearAdd);
911 : // This one is harder, leave as is for now. What about multiple years of data?
912 3 : int runEndOrdinal = General::OrdinalDay(dataperiod.EnMon, dataperiod.EnDay, state.dataWeather->LeapYearAdd);
913 3 : if (runStartOrdinal == 1 && (runEndOrdinal == 366 || runEndOrdinal == 365)) {
914 : // Complete year(s) of weather data, will wrap around
915 3 : OkRun = true;
916 3 : break;
917 : }
918 0 : if (!General::BetweenDates(envCurr.StartJDay, runStartOrdinal, runEndOrdinal)) continue;
919 0 : if (!General::BetweenDates(envCurr.EndJDay, runStartOrdinal, runEndOrdinal)) continue;
920 0 : OkRun = true;
921 : }
922 : }
923 :
924 3 : if (!OkRun) {
925 0 : if (!envCurr.ActualWeather) {
926 0 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
927 0 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
928 0 : ShowSevereError(state,
929 0 : format("{}Runperiod [mm/dd] (Start={},End={}) requested not within Data Period(s) from Weather File",
930 : RoutineName,
931 : StDate,
932 : EnDate));
933 : } else {
934 0 : StDate = format(DateFormatWithYear, envCurr.StartMonth, envCurr.StartDay, envCurr.StartYear);
935 0 : EnDate = format(DateFormatWithYear, envCurr.EndMonth, envCurr.EndDay, envCurr.EndYear);
936 0 : ShowSevereError(
937 : state,
938 0 : format("{}Runperiod [mm/dd/yyyy] (Start={},End={}) requested not within Data Period(s) from Weather File",
939 : RoutineName,
940 : StDate,
941 : EnDate));
942 : }
943 :
944 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
945 0 : StDate = format(DateFormat, dataPeriod1.StMon, dataPeriod1.StDay);
946 0 : EnDate = format(DateFormat, dataPeriod1.EnMon, dataPeriod1.EnDay);
947 0 : if (dataPeriod1.StYear > 0) {
948 0 : StDate += format("/{}", dataPeriod1.StYear);
949 : } else {
950 0 : StDate += "/<noyear>";
951 : }
952 0 : if (dataPeriod1.EnYear > 0) {
953 0 : EnDate += format("/{}", dataPeriod1.EnYear);
954 : } else {
955 0 : EnDate += "/<noyear>";
956 : }
957 0 : if (state.dataWeather->NumDataPeriods == 1) {
958 0 : ShowContinueError(state, format("Weather Data Period (Start={},End={})", StDate, EnDate));
959 : } else {
960 0 : ShowContinueError(state, format("Multiple Weather Data Periods 1st (Start={},End={})", StDate, EnDate));
961 : }
962 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
963 : }
964 :
965 3 : if (missingLeap) {
966 : // Bail out now if we still need to
967 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition.", RoutineName));
968 : }
969 :
970 : // Following builds Environment start/end for ASHRAE 55 warnings
971 3 : StDate = format(DateFormat, envCurr.StartMonth, envCurr.StartDay);
972 3 : EnDate = format(DateFormat, envCurr.EndMonth, envCurr.EndDay);
973 3 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
974 3 : StDate += format("/{}", envCurr.StartYear);
975 3 : EnDate += format("/{}", envCurr.EndYear);
976 : }
977 3 : state.dataEnvrn->EnvironmentStartEnd = StDate + " - " + EnDate;
978 3 : state.dataEnvrn->StartYear = envCurr.StartYear;
979 3 : state.dataEnvrn->EndYear = envCurr.EndYear;
980 :
981 3 : int TWeekDay = (envCurr.DayOfWeek == 0) ? 1 : envCurr.DayOfWeek;
982 3 : auto const &MonWeekDay = envCurr.MonWeekDay;
983 :
984 3 : if (state.dataReportFlag->DoWeatherInitReporting) {
985 0 : std::string_view const AlpUseDST = (envCurr.UseDST) ? "Yes" : "No";
986 0 : std::string_view const AlpUseSpec = (envCurr.UseHolidays) ? "Yes" : "No";
987 0 : std::string_view const ApWkRule = (envCurr.ApplyWeekendRule) ? "Yes" : "No";
988 0 : std::string_view const AlpUseRain = (envCurr.UseRain) ? "Yes" : "No";
989 0 : std::string_view const AlpUseSnow = (envCurr.UseSnow) ? "Yes" : "No";
990 :
991 0 : print(state.files.eio,
992 : EnvNameFormat,
993 0 : envCurr.Title,
994 : kindOfRunPeriod,
995 : StDate,
996 : EnDate,
997 0 : Sched::dayTypeNames[TWeekDay],
998 0 : fmt::to_string(envCurr.TotalDays),
999 : "Use RunPeriod Specified Day",
1000 : AlpUseDST,
1001 : AlpUseSpec,
1002 : ApWkRule,
1003 : AlpUseRain,
1004 : AlpUseSnow,
1005 0 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1006 : }
1007 :
1008 6 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation &&
1009 9 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather && state.dataGlobal->DoWeathSim) &&
1010 3 : (state.dataHeatBal->AdaptiveComfortRequested_ASH55 || state.dataHeatBal->AdaptiveComfortRequested_CEN15251)) {
1011 0 : if (state.dataWeather->WFAllowsLeapYears) {
1012 0 : ShowSevereError(
1013 : state,
1014 0 : format("{}AdaptiveComfort Reporting does not work correctly with leap years in weather files.", RoutineName));
1015 0 : ErrorsFound = true;
1016 : }
1017 0 : if (state.dataWeather->NumDataPeriods != 1) {
1018 0 : ShowSevereError(
1019 : state,
1020 0 : format("{}AdaptiveComfort Reporting does not work correctly with multiple dataperiods in weather files.",
1021 : RoutineName));
1022 0 : ErrorsFound = true;
1023 : }
1024 0 : auto const &dataPeriod1 = state.dataWeather->DataPeriods(1);
1025 0 : if (dataPeriod1.StMon == 1 && dataPeriod1.StDay == 1) {
1026 0 : int RunStJDay = General::OrdinalDay(dataPeriod1.StMon, dataPeriod1.StDay, state.dataWeather->LeapYearAdd);
1027 0 : int RunEnJDay = General::OrdinalDay(dataPeriod1.EnMon, dataPeriod1.EnDay, state.dataWeather->LeapYearAdd);
1028 0 : if (RunEnJDay - RunStJDay + 1 != 365) {
1029 0 : ShowSevereError(state,
1030 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do "
1031 : "not contain 365 days.",
1032 : RoutineName));
1033 0 : ErrorsFound = true;
1034 : }
1035 0 : } else {
1036 0 : ShowSevereError(state,
1037 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that do not "
1038 : "start on 1 January.",
1039 : RoutineName));
1040 0 : ErrorsFound = true;
1041 : }
1042 0 : if (state.dataWeather->NumIntervalsPerHour != 1) {
1043 0 : ShowSevereError(state,
1044 0 : format("{}AdaptiveComfort Reporting does not work correctly with weather files that have "
1045 : "multiple interval records per hour.",
1046 : RoutineName));
1047 0 : ErrorsFound = true;
1048 : }
1049 : } // if
1050 :
1051 : // Only need to set Week days for Run Days
1052 3 : state.dataEnvrn->RunPeriodStartDayOfWeek = TWeekDay;
1053 3 : state.dataWeather->WeekDayTypes = 0;
1054 3 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1055 3 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1056 :
1057 3 : state.dataWeather->curSimDayForEndOfRunPeriod = envCurr.TotalDays;
1058 :
1059 3 : int i = JDay5Start;
1060 : while (true) {
1061 426 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1062 426 : TWeekDay = mod(TWeekDay, 7) + 1;
1063 426 : ++i;
1064 426 : if (i > 366) i = 1;
1065 426 : if (i == JDay5End) break;
1066 : }
1067 :
1068 3 : state.dataWeather->DaylightSavingIsActive =
1069 3 : (state.dataWeather->UseDaylightSaving && state.dataWeather->EPWDaylightSaving) || state.dataWeather->IDFDaylightSaving;
1070 :
1071 3 : envCurr.SetWeekDays = false;
1072 :
1073 3 : if (state.dataWeather->DaylightSavingIsActive) {
1074 0 : SetDSTDateRanges(state, MonWeekDay, state.dataWeather->DSTIndex, DSTActStMon, DSTActStDay, DSTActEnMon, DSTActEnDay);
1075 : }
1076 :
1077 3 : SetSpecialDayDates(state, MonWeekDay);
1078 :
1079 3 : if (envCurr.StartMonth != 1 || envCurr.StartDay != 1) {
1080 1 : state.dataWeather->StartDatesCycleShouldBeReset = true;
1081 1 : state.dataWeather->Jan1DatesShouldBeReset = true;
1082 : }
1083 :
1084 3 : if (envCurr.StartMonth == 1 && envCurr.StartDay == 1) {
1085 2 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1086 2 : state.dataWeather->Jan1DatesShouldBeReset = true;
1087 : }
1088 :
1089 3 : if (envCurr.ActualWeather) {
1090 0 : state.dataWeather->StartDatesCycleShouldBeReset = false;
1091 0 : state.dataWeather->Jan1DatesShouldBeReset = true;
1092 : }
1093 :
1094 : // Report Actual Dates for Daylight Saving and Special Days
1095 3 : if (!state.dataGlobal->KickOffSimulation) {
1096 3 : std::string Source;
1097 3 : if (state.dataWeather->UseDaylightSaving) {
1098 3 : if (state.dataWeather->EPWDaylightSaving) {
1099 0 : Source = "WeatherFile";
1100 : }
1101 : } else {
1102 0 : Source = "RunPeriod Object";
1103 : }
1104 3 : if (state.dataWeather->IDFDaylightSaving) {
1105 0 : Source = "InputFile";
1106 : }
1107 3 : if (state.dataWeather->DaylightSavingIsActive && state.dataReportFlag->DoWeatherInitReporting) {
1108 0 : StDate = format(DateFormat, DSTActStMon, DSTActStDay);
1109 0 : EnDate = format(DateFormat, DSTActEnMon, DSTActEnDay);
1110 0 : print(state.files.eio, EnvDSTYFormat, Source, StDate, EnDate);
1111 3 : } else if (state.dataGlobal->DoOutputReporting) {
1112 0 : print(state.files.eio, EnvDSTNFormat, Source);
1113 : }
1114 3 : for (int k = 1; k <= state.dataWeather->NumSpecialDays; ++k) {
1115 0 : auto &specialDay = state.dataWeather->SpecialDays(k);
1116 : static constexpr std::string_view EnvSpDyFormat("Environment:Special Days,{},{},{},{},{:3}\n");
1117 0 : if (specialDay.WthrFile && state.dataWeather->UseSpecialDays && state.dataReportFlag->DoWeatherInitReporting) {
1118 0 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1119 0 : print(state.files.eio,
1120 : EnvSpDyFormat,
1121 0 : specialDay.Name,
1122 0 : Sched::dayTypeNames[specialDay.DayType],
1123 : "WeatherFile",
1124 : StDate,
1125 0 : specialDay.Duration);
1126 : }
1127 0 : if (!specialDay.WthrFile && state.dataReportFlag->DoWeatherInitReporting) {
1128 0 : StDate = format(DateFormat, specialDay.ActStMon, specialDay.ActStDay);
1129 0 : print(state.files.eio,
1130 : EnvSpDyFormat,
1131 0 : specialDay.Name,
1132 0 : Sched::dayTypeNames[specialDay.DayType],
1133 : "InputFile",
1134 : StDate,
1135 0 : specialDay.Duration);
1136 : }
1137 : }
1138 3 : }
1139 :
1140 460 : } else if (state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay ||
1141 2 : state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay) { // Design Day
1142 453 : auto const &desDayInput = state.dataWeather->DesDayInput(envCurr.DesignDayNum);
1143 453 : state.dataEnvrn->RunPeriodEnvironment = false;
1144 453 : StDate = format(DateFormat, desDayInput.Month, desDayInput.DayOfMonth);
1145 453 : EnDate = StDate;
1146 453 : if (state.dataReportFlag->DoWeatherInitReporting) {
1147 112 : print(state.files.eio,
1148 : EnvNameFormat,
1149 112 : envCurr.Title,
1150 : "SizingPeriod:DesignDay",
1151 : StDate,
1152 : EnDate,
1153 112 : Sched::dayTypeNames[desDayInput.DayType],
1154 : "1",
1155 : "N/A",
1156 : "N/A",
1157 : "N/A",
1158 : "N/A",
1159 : "N/A",
1160 : "N/A",
1161 112 : SkyTempModelNames[(int)envCurr.skyTempModel]);
1162 : }
1163 453 : if (desDayInput.DSTIndicator == 0 && state.dataReportFlag->DoWeatherInitReporting) {
1164 112 : print(state.files.eio, EnvDSTNFormat, "SizingPeriod:DesignDay");
1165 341 : } else if (state.dataReportFlag->DoWeatherInitReporting) {
1166 0 : print(state.files.eio, EnvDSTYFormat, "SizingPeriod:DesignDay", StDate, EnDate);
1167 : }
1168 : }
1169 458 : }
1170 : } // ErrorsFound
1171 : }
1172 :
1173 848 : if (ErrorsFound && !state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
1174 0 : ShowSevereError(state, format("{}Errors found in getting a new environment", RoutineName));
1175 0 : Available = false;
1176 848 : } else if (ErrorsFound) {
1177 0 : Available = false;
1178 : }
1179 848 : return Available && !ErrorsFound;
1180 : }
1181 :
1182 4 : void AddDesignSetToEnvironmentStruct(EnergyPlusData &state, int const HVACSizingIterCount)
1183 : {
1184 4 : int OrigNumOfEnvrn = state.dataWeather->NumOfEnvrn;
1185 :
1186 14 : for (int i = 1; i <= OrigNumOfEnvrn; ++i) {
1187 : // Gotcha: references may no longer be valid after a redimension! Cannot declare reference to Environment(i) here.
1188 10 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1189 8 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1190 8 : auto &envBase = state.dataWeather->Environment(i);
1191 8 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1192 8 : envNew = envBase; // copy over seed data from current array element
1193 8 : envNew.SeedEnvrnNum = i;
1194 8 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeDesignDay;
1195 8 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1196 8 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1197 2 : } else if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
1198 2 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
1199 2 : auto &envBase = state.dataWeather->Environment(i);
1200 2 : auto &envNew = state.dataWeather->Environment(state.dataWeather->NumOfEnvrn);
1201 2 : envNew = envBase; // copy over seed data
1202 2 : envNew.SeedEnvrnNum = i;
1203 2 : envNew.KindOfEnvrn = Constant::KindOfSim::HVACSizeRunPeriodDesign;
1204 2 : envNew.Title = format("{} HVAC Sizing Pass {}", envBase.Title, HVACSizingIterCount);
1205 2 : envNew.HVACSizingIterationNum = HVACSizingIterCount;
1206 : }
1207 : } // for each loop over Environment data strucure
1208 4 : }
1209 :
1210 93 : void SetupWeekDaysByMonth(EnergyPlusData &state, int const StMon, int const StDay, int const StWeekDay, Array1D_int &WeekDays)
1211 : {
1212 :
1213 : // SUBROUTINE INFORMATION:
1214 : // AUTHOR Linda Lawrie
1215 : // DATE WRITTEN August 2000
1216 :
1217 : // PURPOSE OF THIS SUBROUTINE:
1218 : // This subroutine calculates the weekday for each month based on the start date and
1219 : // weekday specified for that date.
1220 :
1221 : // Argument array dimensioning
1222 93 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1223 :
1224 : // Set 1st day of Start Month
1225 93 : int CurWeekDay{StWeekDay};
1226 256 : for (int i = 1; i <= StDay - 1; ++i) {
1227 163 : --CurWeekDay;
1228 163 : if (CurWeekDay == 0) CurWeekDay = 7;
1229 : }
1230 :
1231 93 : WeekDays(StMon) = CurWeekDay;
1232 1080 : for (int i = StMon + 1; i <= 12; ++i) {
1233 :
1234 987 : if (i == 2) {
1235 84 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1236 424 : while (CurWeekDay > 7) {
1237 340 : CurWeekDay -= 7;
1238 : }
1239 84 : WeekDays(i) = CurWeekDay;
1240 903 : } else if (i == 3) {
1241 88 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + state.dataWeather->LeapYearAdd;
1242 440 : while (CurWeekDay > 7) {
1243 352 : CurWeekDay -= 7;
1244 : }
1245 88 : WeekDays(i) = CurWeekDay;
1246 : } else {
1247 815 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1248 4391 : while (CurWeekDay > 7) {
1249 3576 : CurWeekDay -= 7;
1250 : }
1251 815 : WeekDays(i) = CurWeekDay;
1252 : }
1253 : }
1254 :
1255 93 : if (any_eq(WeekDays, 0)) {
1256 : // need to start at StMon and go backwards.
1257 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1258 9 : CurWeekDay = StWeekDay;
1259 146 : for (int i = 1; i <= StDay - 1; ++i) {
1260 137 : --CurWeekDay;
1261 137 : if (CurWeekDay == 0) CurWeekDay = 7;
1262 : }
1263 :
1264 45 : for (int i = StMon - 1; i >= 1; --i) {
1265 :
1266 36 : if (i == 1) {
1267 9 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1268 49 : while (CurWeekDay <= 0) {
1269 40 : CurWeekDay += 7;
1270 : }
1271 9 : WeekDays(i) = CurWeekDay;
1272 27 : } else if (i == 2) {
1273 5 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + state.dataWeather->LeapYearAdd;
1274 25 : while (CurWeekDay <= 0) {
1275 20 : CurWeekDay += 7;
1276 : }
1277 5 : WeekDays(i) = CurWeekDay;
1278 22 : } else if ((i >= 3) && (i <= 12)) {
1279 22 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1280 119 : while (CurWeekDay <= 0) {
1281 97 : CurWeekDay += 7;
1282 : }
1283 22 : WeekDays(i) = CurWeekDay;
1284 : }
1285 : }
1286 : }
1287 93 : }
1288 : #pragma clang diagnostic pop
1289 :
1290 0 : void ResetWeekDaysByMonth(EnergyPlusData &state,
1291 : Array1D_int &WeekDays,
1292 : int const AddLeapYear,
1293 : int const StartMonth,
1294 : int const StartMonthDay,
1295 : int const EndMonth,
1296 : int const EndMonthDay,
1297 : bool const Rollover,
1298 : bool const MidSimReset)
1299 : {
1300 :
1301 : // SUBROUTINE INFORMATION:
1302 : // AUTHOR Linda Lawrie
1303 : // DATE WRITTEN March 2012
1304 :
1305 : // PURPOSE OF THIS SUBROUTINE:
1306 : // This subroutine resets the weekday for each month based on the current weekday
1307 : // and previous weekdays per month.
1308 :
1309 0 : EP_SIZE_CHECK(WeekDays, 12); // NOLINT(misc-static-assert)
1310 :
1311 0 : Array1D_int WeekDaysCopy(12);
1312 : int CurWeekDay;
1313 :
1314 0 : WeekDaysCopy = WeekDays;
1315 0 : if (!MidSimReset) {
1316 0 : if (Rollover) {
1317 0 : if (StartMonth == 1) {
1318 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1319 : } else {
1320 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1321 : }
1322 : } else { // restart at same as before
1323 0 : CurWeekDay = WeekDays(StartMonth);
1324 : }
1325 0 : while (CurWeekDay > 7) {
1326 0 : CurWeekDay -= 7;
1327 : }
1328 :
1329 0 : WeekDays = 0;
1330 0 : WeekDays(StartMonth) = CurWeekDay;
1331 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1332 0 : if (i == 2) {
1333 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1334 0 : while (CurWeekDay > 7) {
1335 0 : CurWeekDay -= 7;
1336 : }
1337 0 : WeekDays(i) = CurWeekDay;
1338 0 : } else if (i == 3) {
1339 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1340 0 : while (CurWeekDay > 7) {
1341 0 : CurWeekDay -= 7;
1342 : }
1343 0 : WeekDays(i) = CurWeekDay;
1344 : } else {
1345 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1346 0 : while (CurWeekDay > 7) {
1347 0 : CurWeekDay -= 7;
1348 : }
1349 0 : WeekDays(i) = CurWeekDay;
1350 : }
1351 : }
1352 :
1353 0 : if (any_eq(WeekDays, 0)) {
1354 : // need to start at StMon and go backwards.
1355 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1356 0 : CurWeekDay = WeekDays(StartMonth);
1357 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1358 0 : --CurWeekDay;
1359 0 : if (CurWeekDay == 0) CurWeekDay = 7;
1360 : }
1361 :
1362 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1363 :
1364 0 : if (i == 1) {
1365 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1366 0 : while (CurWeekDay <= 0) {
1367 0 : CurWeekDay += 7;
1368 : }
1369 0 : WeekDays(i) = CurWeekDay;
1370 0 : } else if (i == 2) {
1371 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1372 0 : while (CurWeekDay <= 0) {
1373 0 : CurWeekDay += 7;
1374 : }
1375 0 : WeekDays(i) = CurWeekDay;
1376 0 : } else if ((i >= 3) && (i <= 12)) {
1377 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1378 0 : while (CurWeekDay <= 0) {
1379 0 : CurWeekDay += 7;
1380 : }
1381 0 : WeekDays(i) = CurWeekDay;
1382 : }
1383 : }
1384 : }
1385 :
1386 : } else {
1387 0 : if (Rollover) {
1388 0 : if (StartMonth == 1) {
1389 0 : CurWeekDay = WeekDays(12) + state.dataWeather->EndDayOfMonth(12) + StartMonthDay - 1;
1390 : } else {
1391 0 : CurWeekDay = WeekDays(EndMonth) + EndMonthDay;
1392 : }
1393 : } else { // restart at same as before
1394 0 : CurWeekDay = WeekDays(StartMonth);
1395 : }
1396 0 : while (CurWeekDay > 7) {
1397 0 : CurWeekDay -= 7;
1398 : }
1399 0 : WeekDays = 0;
1400 0 : if (StartMonth != 1) {
1401 0 : CurWeekDay = WeekDaysCopy(12) + state.dataWeather->EndDayOfMonth(12);
1402 0 : while (CurWeekDay > 7) {
1403 0 : CurWeekDay -= 7;
1404 : }
1405 0 : WeekDays(1) = CurWeekDay;
1406 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1407 0 : while (CurWeekDay > 7) {
1408 0 : CurWeekDay -= 7;
1409 : }
1410 0 : WeekDays(2) = CurWeekDay;
1411 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1412 0 : while (CurWeekDay > 7) {
1413 0 : CurWeekDay -= 7;
1414 : }
1415 0 : WeekDays(3) = CurWeekDay;
1416 0 : for (int i = 4; i <= 12; ++i) {
1417 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1418 0 : while (CurWeekDay > 7) {
1419 0 : CurWeekDay -= 7;
1420 : }
1421 0 : WeekDays(i) = CurWeekDay;
1422 : }
1423 : } else {
1424 0 : WeekDays = 0;
1425 0 : WeekDays(StartMonth) = CurWeekDay;
1426 0 : for (int i = StartMonth + 1; i <= 12; ++i) {
1427 0 : if (i == 2) {
1428 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(1);
1429 0 : while (CurWeekDay > 7) {
1430 0 : CurWeekDay -= 7;
1431 : }
1432 0 : WeekDays(i) = CurWeekDay;
1433 0 : } else if (i == 3) {
1434 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1) + AddLeapYear;
1435 0 : while (CurWeekDay > 7) {
1436 0 : CurWeekDay -= 7;
1437 : }
1438 0 : WeekDays(i) = CurWeekDay;
1439 : } else {
1440 0 : CurWeekDay += state.dataWeather->EndDayOfMonth(i - 1);
1441 0 : while (CurWeekDay > 7) {
1442 0 : CurWeekDay -= 7;
1443 : }
1444 0 : WeekDays(i) = CurWeekDay;
1445 : }
1446 : }
1447 :
1448 0 : if (any_eq(WeekDays, 0)) {
1449 : // need to start at StMon and go backwards.
1450 : // EndDayOfMonth is also "days" in month. (without leapyear day in February)
1451 0 : CurWeekDay = WeekDays(StartMonth);
1452 0 : for (int i = 1; i <= StartMonthDay - 1; ++i) {
1453 0 : --CurWeekDay;
1454 0 : if (CurWeekDay == 0) CurWeekDay = 7;
1455 : }
1456 :
1457 0 : for (int i = StartMonth - 1; i >= 1; --i) {
1458 :
1459 0 : if (i == 1) {
1460 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(1);
1461 0 : while (CurWeekDay <= 0) {
1462 0 : CurWeekDay += 7;
1463 : }
1464 0 : WeekDays(i) = CurWeekDay;
1465 0 : } else if (i == 2) {
1466 0 : CurWeekDay = CurWeekDay - state.dataWeather->EndDayOfMonth(2) + AddLeapYear;
1467 0 : while (CurWeekDay <= 0) {
1468 0 : CurWeekDay += 7;
1469 : }
1470 0 : WeekDays(i) = CurWeekDay;
1471 0 : } else if ((i >= 3) && (i <= 12)) {
1472 0 : CurWeekDay -= state.dataWeather->EndDayOfMonth(i);
1473 0 : while (CurWeekDay <= 0) {
1474 0 : CurWeekDay += 7;
1475 : }
1476 0 : WeekDays(i) = CurWeekDay;
1477 : }
1478 : }
1479 : }
1480 : }
1481 : }
1482 0 : }
1483 :
1484 0 : void SetDSTDateRanges(EnergyPlusData &state,
1485 : Array1D_int const &MonWeekDay, // Weekday of each day 1 of month
1486 : Array1D_int &DSTIdx, // DST Index for each julian day (1:366)
1487 : ObjexxFCL::Optional_int DSTActStMon,
1488 : ObjexxFCL::Optional_int DSTActStDay,
1489 : ObjexxFCL::Optional_int DSTActEnMon,
1490 : ObjexxFCL::Optional_int DSTActEnDay)
1491 : {
1492 :
1493 : // SUBROUTINE INFORMATION:
1494 : // AUTHOR Linda Lawrie
1495 : // DATE WRITTEN March 2012
1496 :
1497 : // PURPOSE OF THIS SUBROUTINE:
1498 : // With multiple year weather files (or repeating weather files that rollover day),
1499 : // need to set DST (Daylight Saving Time) dates at start of environment or year.
1500 : // DST is only projected for one year.
1501 :
1502 : static constexpr std::string_view RoutineName("SetDSTDateRanges: ");
1503 :
1504 : int ActStartMonth; // Actual Start Month
1505 : int ActStartDay; // Actual Start Day of Month
1506 : int ActEndMonth; // Actual End Month
1507 : int ActEndDay; // Actual End Day of Month
1508 :
1509 0 : bool ErrorsFound = false;
1510 0 : if (state.dataWeather->DST.StDateType == DateType::MonthDay) {
1511 0 : ActStartMonth = state.dataWeather->DST.StMon;
1512 0 : ActStartDay = state.dataWeather->DST.StDay;
1513 0 : } else if (state.dataWeather->DST.StDateType == DateType::NthDayInMonth) {
1514 0 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1515 0 : while (ThisDay <= 0) {
1516 0 : ThisDay += 7;
1517 : }
1518 0 : ThisDay += 7 * (state.dataWeather->DST.StDay - 1);
1519 0 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1520 0 : ShowSevereError(state, format("{}Determining DST: DST Start Date, Nth Day of Month, not enough Nths", RoutineName));
1521 0 : ErrorsFound = true;
1522 : } else {
1523 0 : ActStartMonth = state.dataWeather->DST.StMon;
1524 0 : ActStartDay = ThisDay;
1525 : }
1526 : } else { // LastWeekDayInMonth
1527 0 : int ThisDay = state.dataWeather->DST.StWeekDay - MonWeekDay(state.dataWeather->DST.StMon) + 1;
1528 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.StMon)) {
1529 0 : ThisDay += 7;
1530 : }
1531 0 : ActStartMonth = state.dataWeather->DST.StMon;
1532 0 : ActStartDay = ThisDay;
1533 : }
1534 :
1535 0 : if (state.dataWeather->DST.EnDateType == DateType::MonthDay) {
1536 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1537 0 : ActEndDay = state.dataWeather->DST.EnDay;
1538 0 : } else if (state.dataWeather->DST.EnDateType == DateType::NthDayInMonth) {
1539 0 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1540 0 : while (ThisDay <= 0) {
1541 0 : ThisDay += 7;
1542 : }
1543 0 : ThisDay += 7 * (state.dataWeather->DST.EnDay - 1);
1544 0 : if (ThisDay >> state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1545 0 : ActEndMonth = 0; // Suppress uninitialized warning
1546 0 : ActEndDay = 0; // Suppress uninitialized warning
1547 0 : ShowSevereError(state, format("{}Determining DST: DST End Date, Nth Day of Month, not enough Nths", RoutineName));
1548 0 : ErrorsFound = true;
1549 : } else {
1550 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1551 0 : ActEndDay = ThisDay;
1552 : }
1553 : } else { // LastWeekDayInMonth
1554 0 : int ThisDay = state.dataWeather->DST.EnWeekDay - MonWeekDay(state.dataWeather->DST.EnMon) + 1;
1555 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(state.dataWeather->DST.EnMon)) {
1556 0 : ThisDay += 7;
1557 : }
1558 0 : ActEndMonth = state.dataWeather->DST.EnMon;
1559 0 : ActEndDay = ThisDay;
1560 : }
1561 :
1562 0 : if (ErrorsFound) {
1563 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1564 : }
1565 :
1566 0 : if (present(DSTActStMon)) {
1567 0 : DSTActStMon = ActStartMonth;
1568 0 : DSTActStDay = ActStartDay;
1569 0 : DSTActEnMon = ActEndMonth;
1570 0 : DSTActEnDay = ActEndDay;
1571 : }
1572 :
1573 0 : DSTIdx = 0;
1574 0 : int JDay = General::OrdinalDay(ActStartMonth, ActStartDay, state.dataWeather->LeapYearAdd);
1575 0 : int JDay1 = General::OrdinalDay(ActEndMonth, ActEndDay, state.dataWeather->LeapYearAdd);
1576 0 : if (JDay1 >= JDay) {
1577 0 : DSTIdx({JDay, JDay1}) = 1;
1578 : } else {
1579 0 : DSTIdx({JDay, 366}) = 1;
1580 0 : DSTIdx({1, JDay1}) = 1;
1581 : }
1582 0 : }
1583 :
1584 9 : void SetSpecialDayDates(EnergyPlusData &state, Array1D_int const &MonWeekDay) // Weekday of each day 1 of month
1585 : {
1586 :
1587 : // SUBROUTINE INFORMATION:
1588 : // AUTHOR Linda Lawrie
1589 : // DATE WRITTEN March 2012
1590 :
1591 : // PURPOSE OF THIS SUBROUTINE:
1592 : // With multiple year weather files (or repeating weather files that rollover day),
1593 : // need to set Special Day dates at start of environment or year.
1594 : // Special Days are only projected for one year.
1595 :
1596 : static constexpr std::string_view RoutineName("SetSpecialDayDates: ");
1597 :
1598 : int JDay;
1599 :
1600 9 : bool ErrorsFound = false;
1601 9 : state.dataWeather->SpecialDayTypes = 0;
1602 9 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1603 0 : auto &specialDay = state.dataWeather->SpecialDays(i);
1604 0 : if (specialDay.WthrFile && !state.dataWeather->UseSpecialDays) continue;
1605 0 : if (specialDay.dateType <= DateType::MonthDay) {
1606 0 : JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd);
1607 0 : if (specialDay.Duration == 1 && state.dataWeather->Environment(state.dataWeather->Envrn).ApplyWeekendRule) {
1608 0 : if (state.dataWeather->WeekDayTypes(JDay) == static_cast<int>(Sched::DayType::Sunday)) {
1609 : // Sunday, must go to Monday
1610 0 : ++JDay;
1611 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) JDay = 1;
1612 0 : } else if (state.dataWeather->WeekDayTypes(JDay) == (int)Sched::DayType::Saturday) {
1613 0 : ++JDay;
1614 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) JDay = 1;
1615 0 : ++JDay;
1616 0 : if (JDay == 366 && state.dataWeather->LeapYearAdd == 0) JDay = 1;
1617 : }
1618 : }
1619 0 : General::InvOrdinalDay(JDay, specialDay.ActStMon, specialDay.ActStDay, state.dataWeather->LeapYearAdd);
1620 0 : } else if (specialDay.dateType == DateType::NthDayInMonth) {
1621 0 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1622 0 : if (specialDay.WeekDay < MonWeekDay(specialDay.Month)) {
1623 0 : ThisDay += 7;
1624 : }
1625 0 : ThisDay += 7 * (specialDay.Day - 1);
1626 0 : if (ThisDay > state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1627 0 : ShowSevereError(state,
1628 0 : format("{}Special Day Date, Nth Day of Month, not enough Nths, for SpecialDay={}", RoutineName, specialDay.Name));
1629 0 : ErrorsFound = true;
1630 0 : continue;
1631 : }
1632 0 : specialDay.ActStMon = specialDay.Month;
1633 0 : specialDay.ActStDay = ThisDay;
1634 0 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1635 : } else { // LastWeekDayInMonth
1636 0 : int ThisDay = specialDay.WeekDay - MonWeekDay(specialDay.Month) + 1;
1637 0 : while (ThisDay + 7 <= state.dataWeather->EndDayOfMonthWithLeapDay(specialDay.Month)) {
1638 0 : ThisDay += 7;
1639 : }
1640 0 : specialDay.ActStMon = specialDay.Month;
1641 0 : specialDay.ActStDay = ThisDay;
1642 0 : JDay = General::OrdinalDay(specialDay.Month, ThisDay, state.dataWeather->LeapYearAdd);
1643 : }
1644 0 : if (state.dataWeather->SpecialDayTypes(JDay) != 0) {
1645 0 : ShowWarningError(
1646 : state,
1647 0 : format("{}Special Day definition ({}) is overwriting previously entered special day period", RoutineName, specialDay.Name));
1648 0 : if (state.dataWeather->UseSpecialDays) {
1649 0 : ShowContinueError(state, "...This could be caused by definitions on the Weather File.");
1650 : }
1651 0 : ShowContinueError(state, "...This could be caused by duplicate definitions in the Input File.");
1652 : }
1653 0 : int JDay1 = JDay - 1;
1654 0 : for (int j = 0; j <= specialDay.Duration - 1; ++j) {
1655 0 : ++JDay1;
1656 0 : if (JDay1 == 366 && state.dataWeather->LeapYearAdd == 0) JDay1 = 1;
1657 0 : if (JDay1 == 367) JDay1 = 1;
1658 0 : state.dataWeather->SpecialDayTypes(JDay1) = specialDay.DayType;
1659 : }
1660 : }
1661 :
1662 9 : if (ErrorsFound) {
1663 0 : ShowFatalError(state, format("{}Program terminates due to preceding condition(s).", RoutineName));
1664 : }
1665 9 : }
1666 :
1667 326115 : void InitializeWeather(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
1668 : {
1669 :
1670 : // SUBROUTINE INFORMATION:
1671 : // AUTHOR Rick Strand
1672 : // DATE WRITTEN June 1997
1673 :
1674 : // PURPOSE OF THIS SUBROUTINE:
1675 : // This subroutine is the main driver of the weather initializations.
1676 : // Most of the weather handling can be described as "initializations"
1677 : // so most of the work is done via this subroutine.
1678 :
1679 326115 : if (state.dataGlobal->BeginSimFlag && state.dataWeather->FirstCall) {
1680 :
1681 103 : state.dataWeather->FirstCall = false;
1682 103 : state.dataEnvrn->EndMonthFlag = false;
1683 :
1684 : } // ... end of DataGlobals::BeginSimFlag IF-THEN block.
1685 :
1686 326115 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
1687 326115 : if (state.dataGlobal->BeginEnvrnFlag) {
1688 :
1689 : // Call and setup the Design Day environment
1690 538 : if (envCurr.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) {
1691 518 : if (envCurr.DesignDayNum > 0) {
1692 516 : SetUpDesignDay(state, envCurr.DesignDayNum);
1693 516 : state.dataEnvrn->EnvironmentName = envCurr.Title;
1694 : }
1695 : }
1696 :
1697 : // Only used in Weather file environments
1698 : // Start over missing values with each environment
1699 538 : state.dataWeather->wvarsMissing.OutBaroPress = state.dataEnvrn->StdBaroPress; // Initial "missing" value
1700 538 : state.dataWeather->wvarsMissing.OutDryBulbTemp = 6.0; // Initial "missing" value
1701 538 : state.dataWeather->wvarsMissing.OutDewPointTemp = 3.0; // Initial "missing" value
1702 538 : state.dataWeather->wvarsMissing.OutRelHum = 50.0; // Initial "missing" value
1703 538 : state.dataWeather->wvarsMissing.WindSpeed = 2.5; // Initial "missing" value
1704 538 : state.dataWeather->wvarsMissing.WindDir = 180; // Initial "missing" value
1705 538 : state.dataWeather->wvarsMissing.TotalSkyCover = 5; // Initial "missing" value
1706 538 : state.dataWeather->wvarsMissing.OpaqueSkyCover = 5; // Initial "missing" value
1707 538 : state.dataWeather->wvarsMissing.Visibility = 777.7; // Initial "missing" value
1708 538 : state.dataWeather->wvarsMissing.Ceiling = 77777; // Initial "missing" value
1709 538 : state.dataWeather->wvarsMissing.AerOptDepth = 0.0; // Initial "missing" value
1710 538 : state.dataWeather->wvarsMissing.SnowDepth = 0; // Initial "missing" value
1711 538 : state.dataWeather->wvarsMissing.DaysLastSnow = 88; // Initial "missing" value
1712 538 : state.dataWeather->wvarsMissing.Albedo = 0.0; // Initial "missing" value
1713 538 : state.dataWeather->wvarsMissing.LiquidPrecip = 0.0; // Initial "missing" value
1714 : // Counts set to 0 for each environment
1715 538 : state.dataWeather->wvarsMissedCounts = Weather::WeatherVarCounts();
1716 :
1717 : // Counts set to 0 for each environment
1718 538 : state.dataWeather->wvarsOutOfRangeCounts = Weather::WeatherVarCounts();
1719 :
1720 538 : state.dataWeather->IsRainThreshold = 0.8 / double(state.dataGlobal->TimeStepsInHour); // [mm]
1721 :
1722 538 : if (!state.dataWeather->RPReadAllWeatherData) {
1723 536 : printEnvrnStamp = true; // Set this to true so that on first non-warmup day (only) the environment header will print out
1724 : }
1725 :
1726 538 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
1727 0 : state.dataWeather->SpecialDays(i).Used = false;
1728 : }
1729 :
1730 560 : if ((state.dataGlobal->KindOfSim != Constant::KindOfSim::DesignDay) &&
1731 22 : (state.dataGlobal->KindOfSim != Constant::KindOfSim::HVACSizeDesignDay)) {
1732 22 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read first day's weather
1733 : } else {
1734 516 : state.dataWeather->TomorrowVariables = state.dataWeather->DesignDay(envCurr.DesignDayNum);
1735 : }
1736 :
1737 : } // ... end of DataGlobals::BeginEnvrnFlag IF-THEN block.
1738 :
1739 326115 : if (state.dataGlobal->BeginDayFlag) {
1740 :
1741 : // Check Holidays, Daylight Saving Time, Ground Temperatures, etc.
1742 :
1743 3083 : UpdateWeatherData(state); // Update daily weather info
1744 :
1745 : // Read tomorrow's weather only if necessary. This means that the
1746 : // simulation is out of warmup, is using a weather tape for this
1747 : // environment, and is not on the last day (day after last day is
1748 : // assumed to be equal to last day).
1749 :
1750 : // Following code checks whether the present day of simulation matches the start month and start day.
1751 : // In a multi year simulation with run period less than 365, we need to position the weather line
1752 : // appropriately.
1753 :
1754 4084 : if ((!state.dataGlobal->WarmupFlag) &&
1755 1001 : ((envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) && (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay))) {
1756 730 : if (state.dataGlobal->DayOfSim < state.dataGlobal->NumOfDayInEnvrn) {
1757 728 : if (state.dataGlobal->DayOfSim == state.dataWeather->curSimDayForEndOfRunPeriod) {
1758 1 : state.dataWeather->curSimDayForEndOfRunPeriod += envCurr.RawSimDays;
1759 1 : if (state.dataWeather->StartDatesCycleShouldBeReset) {
1760 0 : ResetWeekDaysByMonth(state,
1761 0 : envCurr.MonWeekDay,
1762 0 : state.dataWeather->LeapYearAdd,
1763 : envCurr.StartMonth,
1764 : envCurr.StartDay,
1765 : envCurr.EndMonth,
1766 : envCurr.EndDay,
1767 0 : envCurr.RollDayTypeOnRepeat);
1768 0 : if (state.dataWeather->DaylightSavingIsActive) {
1769 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1770 : }
1771 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1772 : }
1773 1 : ++state.dataWeather->YearOfSim;
1774 1 : ReadWeatherForDay(state, 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1775 : } else {
1776 727 : ReadWeatherForDay(state, state.dataGlobal->DayOfSim + 1, state.dataWeather->Envrn, false); // Read tomorrow's weather
1777 : }
1778 : }
1779 : }
1780 :
1781 3083 : state.dataEnvrn->EndYearFlag = false;
1782 3083 : if (state.dataEnvrn->DayOfMonth == state.dataWeather->EndDayOfMonthWithLeapDay(state.dataEnvrn->Month)) {
1783 24 : state.dataEnvrn->EndMonthFlag = true;
1784 24 : state.dataEnvrn->EndYearFlag = (state.dataEnvrn->Month == 12);
1785 : }
1786 :
1787 : // Set Tomorrow's date data
1788 3083 : state.dataEnvrn->MonthTomorrow = state.dataWeather->TomorrowVariables.Month;
1789 3083 : state.dataEnvrn->DayOfMonthTomorrow = state.dataWeather->TomorrowVariables.DayOfMonth;
1790 3083 : state.dataEnvrn->DayOfWeekTomorrow = state.dataWeather->TomorrowVariables.DayOfWeek;
1791 3083 : state.dataEnvrn->HolidayIndexTomorrow = state.dataWeather->TomorrowVariables.HolidayIndex;
1792 3083 : state.dataEnvrn->YearTomorrow = state.dataWeather->TomorrowVariables.Year;
1793 :
1794 3083 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1795 20 : if (state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1 && envCurr.ActualWeather) {
1796 0 : if (state.dataWeather->DatesShouldBeReset) {
1797 0 : if (envCurr.TreatYearsAsConsecutive) {
1798 0 : ++envCurr.CurrentYear;
1799 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1800 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1801 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1802 :
1803 : // need to reset MonWeekDay and WeekDayTypes
1804 0 : int JDay5Start = General::OrdinalDay(envCurr.StartMonth, envCurr.StartDay, state.dataWeather->LeapYearAdd);
1805 0 : int JDay5End = General::OrdinalDay(envCurr.EndMonth, envCurr.EndDay, state.dataWeather->LeapYearAdd);
1806 0 : if (!envCurr.ActualWeather)
1807 0 : state.dataWeather->curSimDayForEndOfRunPeriod =
1808 0 : state.dataGlobal->DayOfSim + envCurr.RawSimDays + state.dataWeather->LeapYearAdd - 1;
1809 :
1810 : {
1811 0 : int i = JDay5Start;
1812 0 : int TWeekDay = state.dataEnvrn->DayOfWeek;
1813 : while (true) {
1814 0 : state.dataWeather->WeekDayTypes(i) = TWeekDay;
1815 0 : TWeekDay = mod(TWeekDay, 7) + 1;
1816 0 : ++i;
1817 0 : if (i > 366) i = 1;
1818 0 : if (i == JDay5End) break;
1819 : }
1820 : }
1821 0 : ResetWeekDaysByMonth(state,
1822 0 : envCurr.MonWeekDay,
1823 0 : state.dataWeather->LeapYearAdd,
1824 : envCurr.StartMonth,
1825 : envCurr.StartDay,
1826 : envCurr.EndMonth,
1827 : envCurr.EndDay,
1828 0 : envCurr.RollDayTypeOnRepeat);
1829 0 : if (state.dataWeather->DaylightSavingIsActive) {
1830 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1831 : }
1832 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1833 : }
1834 : }
1835 20 : } else if ((state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1) && state.dataWeather->DatesShouldBeReset &&
1836 0 : (state.dataWeather->Jan1DatesShouldBeReset)) {
1837 0 : if (envCurr.TreatYearsAsConsecutive) {
1838 0 : ++envCurr.CurrentYear;
1839 0 : envCurr.IsLeapYear = isLeapYear(envCurr.CurrentYear);
1840 0 : state.dataEnvrn->CurrentYearIsLeapYear = envCurr.IsLeapYear;
1841 0 : if (state.dataEnvrn->CurrentYearIsLeapYear && !state.dataWeather->WFAllowsLeapYears)
1842 0 : state.dataEnvrn->CurrentYearIsLeapYear = false;
1843 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod && state.dataEnvrn->CurrentYearIsLeapYear)
1844 0 : ++state.dataWeather->curSimDayForEndOfRunPeriod;
1845 : }
1846 :
1847 0 : state.dataWeather->LeapYearAdd = (int)(state.dataEnvrn->CurrentYearIsLeapYear && state.dataWeather->WFAllowsLeapYears);
1848 :
1849 0 : if (state.dataGlobal->DayOfSim < state.dataWeather->curSimDayForEndOfRunPeriod) {
1850 0 : ResetWeekDaysByMonth(state,
1851 0 : envCurr.MonWeekDay,
1852 0 : state.dataWeather->LeapYearAdd,
1853 : envCurr.StartMonth,
1854 : envCurr.StartDay,
1855 : envCurr.EndMonth,
1856 : envCurr.EndDay,
1857 0 : envCurr.RollDayTypeOnRepeat,
1858 0 : envCurr.RollDayTypeOnRepeat || state.dataEnvrn->CurrentYearIsLeapYear);
1859 0 : if (state.dataWeather->DaylightSavingIsActive) {
1860 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
1861 : }
1862 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
1863 : }
1864 : }
1865 : }
1866 :
1867 : // at the end of each day find the min/max weather used for DOAS sizing
1868 3083 : if (state.dataGlobal->AirLoopHVACDOASUsedInSim) {
1869 68 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign || envCurr.KindOfEnvrn == Constant::KindOfSim::DesignDay) {
1870 1700 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
1871 11424 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
1872 9792 : Real64 Tdb = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDryBulbTemp;
1873 9792 : Real64 Tdp = state.dataWeather->wvarsHrTsToday(iTS, iHr).OutDewPointTemp;
1874 9792 : if (Tdb > envCurr.maxCoolingOATSizing) {
1875 84 : envCurr.maxCoolingOATSizing = Tdb;
1876 84 : envCurr.maxCoolingOADPSizing = Tdp;
1877 : }
1878 9792 : if (Tdb < envCurr.minHeatingOATSizing) {
1879 62 : envCurr.minHeatingOATSizing = Tdb;
1880 62 : envCurr.minHeatingOADPSizing = Tdp;
1881 : }
1882 : } // for (iTS)
1883 : } // for (iHr)
1884 : }
1885 : }
1886 :
1887 : } // ... end of DataGlobals::BeginDayFlag IF-THEN block.
1888 :
1889 649147 : if (!state.dataGlobal->BeginDayFlag && !state.dataGlobal->WarmupFlag &&
1890 103763 : (state.dataEnvrn->Month != envCurr.StartMonth || state.dataEnvrn->DayOfMonth != envCurr.StartDay) &&
1891 649147 : !state.dataWeather->DatesShouldBeReset && envCurr.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
1892 0 : state.dataWeather->DatesShouldBeReset = true;
1893 : }
1894 :
1895 326137 : if (state.dataGlobal->EndEnvrnFlag && (envCurr.KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
1896 22 : (envCurr.KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
1897 22 : state.files.inputWeatherFile.rewind();
1898 22 : SkipEPlusWFHeader(state);
1899 22 : ReportMissing_RangeData(state);
1900 : }
1901 :
1902 : // set the EndDesignDayEnvrnsFlag (dataGlobal)
1903 : // 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)
1904 326115 : state.dataGlobal->EndDesignDayEnvrnsFlag = false;
1905 326115 : if (state.dataGlobal->EndEnvrnFlag) {
1906 535 : if (state.dataWeather->Envrn < state.dataWeather->NumOfEnvrn) {
1907 341 : if (envCurr.KindOfEnvrn != state.dataWeather->Environment(state.dataWeather->Envrn + 1).KindOfEnvrn) {
1908 116 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1909 : }
1910 : } else {
1911 : // if the last environment set the flag to true.
1912 194 : state.dataGlobal->EndDesignDayEnvrnsFlag = true;
1913 : }
1914 : }
1915 :
1916 326115 : if (state.dataWeather->WaterMainsParameterReport) {
1917 : // this is done only once
1918 103 : if (state.dataWeather->WaterMainsTempsMethod == WaterMainsTempCalcMethod::CorrelationFromWeatherFile) {
1919 0 : if (!state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
1920 0 : state.dataWeather->OADryBulbAverage.CalcAnnualAndMonthlyDryBulbTemp(state);
1921 : }
1922 : }
1923 : // reports to eio file
1924 103 : ReportWaterMainsTempParameters(state);
1925 103 : state.dataWeather->WaterMainsParameterReport = false;
1926 : }
1927 326115 : }
1928 :
1929 3083 : void UpdateWeatherData(EnergyPlusData &state)
1930 : {
1931 :
1932 : // SUBROUTINE INFORMATION:
1933 : // AUTHOR Rick Strand
1934 : // DATE WRITTEN June 1997
1935 :
1936 : // PURPOSE OF THIS SUBROUTINE:
1937 : // This subroutine updates all of the daily weather data in the local
1938 : // module level variables and the global variables.
1939 : // This subroutine will temporarily transfer the weather data for the
1940 : // current day to the old data structure contained in envdat.inc until
1941 : // enough reengineering has taken place to eliminate the need for this
1942 : // include.
1943 :
1944 3083 : state.dataWeather->TodayVariables = state.dataWeather->TomorrowVariables; // Transfer Tomorrow's Daily Weather Variables to Today
1945 :
1946 3083 : if (state.dataGlobal->BeginEnvrnFlag) {
1947 538 : state.dataGlobal->PreviousHour = 24;
1948 : }
1949 :
1950 3083 : state.dataWeather->wvarsHrTsToday = state.dataWeather->wvarsHrTsTomorrow; // What a waste
1951 :
1952 : // Update Global Data
1953 :
1954 3083 : state.dataEnvrn->DayOfYear = state.dataWeather->TodayVariables.DayOfYear;
1955 3083 : state.dataEnvrn->Year = state.dataWeather->TodayVariables.Year;
1956 3083 : state.dataEnvrn->Month = state.dataWeather->TodayVariables.Month;
1957 3083 : state.dataEnvrn->DayOfMonth = state.dataWeather->TodayVariables.DayOfMonth;
1958 3083 : state.dataEnvrn->DayOfWeek = state.dataWeather->TodayVariables.DayOfWeek;
1959 3083 : state.dataEnvrn->HolidayIndex = state.dataWeather->TodayVariables.HolidayIndex;
1960 3083 : if (state.dataEnvrn->HolidayIndex > 0) {
1961 2333 : state.dataWeather->RptDayType = state.dataEnvrn->HolidayIndex;
1962 : } else {
1963 750 : state.dataWeather->RptDayType = state.dataEnvrn->DayOfWeek;
1964 : }
1965 3083 : state.dataEnvrn->DSTIndicator = state.dataWeather->TodayVariables.DaylightSavingIndex;
1966 3083 : state.dataEnvrn->EquationOfTime = state.dataWeather->TodayVariables.EquationOfTime;
1967 3083 : state.dataEnvrn->CosSolarDeclinAngle = state.dataWeather->TodayVariables.CosSolarDeclinAngle;
1968 3083 : state.dataEnvrn->SinSolarDeclinAngle = state.dataWeather->TodayVariables.SinSolarDeclinAngle;
1969 3083 : }
1970 :
1971 326120 : void SetCurrentWeather(EnergyPlusData &state)
1972 : {
1973 :
1974 : // SUBROUTINE INFORMATION:
1975 : // AUTHOR Russ Taylor
1976 : // DATE WRITTEN March 1990
1977 : // MODIFIED Aug94 (LKL) Fixed improper weighting
1978 : // Nov98 (FCW) Added call to get exterior illuminances
1979 : // Jan02 (FCW) Changed how ground reflectance for daylighting is set
1980 : // Mar12 (LKL) Changed settings for leap years/ current years.
1981 : // RE-ENGINEERED Apr97,May97 (RKS)
1982 :
1983 : // PURPOSE OF THIS SUBROUTINE:
1984 : // The purpose of this subroutine is to interpolate the hourly
1985 : // environment data for the sub-hourly time steps in EnergyPlus. In
1986 : // other words, this subroutine puts the current weather conditions
1987 : // into the proper variables. Rather than using the same data for
1988 : // each time step, environment data is interpolated as a continuum
1989 : // throughout the day.
1990 :
1991 : // METHODOLOGY EMPLOYED:
1992 : // The current hour (DataGlobals::HourOfDay) as well as the next hour are used
1993 : // to come up with environment data per time step interval. Method
1994 : // used is to assign a weighting for the current hour's data and
1995 : // (1-that weighting) to the next hour's data. Actual method is: if
1996 : // the current time step is 15 minutes into hour, the interpolated dry
1997 : // bulb temperature should be 3/4*dry bulb temperature of current hour
1998 : // and 1/4*dry bulb temperature of next environment hourly data. At
1999 : // day boundary (current hour = 24), the next hour is hour 1 of next
2000 : // weather data day (Tomorrow%).
2001 :
2002 : static constexpr std::string_view RoutineName("SetCurrentWeather");
2003 :
2004 326120 : state.dataWeather->NextHour = state.dataGlobal->HourOfDay + 1;
2005 :
2006 326120 : if (state.dataGlobal->HourOfDay == 24) { // Should investigate whether EndDayFlag is always set here and use that instead
2007 13821 : state.dataWeather->NextHour = 1;
2008 : }
2009 :
2010 326120 : if (state.dataGlobal->HourOfDay == 1) { // Should investigate whether DataGlobals::BeginDayFlag is always set here and use that instead
2011 14085 : state.dataEnvrn->DayOfYear_Schedule = General::OrdinalDay(state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, 1);
2012 : }
2013 :
2014 326120 : Sched::UpdateScheduleVals(state);
2015 :
2016 326120 : state.dataEnvrn->CurMnDyHr =
2017 652240 : format("{:02d}/{:02d} {:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, (unsigned short)(state.dataGlobal->HourOfDay - 1));
2018 326120 : state.dataEnvrn->CurMnDy = format("{:02d}/{:02d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth);
2019 326120 : state.dataEnvrn->CurMnDyYr =
2020 652240 : format("{:02d}/{:02d}/{:04d}", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->CalendarYear);
2021 :
2022 326120 : state.dataGlobal->WeightNow = state.dataWeather->Interpolation(state.dataGlobal->TimeStep);
2023 326120 : state.dataGlobal->WeightPreviousHour = 1.0 - state.dataGlobal->WeightNow;
2024 :
2025 326120 : state.dataGlobal->CurrentTime = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * (state.dataWeather->TimeStepFraction);
2026 326120 : state.dataGlobal->SimTimeSteps = (state.dataGlobal->DayOfSim - 1) * 24 * state.dataGlobal->TimeStepsInHour +
2027 326120 : (state.dataGlobal->HourOfDay - 1) * state.dataGlobal->TimeStepsInHour + state.dataGlobal->TimeStep;
2028 :
2029 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] =
2030 326120 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2031 326120 : state.dataEnvrn->GroundTempKelvin = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface] + Constant::Kelvin;
2032 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::FCFactorMethod] =
2033 326120 : state.dataWeather->siteFCFactorMethodGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2034 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow] =
2035 326120 : state.dataWeather->siteShallowGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2036 326120 : state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep] =
2037 326120 : state.dataWeather->siteDeepGroundTempsPtr->getGroundTempAtTimeInMonths(state, 0, state.dataEnvrn->Month);
2038 326120 : state.dataEnvrn->GndReflectance = state.dataWeather->GroundReflectances(state.dataEnvrn->Month);
2039 326120 : state.dataEnvrn->GndReflectanceForDayltg = state.dataEnvrn->GndReflectance;
2040 :
2041 326120 : CalcWaterMainsTemp(state);
2042 :
2043 : // Determine if Sun is up or down, set Solar Cosine values for time step.
2044 326120 : DetermineSunUpDown(state, state.dataEnvrn->SOLCOS);
2045 326120 : if (state.dataEnvrn->SunIsUp && state.dataWeather->SolarAltitudeAngle < 0.0) {
2046 0 : ShowFatalError(state, format("SetCurrentWeather: At {} Sun is Up but Solar Altitude Angle is < 0.0", state.dataEnvrn->CurMnDyHr));
2047 : }
2048 :
2049 326120 : auto const &today = state.dataWeather->wvarsHrTsToday(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2050 326120 : state.dataEnvrn->OutDryBulbTemp = today.OutDryBulbTemp;
2051 326120 : if (state.dataEnvrn->EMSOutDryBulbOverrideOn) state.dataEnvrn->OutDryBulbTemp = state.dataEnvrn->EMSOutDryBulbOverrideValue;
2052 326120 : state.dataEnvrn->OutBaroPress = today.OutBaroPress;
2053 326120 : state.dataEnvrn->OutDewPointTemp = today.OutDewPointTemp;
2054 326120 : if (state.dataEnvrn->EMSOutDewPointTempOverrideOn) state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->EMSOutDewPointTempOverrideValue;
2055 326120 : state.dataEnvrn->OutRelHum = today.OutRelHum;
2056 326120 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->OutRelHum / 100.0;
2057 326120 : if (state.dataEnvrn->EMSOutRelHumOverrideOn) {
2058 2 : state.dataEnvrn->OutRelHumValue = state.dataEnvrn->EMSOutRelHumOverrideValue / 100.0;
2059 2 : state.dataEnvrn->OutRelHum = state.dataEnvrn->EMSOutRelHumOverrideValue;
2060 : }
2061 :
2062 : // Humidity Ratio and Wet Bulb are derived
2063 326120 : state.dataEnvrn->OutHumRat = Psychrometrics::PsyWFnTdbRhPb(
2064 326120 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutRelHumValue, state.dataEnvrn->OutBaroPress, RoutineName);
2065 652240 : state.dataEnvrn->OutWetBulbTemp =
2066 326120 : Psychrometrics::PsyTwbFnTdbWPb(state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat, state.dataEnvrn->OutBaroPress);
2067 326120 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) {
2068 29874 : state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2069 59748 : Real64 TempVal = Psychrometrics::PsyWFnTdbTwbPb(
2070 29874 : state, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutBaroPress);
2071 29874 : state.dataEnvrn->OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, TempVal, state.dataEnvrn->OutBaroPress);
2072 : }
2073 :
2074 326120 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) {
2075 55473 : state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2076 : }
2077 :
2078 396260 : if ((state.dataGlobal->KindOfSim == Constant::KindOfSim::DesignDay) ||
2079 70140 : (state.dataGlobal->KindOfSim == Constant::KindOfSim::HVACSizeDesignDay)) {
2080 :
2081 751458 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2082 495478 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2083 : }
2084 :
2085 255980 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2086 255980 : int const envrnDayNum = envCurr.DesignDayNum;
2087 255980 : auto const &desDayInput = state.dataWeather->DesDayInput(envrnDayNum);
2088 255980 : auto &spSiteSchedule = state.dataWeather->spSiteSchedules(envrnDayNum);
2089 255980 : auto const &desDayMod = state.dataWeather->desDayMods(envrnDayNum)(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
2090 :
2091 255980 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Default) {
2092 0 : spSiteSchedule.OutDryBulbTemp = desDayMod.OutDryBulbTemp;
2093 : }
2094 :
2095 255980 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
2096 255977 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
2097 3 : spSiteSchedule.OutRelHum = desDayMod.OutRelHum;
2098 : }
2099 255980 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
2100 0 : spSiteSchedule.BeamSolarRad = desDayMod.BeamSolarRad;
2101 0 : spSiteSchedule.DifSolarRad = desDayMod.DifSolarRad;
2102 : }
2103 :
2104 255980 : if (envCurr.skyTempModel == SkyTempModel::ScheduleValue || envCurr.skyTempModel == SkyTempModel::DryBulbDelta ||
2105 255980 : envCurr.skyTempModel == SkyTempModel::DewPointDelta) {
2106 0 : spSiteSchedule.SkyTemp = desDayMod.SkyTemp;
2107 : }
2108 70140 : } else if (state.dataEnvrn->TotDesDays > 0) {
2109 105291 : for (int iDD = 1; iDD <= state.dataEnvrn->TotDesDays; ++iDD) {
2110 70194 : state.dataWeather->spSiteSchedules(iDD) = {-999.0, -999.0, -999.0, -999.0, -999.0};
2111 : }
2112 : }
2113 :
2114 326120 : state.dataEnvrn->WindSpeed = today.WindSpeed;
2115 326120 : if (state.dataEnvrn->EMSWindSpeedOverrideOn) state.dataEnvrn->WindSpeed = state.dataEnvrn->EMSWindSpeedOverrideValue;
2116 326120 : state.dataEnvrn->WindDir = today.WindDir;
2117 326120 : if (state.dataEnvrn->EMSWindDirOverrideOn) state.dataEnvrn->WindDir = state.dataEnvrn->EMSWindDirOverrideValue;
2118 326120 : state.dataWeather->HorizIRSky = today.HorizIRSky;
2119 326120 : state.dataEnvrn->SkyTemp = today.SkyTemp;
2120 326120 : state.dataEnvrn->SkyTempKelvin = state.dataEnvrn->SkyTemp + Constant::Kelvin;
2121 326120 : state.dataEnvrn->DifSolarRad = today.DifSolarRad;
2122 326120 : if (state.dataEnvrn->EMSDifSolarRadOverrideOn) state.dataEnvrn->DifSolarRad = state.dataEnvrn->EMSDifSolarRadOverrideValue;
2123 326120 : state.dataEnvrn->BeamSolarRad = today.BeamSolarRad;
2124 326120 : if (state.dataEnvrn->EMSBeamSolarRadOverrideOn) state.dataEnvrn->BeamSolarRad = state.dataEnvrn->EMSBeamSolarRadOverrideValue;
2125 326120 : state.dataEnvrn->LiquidPrecipitation = today.LiquidPrecip / 1000.0; // convert from mm to m
2126 326120 : if ((state.dataEnvrn->RunPeriodEnvironment) && (!state.dataGlobal->WarmupFlag)) {
2127 35042 : int month = state.dataEnvrn->Month;
2128 35042 : state.dataWaterData->RainFall.MonthlyTotalPrecInWeather.at(month - 1) += state.dataEnvrn->LiquidPrecipitation * 1000.0;
2129 35042 : if ((state.dataEnvrn->LiquidPrecipitation > 0) && (state.dataGlobal->TimeStep == 1)) {
2130 43 : state.dataWaterData->RainFall.numRainyHoursInWeather.at(month - 1) += 1;
2131 : }
2132 : }
2133 :
2134 326120 : WaterManager::UpdatePrecipitation(state);
2135 :
2136 326120 : state.dataEnvrn->TotalCloudCover = today.TotalSkyCover;
2137 326120 : state.dataEnvrn->OpaqueCloudCover = today.OpaqueSkyCover;
2138 :
2139 326120 : if (state.dataWeather->UseRainValues) {
2140 : // It is set as LiquidPrecipitation >= .8 mm here: state.dataWeather->TomorrowLiquidPrecip(ts, hour) >=
2141 : // state.dataWeather->IsRainThreshold;
2142 326120 : state.dataEnvrn->IsRain = today.IsRain;
2143 326120 : if (state.dataWaterData->RainFall.ModeID == DataWater::RainfallMode::RainSchedDesign && state.dataEnvrn->RunPeriodEnvironment) {
2144 : // CurrentAmount unit: m
2145 1 : state.dataEnvrn->IsRain = state.dataWaterData->RainFall.CurrentAmount >= (state.dataWeather->IsRainThreshold / 1000.0);
2146 : }
2147 : } else {
2148 0 : state.dataEnvrn->IsRain = false;
2149 : }
2150 326120 : if (state.dataWeather->UseSnowValues) {
2151 326120 : state.dataEnvrn->IsSnow = today.IsSnow;
2152 : } else {
2153 0 : state.dataEnvrn->IsSnow = false;
2154 : }
2155 :
2156 326120 : if (state.dataEnvrn->IsSnow) {
2157 0 : state.dataEnvrn->GndReflectance = max(min(state.dataEnvrn->GndReflectance * state.dataWeather->SnowGndRefModifier, 1.0), 0.0);
2158 0 : state.dataEnvrn->GndReflectanceForDayltg =
2159 0 : max(min(state.dataEnvrn->GndReflectanceForDayltg * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0);
2160 : }
2161 :
2162 326120 : state.dataEnvrn->GndSolarRad =
2163 326120 : max((state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z + state.dataEnvrn->DifSolarRad) * state.dataEnvrn->GndReflectance, 0.0);
2164 :
2165 326120 : if (!state.dataEnvrn->SunIsUp) {
2166 166429 : state.dataEnvrn->DifSolarRad = 0.0;
2167 166429 : state.dataEnvrn->BeamSolarRad = 0.0;
2168 166429 : state.dataEnvrn->GndSolarRad = 0.0;
2169 : }
2170 :
2171 326120 : state.dataEnvrn->OutEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2172 652240 : state.dataEnvrn->OutAirDensity =
2173 326120 : Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
2174 :
2175 326120 : if (state.dataEnvrn->OutDryBulbTemp < state.dataEnvrn->OutWetBulbTemp) state.dataEnvrn->OutWetBulbTemp = state.dataEnvrn->OutDryBulbTemp;
2176 326120 : if (state.dataEnvrn->OutDewPointTemp > state.dataEnvrn->OutWetBulbTemp) state.dataEnvrn->OutDewPointTemp = state.dataEnvrn->OutWetBulbTemp;
2177 :
2178 326120 : DayltgCurrentExtHorizIllum(state);
2179 :
2180 326120 : if (!state.dataEnvrn->IsRain) {
2181 325913 : state.dataWeather->RptIsRain = 0;
2182 : } else {
2183 207 : state.dataWeather->RptIsRain = 1;
2184 : }
2185 :
2186 326120 : if (!state.dataEnvrn->IsSnow) {
2187 326120 : state.dataWeather->RptIsSnow = 0;
2188 : } else {
2189 0 : state.dataWeather->RptIsSnow = 1;
2190 : }
2191 326120 : }
2192 :
2193 755 : void ReadWeatherForDay(EnergyPlusData &state,
2194 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2195 : int const Environ, // Environment being simulated
2196 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2197 : )
2198 : {
2199 :
2200 : // SUBROUTINE INFORMATION:
2201 : // AUTHOR Linda K. Lawrie
2202 : // DATE WRITTEN April 1999
2203 :
2204 : // PURPOSE OF THIS SUBROUTINE:
2205 : // This subroutine is the driving routine behind reading the weather data.
2206 : // Theoretically, several kinds of weather files could be read here. As
2207 : // distributed only EPW files are allowed.
2208 :
2209 755 : ReadEPlusWeatherForDay(state, DayToRead, Environ, BackSpaceAfterRead);
2210 755 : }
2211 :
2212 755 : void ReadEPlusWeatherForDay(EnergyPlusData &state,
2213 : int const DayToRead, // =1 when starting out, otherwise signifies next day
2214 : int const Environ, // Environment being simulated
2215 : bool const BackSpaceAfterRead // True if weather file is to be backspaced after read
2216 : )
2217 : {
2218 :
2219 : // SUBROUTINE INFORMATION:
2220 : // AUTHOR Linda K. Lawrie
2221 : // DATE WRITTEN April 1999
2222 : // MODIFIED March 2012; add actual weather read.
2223 :
2224 : // PURPOSE OF THIS SUBROUTINE:
2225 : // This subroutine reads the appropriate day of EPW weather data.
2226 :
2227 : int WYear;
2228 : int WMonth;
2229 : int WDay;
2230 : int WHour;
2231 : int WMinute;
2232 : Real64 DryBulb;
2233 : Real64 DewPoint;
2234 : Real64 RelHum;
2235 : Real64 AtmPress;
2236 : Real64 ETHoriz;
2237 : Real64 ETDirect;
2238 : Real64 IRHoriz;
2239 : Real64 GLBHoriz;
2240 : Real64 DirectRad;
2241 : Real64 DiffuseRad;
2242 : Real64 GLBHorizIllum;
2243 : Real64 DirectNrmIllum;
2244 : Real64 DiffuseHorizIllum;
2245 : Real64 ZenLum;
2246 : Real64 WindDir;
2247 : Real64 WindSpeed;
2248 : Real64 TotalSkyCover;
2249 : Real64 OpaqueSkyCover;
2250 : Real64 Visibility;
2251 : Real64 CeilHeight;
2252 : Real64 PrecipWater;
2253 : Real64 AerosolOptDepth;
2254 : Real64 SnowDepth;
2255 : Real64 DaysSinceLastSnow;
2256 : Real64 Albedo;
2257 : Real64 LiquidPrecip;
2258 : int PresWeathObs;
2259 755 : Array1D_int PresWeathConds(9);
2260 :
2261 755 : constexpr std::string_view routineName = "ReadEPlusWeatherForDay";
2262 :
2263 755 : Array1D<WeatherVars> wvarsHr = Array1D<WeatherVars>(Constant::iHoursInDay);
2264 :
2265 755 : auto &thisEnviron = state.dataWeather->Environment(Environ);
2266 :
2267 755 : if (DayToRead == 1) {
2268 :
2269 : // Checks whether Weather file contains just one year of data. If yes then rewind and position to first
2270 : // day of weather file. The rest of code appropriately positions to the start day.
2271 :
2272 27 : bool Ready = false;
2273 27 : int NumRewinds = 0;
2274 : // Must position file to proper day
2275 : // File already position to first data record
2276 : // Set Current Day of Week to "start of Data Period"
2277 27 : state.dataWeather->ReadEPlusWeatherCurTime = 1.0 / double(state.dataWeather->NumIntervalsPerHour);
2278 27 : state.dataWeather->CurDayOfWeek = state.dataWeather->DataPeriods(1).WeekDay - 1;
2279 27 : WYear = 0;
2280 27 : WMonth = 0;
2281 27 : WDay = 0;
2282 27 : WHour = 0;
2283 27 : WMinute = 0;
2284 27 : state.dataWeather->LastHourSet = false;
2285 27 : InputFile::ReadResult<std::string> WeatherDataLine{"", true, false};
2286 752 : while (!Ready) {
2287 725 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2288 725 : if (WeatherDataLine.good) {
2289 : bool ErrorFound;
2290 724 : InterpretWeatherDataLine(state,
2291 : WeatherDataLine.data,
2292 : ErrorFound,
2293 : WYear,
2294 : WMonth,
2295 : WDay,
2296 : WHour,
2297 : WMinute,
2298 : DryBulb,
2299 : DewPoint,
2300 : RelHum,
2301 : AtmPress,
2302 : ETHoriz,
2303 : ETDirect,
2304 : IRHoriz,
2305 : GLBHoriz,
2306 : DirectRad,
2307 : DiffuseRad,
2308 : GLBHorizIllum,
2309 : DirectNrmIllum,
2310 : DiffuseHorizIllum,
2311 : ZenLum,
2312 : WindDir,
2313 : WindSpeed,
2314 : TotalSkyCover,
2315 : OpaqueSkyCover,
2316 : Visibility,
2317 : CeilHeight,
2318 : PresWeathObs,
2319 : PresWeathConds,
2320 : PrecipWater,
2321 : AerosolOptDepth,
2322 : SnowDepth,
2323 : DaysSinceLastSnow,
2324 : Albedo,
2325 : LiquidPrecip);
2326 1 : } else if (WeatherDataLine.eof) {
2327 1 : if (NumRewinds > 0) {
2328 0 : std::string date = fmt::to_string(thisEnviron.StartMonth) + '/' + fmt::to_string(thisEnviron.StartDay);
2329 0 : if (thisEnviron.MatchYear) {
2330 0 : date += '/' + fmt::to_string(thisEnviron.StartYear);
2331 : }
2332 0 : ShowSevereError(state, format("Multiple rewinds on EPW while searching for first day {}", date));
2333 0 : } else {
2334 1 : state.files.inputWeatherFile.rewind();
2335 1 : ++NumRewinds;
2336 1 : SkipEPlusWFHeader(state);
2337 1 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2338 : bool ErrorFound;
2339 1 : InterpretWeatherDataLine(state,
2340 : WeatherDataLine.data,
2341 : ErrorFound,
2342 : WYear,
2343 : WMonth,
2344 : WDay,
2345 : WHour,
2346 : WMinute,
2347 : DryBulb,
2348 : DewPoint,
2349 : RelHum,
2350 : AtmPress,
2351 : ETHoriz,
2352 : ETDirect,
2353 : IRHoriz,
2354 : GLBHoriz,
2355 : DirectRad,
2356 : DiffuseRad,
2357 : GLBHorizIllum,
2358 : DirectNrmIllum,
2359 : DiffuseHorizIllum,
2360 : ZenLum,
2361 : WindDir,
2362 : WindSpeed,
2363 : TotalSkyCover,
2364 : OpaqueSkyCover,
2365 : Visibility,
2366 : CeilHeight,
2367 : PresWeathObs,
2368 : PresWeathConds,
2369 : PrecipWater,
2370 : AerosolOptDepth,
2371 : SnowDepth,
2372 : DaysSinceLastSnow,
2373 : Albedo,
2374 : LiquidPrecip);
2375 : }
2376 : }
2377 725 : if (!WeatherDataLine.good) {
2378 0 : ShowFatalError(state,
2379 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2380 : WYear,
2381 : WMonth,
2382 : WDay,
2383 : WHour,
2384 : WMinute,
2385 0 : state.files.inputWeatherFile.error_state_to_string()),
2386 0 : OptionalOutputFileRef{state.files.eso});
2387 : }
2388 725 : if (state.dataWeather->CurDayOfWeek <= 7) {
2389 725 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2390 : }
2391 725 : bool RecordDateMatch =
2392 1423 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && !thisEnviron.MatchYear) ||
2393 698 : (WMonth == thisEnviron.StartMonth && WDay == thisEnviron.StartDay && thisEnviron.MatchYear && WYear == thisEnviron.StartYear);
2394 725 : if (RecordDateMatch) {
2395 27 : state.files.inputWeatherFile.backspace();
2396 27 : Ready = true;
2397 27 : if (state.dataWeather->CurDayOfWeek <= 7) {
2398 27 : --state.dataWeather->CurDayOfWeek;
2399 : }
2400 : // Do the range checks on the first set of fields -- no others.
2401 27 : bool ErrorsFound = false;
2402 27 : if (DryBulb < 99.9 && (DryBulb < -90.0 || DryBulb > 70.0)) {
2403 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2404 0 : ShowContinueError(state, format("DryBulb Temperature ({:.2R}) is out of range [-90.0, 70.0]", DryBulb));
2405 0 : ErrorsFound = true;
2406 : }
2407 :
2408 27 : if (DewPoint < 99.9 && (DewPoint < -90.0 || DewPoint > 70.0)) {
2409 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2410 0 : ShowContinueError(state, format("DewPoint Temperature ({:.2R}) is out of range [-90.0, 70.0]", DewPoint));
2411 0 : ErrorsFound = true;
2412 : }
2413 :
2414 27 : if (RelHum < 999.0 && (RelHum < 0.0 || RelHum > 110.0)) {
2415 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2416 0 : ShowContinueError(state, format("Relative Humidity ({:.2R}) is out of range [0.0, 100.0]", RelHum));
2417 0 : ErrorsFound = true;
2418 : }
2419 :
2420 27 : if (AtmPress < 999999.0 && (AtmPress <= 31000.0 || AtmPress > 120000.0)) {
2421 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2422 0 : ShowContinueError(state, format("Atmospheric Pressure ({:.0R}) is out of range [31000, 120000]", AtmPress));
2423 0 : ErrorsFound = true;
2424 : }
2425 :
2426 27 : if (DirectRad < 0.0) {
2427 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2428 0 : ShowContinueError(state, format("Direct Radiation ({:.2R}) is out of range [0.0, -]", DirectRad));
2429 0 : ErrorsFound = true;
2430 : }
2431 :
2432 27 : if (DiffuseRad < 0.0) {
2433 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2434 0 : ShowContinueError(state, format("Diffuse Radiation ({:.2R}) is out of range [0.0, -]", DiffuseRad));
2435 0 : ErrorsFound = true;
2436 : }
2437 :
2438 27 : if (WindDir < 999.0 && (WindDir < 0.0 || WindDir > 360.0)) {
2439 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2440 0 : ShowContinueError(state, format("Wind Direction ({:.2R}) is out of range [0.0, 360.0]", WindDir));
2441 0 : ErrorsFound = true;
2442 : }
2443 :
2444 27 : if (WindSpeed < 999.0 && (WindSpeed < 0.0 || WindSpeed > 40.0)) {
2445 0 : ShowSevereError(state, format("{}: {}", routineName, state.dataEnvrn->WeatherFileLocationTitle));
2446 0 : ShowContinueError(state, format("Wind Speed ({:.2R}) is out of range [0.0, 40.0]", WindSpeed));
2447 0 : ErrorsFound = true;
2448 : }
2449 :
2450 27 : if (ErrorsFound) {
2451 0 : ShowSevereError(state, "Out of Range errors found with initial day of WeatherFile");
2452 : }
2453 : } else {
2454 : // Must skip this day
2455 698 : for (int i = 2; i <= state.dataWeather->NumIntervalsPerHour; ++i) {
2456 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2457 0 : if (!WeatherDataLine.good) {
2458 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2459 0 : ShowFatalError(state,
2460 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2461 : WYear,
2462 : WMonth,
2463 : WDay,
2464 : WHour,
2465 : WMinute,
2466 0 : state.files.inputWeatherFile.error_state_to_string()),
2467 0 : OptionalOutputFileRef{state.files.eso});
2468 : }
2469 : }
2470 16752 : for (int i = 1; i <= 23 * state.dataWeather->NumIntervalsPerHour; ++i) {
2471 16054 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2472 16054 : if (!WeatherDataLine.good) {
2473 0 : readList(WeatherDataLine.data, WYear, WMonth, WDay, WHour, WMinute);
2474 0 : ShowFatalError(state,
2475 0 : format("Error occurred on EPW while searching for first day, stopped at {}/{}/{} {}:{} IO Error='{}'",
2476 : WYear,
2477 : WMonth,
2478 : WDay,
2479 : WHour,
2480 : WMinute,
2481 0 : state.files.inputWeatherFile.error_state_to_string()),
2482 0 : OptionalOutputFileRef{state.files.eso});
2483 : }
2484 : }
2485 : }
2486 : }
2487 :
2488 27 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
2489 : // Why do some things here use state.dataWeather->Envrn and some the parameter Environ?
2490 :
2491 : // Positioned to proper day
2492 36 : if (!state.dataGlobal->KickOffSimulation && !state.dataGlobal->DoingSizing &&
2493 9 : thisEnviron.KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather) {
2494 6 : ++thisEnviron.CurrentCycle;
2495 6 : if (!thisEnviron.RollDayTypeOnRepeat) {
2496 0 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2497 0 : if (state.dataWeather->DaylightSavingIsActive) {
2498 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2499 : }
2500 0 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2501 6 : } else if (thisEnviron.CurrentCycle == 1) {
2502 6 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2503 6 : thisEnviron.SetWeekDays = true;
2504 6 : if (state.dataWeather->DaylightSavingIsActive) {
2505 0 : SetDSTDateRanges(state, envCurr.MonWeekDay, state.dataWeather->DSTIndex);
2506 : }
2507 6 : SetSpecialDayDates(state, envCurr.MonWeekDay);
2508 : } else {
2509 0 : state.dataWeather->CurDayOfWeek = state.dataEnvrn->DayOfWeekTomorrow;
2510 : }
2511 : } else {
2512 21 : SetDayOfWeekInitialValues(thisEnviron.DayOfWeek, state.dataWeather->CurDayOfWeek);
2513 : }
2514 27 : }
2515 :
2516 755 : bool TryAgain = true;
2517 755 : bool SkipThisDay = false;
2518 :
2519 1510 : while (TryAgain) {
2520 :
2521 755 : TryAgain = false;
2522 :
2523 18875 : for (int hour = 1; hour <= 24; ++hour) {
2524 36240 : for (int CurTimeStep = 1; CurTimeStep <= state.dataWeather->NumIntervalsPerHour; ++CurTimeStep) {
2525 18120 : state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour) = WeatherVars();
2526 18120 : auto WeatherDataLine = state.files.inputWeatherFile.readLine();
2527 18120 : if (!WeatherDataLine.good) {
2528 0 : WeatherDataLine.data.clear();
2529 : }
2530 18120 : if (WeatherDataLine.data.empty()) {
2531 0 : if (hour == 1) {
2532 0 : WeatherDataLine.eof = true;
2533 0 : WeatherDataLine.good = false;
2534 : } else {
2535 0 : WeatherDataLine.good = false;
2536 : }
2537 : }
2538 18120 : if (WeatherDataLine.good) {
2539 : bool ErrorFound;
2540 18120 : InterpretWeatherDataLine(state,
2541 : WeatherDataLine.data,
2542 : ErrorFound,
2543 : WYear,
2544 : WMonth,
2545 : WDay,
2546 : WHour,
2547 : WMinute,
2548 : DryBulb,
2549 : DewPoint,
2550 : RelHum,
2551 : AtmPress,
2552 : ETHoriz,
2553 : ETDirect,
2554 : IRHoriz,
2555 : GLBHoriz,
2556 : DirectRad,
2557 : DiffuseRad,
2558 : GLBHorizIllum,
2559 : DirectNrmIllum,
2560 : DiffuseHorizIllum,
2561 : ZenLum,
2562 : WindDir,
2563 : WindSpeed,
2564 : TotalSkyCover,
2565 : OpaqueSkyCover,
2566 : Visibility,
2567 : CeilHeight,
2568 : PresWeathObs,
2569 : PresWeathConds,
2570 : PrecipWater,
2571 : AerosolOptDepth,
2572 : SnowDepth,
2573 : DaysSinceLastSnow,
2574 : Albedo,
2575 : LiquidPrecip);
2576 : } else { // ReadStatus /=0
2577 0 : if (WeatherDataLine.eof &&
2578 0 : state.dataWeather->NumDataPeriods == 1) { // Standard End-of-file, rewind and position to first day...
2579 0 : if (state.dataWeather->DataPeriods(1).NumDays >= state.dataWeather->NumDaysInYear) {
2580 0 : state.files.inputWeatherFile.rewind();
2581 0 : SkipEPlusWFHeader(state);
2582 0 : WeatherDataLine.update(state.files.inputWeatherFile.readLine());
2583 : bool ErrorFound;
2584 0 : InterpretWeatherDataLine(state,
2585 : WeatherDataLine.data,
2586 : ErrorFound,
2587 : WYear,
2588 : WMonth,
2589 : WDay,
2590 : WHour,
2591 : WMinute,
2592 : DryBulb,
2593 : DewPoint,
2594 : RelHum,
2595 : AtmPress,
2596 : ETHoriz,
2597 : ETDirect,
2598 : IRHoriz,
2599 : GLBHoriz,
2600 : DirectRad,
2601 : DiffuseRad,
2602 : GLBHorizIllum,
2603 : DirectNrmIllum,
2604 : DiffuseHorizIllum,
2605 : ZenLum,
2606 : WindDir,
2607 : WindSpeed,
2608 : TotalSkyCover,
2609 : OpaqueSkyCover,
2610 : Visibility,
2611 : CeilHeight,
2612 : PresWeathObs,
2613 : PresWeathConds,
2614 : PrecipWater,
2615 : AerosolOptDepth,
2616 : SnowDepth,
2617 : DaysSinceLastSnow,
2618 : Albedo,
2619 : LiquidPrecip);
2620 : } else {
2621 0 : ShowFatalError(state,
2622 0 : format("End-of-File encountered after {}/{}/{} {}:{}, starting from first day of Weather File would "
2623 : "not be \"next day\"",
2624 : WYear,
2625 : WMonth,
2626 : WDay,
2627 : WHour,
2628 : WMinute));
2629 : }
2630 : } else {
2631 0 : ShowFatalError(state,
2632 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2633 : WYear,
2634 : WMonth,
2635 : WDay,
2636 : WHour,
2637 : WMinute),
2638 0 : OptionalOutputFileRef{state.files.eso});
2639 : }
2640 : }
2641 :
2642 18120 : if (hour != WHour) {
2643 0 : ShowFatalError(state,
2644 0 : format("Unexpected error condition in middle of reading EPW file, stopped at {}/{}/{} {}:{}",
2645 : WYear,
2646 : WMonth,
2647 : WDay,
2648 : WHour,
2649 : WMinute),
2650 0 : OptionalOutputFileRef{state.files.eso});
2651 : }
2652 :
2653 : // Set possible missing values
2654 18120 : if (ETHoriz < 0.0) ETHoriz = 9999.0;
2655 18120 : if (ETDirect < 0.0) ETDirect = 9999.0;
2656 18120 : if (IRHoriz <= 0.0) IRHoriz = 9999.0;
2657 18120 : if (GLBHoriz < 0.0) GLBHoriz = 9999.0;
2658 18120 : if (state.dataEnvrn->DisplayWeatherMissingDataWarnings) {
2659 0 : if (DirectRad >= 9999.0) {
2660 0 : ++state.dataWeather->wvarsMissedCounts.BeamSolarRad;
2661 : }
2662 0 : if (DiffuseRad >= 9999.0) {
2663 0 : state.dataWeather->wvarsMissedCounts.DifSolarRad = state.dataWeather->wvarsMissedCounts.BeamSolarRad + 1;
2664 : }
2665 0 : if (DirectRad < 0.0) {
2666 0 : DirectRad = 9999.0;
2667 0 : ++state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad;
2668 : }
2669 0 : if (DiffuseRad < 0.0) {
2670 0 : DiffuseRad = 9999.0;
2671 0 : ++state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad;
2672 : }
2673 : }
2674 18120 : if (GLBHorizIllum < 0.0) GLBHorizIllum = 999999.0;
2675 18120 : if (DirectNrmIllum < 0.0) DirectNrmIllum = 999999.0;
2676 18120 : if (DiffuseHorizIllum < 0.0) DiffuseHorizIllum = 999999.0;
2677 18120 : if (ZenLum < 0.0) ZenLum = 99999.0;
2678 18120 : if (AtmPress < 0.0) AtmPress = 999999.0;
2679 18120 : if (WindSpeed < 0.0) WindSpeed = 999.0;
2680 18120 : if (WindDir < -360.0 || WindDir > 360.0) WindDir = 999.0;
2681 18120 : if (TotalSkyCover < 0.0) TotalSkyCover = 99.0;
2682 18120 : if (RelHum < 0.0) RelHum = 999.0;
2683 18120 : if (OpaqueSkyCover < 0.0) OpaqueSkyCover = 99.0;
2684 18120 : if (Visibility < 0.0) Visibility = 9999.0;
2685 18120 : if (CeilHeight < 0.0) CeilHeight = 9999.0;
2686 18120 : if (PresWeathObs < 0) PresWeathObs = 9;
2687 18120 : if (PrecipWater < 0.0) PrecipWater = 999.0;
2688 18120 : if (AerosolOptDepth < 0.0) AerosolOptDepth = 999.0;
2689 18120 : if (SnowDepth < 0.0) SnowDepth = 999.0;
2690 18120 : if (DaysSinceLastSnow < 0.0) DaysSinceLastSnow = 99.0;
2691 18120 : if (Albedo < 0.0) Albedo = 999.0;
2692 18120 : if (LiquidPrecip < 0.0) LiquidPrecip = 999.0;
2693 :
2694 18120 : if (hour == 1 && CurTimeStep == 1) {
2695 755 : if (WMonth == 2 && WDay == 29 && (!state.dataEnvrn->CurrentYearIsLeapYear || !state.dataWeather->WFAllowsLeapYears)) {
2696 0 : state.dataWeather->EndDayOfMonth(2) = 28;
2697 0 : state.dataWeather->EndDayOfMonthWithLeapDay(2) = 28;
2698 0 : SkipThisDay = true;
2699 0 : TryAgain = true;
2700 0 : ShowWarningError(state, "ReadEPlusWeatherForDay: Feb29 data encountered but will not be processed.");
2701 0 : if (!state.dataWeather->WFAllowsLeapYears) {
2702 0 : ShowContinueError(
2703 : state, "...WeatherFile does not allow Leap Years. HOLIDAYS/DAYLIGHT SAVINGS header must indicate \"Yes\".");
2704 : }
2705 0 : continue;
2706 : } else {
2707 755 : TryAgain = false;
2708 755 : SkipThisDay = false;
2709 : }
2710 :
2711 755 : if (thisEnviron.ActualWeather && state.dataEnvrn->CurrentYearIsLeapYear) {
2712 0 : if (WMonth == 3 && WDay == 1 && state.dataEnvrn->Month == 2 && state.dataEnvrn->DayOfMonth == 28) {
2713 0 : ShowFatalError(state, "ReadEPlusWeatherForDay: Current year is a leap year, but Feb29 data is missing.");
2714 : }
2715 : }
2716 :
2717 755 : state.dataWeather->TomorrowVariables.Year = WYear;
2718 755 : state.dataWeather->TomorrowVariables.Month = WMonth;
2719 755 : state.dataWeather->TomorrowVariables.DayOfMonth = WDay;
2720 755 : state.dataWeather->TomorrowVariables.DayOfYear = General::OrdinalDay(WMonth, WDay, state.dataWeather->LeapYearAdd);
2721 755 : state.dataWeather->TomorrowVariables.DayOfYear_Schedule = General::OrdinalDay(WMonth, WDay, 1);
2722 : Real64 A;
2723 : Real64 B;
2724 : Real64 C;
2725 : Real64 AVSC;
2726 755 : CalculateDailySolarCoeffs(state,
2727 755 : state.dataWeather->TomorrowVariables.DayOfYear,
2728 : A,
2729 : B,
2730 : C,
2731 : AVSC,
2732 755 : state.dataWeather->TomorrowVariables.EquationOfTime,
2733 755 : state.dataWeather->TomorrowVariables.SinSolarDeclinAngle,
2734 755 : state.dataWeather->TomorrowVariables.CosSolarDeclinAngle);
2735 755 : if (state.dataWeather->CurDayOfWeek <= 7) {
2736 755 : state.dataWeather->CurDayOfWeek = mod(state.dataWeather->CurDayOfWeek, 7) + 1;
2737 : }
2738 755 : state.dataWeather->TomorrowVariables.DayOfWeek = state.dataWeather->CurDayOfWeek;
2739 1510 : state.dataWeather->TomorrowVariables.DaylightSavingIndex =
2740 755 : state.dataWeather->DSTIndex(state.dataWeather->TomorrowVariables.DayOfYear);
2741 755 : state.dataWeather->TomorrowVariables.HolidayIndex =
2742 755 : state.dataWeather->SpecialDayTypes(state.dataWeather->TomorrowVariables.DayOfYear);
2743 : }
2744 :
2745 18120 : if (SkipThisDay) continue;
2746 :
2747 : // Check out missing values
2748 :
2749 18120 : if (DryBulb >= 99.9) {
2750 0 : DryBulb = state.dataWeather->wvarsMissing.OutDryBulbTemp;
2751 0 : ++state.dataWeather->wvarsMissedCounts.OutDryBulbTemp;
2752 : }
2753 18120 : if (DryBulb < -90.0 || DryBulb > 70.0) {
2754 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp;
2755 : }
2756 :
2757 18120 : if (DewPoint >= 99.9) {
2758 0 : DewPoint = state.dataWeather->wvarsMissing.OutDewPointTemp;
2759 0 : ++state.dataWeather->wvarsMissedCounts.OutDewPointTemp;
2760 : }
2761 18120 : if (DewPoint < -90.0 || DewPoint > 70.0) {
2762 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp;
2763 : }
2764 :
2765 18120 : if (RelHum >= 999.0) {
2766 0 : RelHum = state.dataWeather->wvarsMissing.OutRelHum;
2767 0 : ++state.dataWeather->wvarsMissedCounts.OutRelHum;
2768 : }
2769 18120 : if (RelHum < 0.0 || RelHum > 110.0) {
2770 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutRelHum;
2771 : }
2772 :
2773 18120 : if (AtmPress >= 999999.0) {
2774 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2775 0 : ++state.dataWeather->wvarsMissedCounts.OutBaroPress;
2776 : }
2777 18120 : if (AtmPress <= 31000.0 || AtmPress > 120000.0) {
2778 0 : ++state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress;
2779 0 : AtmPress = state.dataWeather->wvarsMissing.OutBaroPress;
2780 : }
2781 :
2782 18120 : if (WindDir >= 999.0) {
2783 0 : WindDir = state.dataWeather->wvarsMissing.WindDir;
2784 0 : ++state.dataWeather->wvarsMissedCounts.WindDir;
2785 : }
2786 18120 : if (WindDir < 0.0 || WindDir > 360.0) {
2787 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindDir;
2788 : }
2789 :
2790 18120 : if (WindSpeed >= 999.0) {
2791 0 : WindSpeed = state.dataWeather->wvarsMissing.WindSpeed;
2792 0 : ++state.dataWeather->wvarsMissedCounts.WindSpeed;
2793 : }
2794 18120 : if (WindSpeed < 0.0 || WindSpeed > 40.0) {
2795 0 : ++state.dataWeather->wvarsOutOfRangeCounts.WindSpeed;
2796 : }
2797 :
2798 18120 : if (TotalSkyCover >= 99.0) {
2799 0 : TotalSkyCover = state.dataWeather->wvarsMissing.TotalSkyCover;
2800 0 : ++state.dataWeather->wvarsMissedCounts.TotalSkyCover;
2801 : }
2802 :
2803 18120 : if (OpaqueSkyCover >= 99.0) {
2804 0 : OpaqueSkyCover = state.dataWeather->wvarsMissing.OpaqueSkyCover;
2805 0 : ++state.dataWeather->wvarsMissedCounts.OpaqueSkyCover;
2806 : }
2807 :
2808 18120 : if (SnowDepth >= 999.0) {
2809 0 : SnowDepth = state.dataWeather->wvarsMissing.SnowDepth;
2810 0 : ++state.dataWeather->wvarsMissedCounts.SnowDepth;
2811 : }
2812 :
2813 18120 : if (Albedo >= 999.0) {
2814 16608 : Albedo = state.dataWeather->wvarsMissing.Albedo;
2815 16608 : ++state.dataWeather->wvarsMissedCounts.Albedo;
2816 : }
2817 :
2818 18120 : if (LiquidPrecip >= 999.0) {
2819 16641 : LiquidPrecip = state.dataWeather->wvarsMissing.LiquidPrecip;
2820 16641 : ++state.dataWeather->wvarsMissedCounts.LiquidPrecip;
2821 : }
2822 :
2823 18120 : auto &tomorrow = state.dataWeather->wvarsHrTsTomorrow(CurTimeStep, hour);
2824 18120 : tomorrow.OutDryBulbTemp = DryBulb;
2825 18120 : tomorrow.OutDewPointTemp = DewPoint;
2826 18120 : tomorrow.OutBaroPress = AtmPress;
2827 18120 : tomorrow.OutRelHum = RelHum;
2828 18120 : RelHum *= 0.01;
2829 18120 : tomorrow.WindSpeed = WindSpeed;
2830 18120 : tomorrow.WindDir = WindDir;
2831 18120 : tomorrow.LiquidPrecip = LiquidPrecip;
2832 18120 : tomorrow.TotalSkyCover = TotalSkyCover;
2833 18120 : tomorrow.OpaqueSkyCover = OpaqueSkyCover;
2834 :
2835 18120 : calcSky(state, tomorrow.HorizIRSky, tomorrow.SkyTemp, OpaqueSkyCover, DryBulb, DewPoint, RelHum, IRHoriz);
2836 :
2837 18120 : if (ETHoriz >= 9999.0) ETHoriz = 0.0;
2838 18120 : if (ETDirect >= 9999.0) ETDirect = 0.0;
2839 18120 : if (GLBHoriz >= 9999.0) GLBHoriz = 0.0;
2840 18120 : if (DirectRad >= 9999.0) DirectRad = 0.0;
2841 18120 : if (DiffuseRad >= 9999.0) DiffuseRad = 0.0;
2842 18120 : if (GLBHorizIllum >= 999900.0) GLBHorizIllum = 0.0;
2843 18120 : if (DirectNrmIllum >= 999900.0) DirectNrmIllum = 0.0;
2844 18120 : if (DiffuseHorizIllum >= 999900.0) DiffuseHorizIllum = 0.0;
2845 18120 : if (ZenLum >= 99990.0) ZenLum = 0.0;
2846 18120 : if (state.dataEnvrn->IgnoreSolarRadiation) {
2847 0 : GLBHoriz = 0.0;
2848 0 : DirectRad = 0.0;
2849 0 : DiffuseRad = 0.0;
2850 : }
2851 18120 : if (state.dataEnvrn->IgnoreBeamRadiation) {
2852 0 : DirectRad = 0.0;
2853 : }
2854 18120 : if (state.dataEnvrn->IgnoreDiffuseRadiation) {
2855 0 : DiffuseRad = 0.0;
2856 : }
2857 :
2858 18120 : tomorrow.BeamSolarRad = DirectRad;
2859 18120 : tomorrow.DifSolarRad = DiffuseRad;
2860 :
2861 18120 : tomorrow.IsRain = false;
2862 18120 : if (PresWeathObs == 0) {
2863 0 : if (PresWeathConds(1) < 9 || PresWeathConds(2) < 9 || PresWeathConds(3) < 9) tomorrow.IsRain = true;
2864 : } else {
2865 18120 : tomorrow.IsRain = false;
2866 : }
2867 18120 : tomorrow.IsSnow = (SnowDepth > 0.0);
2868 :
2869 : // default if rain but none on weather file
2870 18120 : if (tomorrow.IsRain && tomorrow.LiquidPrecip == 0.0) tomorrow.LiquidPrecip = 2.0; // 2mm in an hour ~ .08 inch
2871 :
2872 18120 : state.dataWeather->wvarsMissing.OutDryBulbTemp = DryBulb;
2873 18120 : state.dataWeather->wvarsMissing.OutDewPointTemp = DewPoint;
2874 18120 : state.dataWeather->wvarsMissing.OutRelHum = static_cast<int>(std::round(RelHum * 100.0));
2875 18120 : state.dataWeather->wvarsMissing.OutBaroPress = AtmPress;
2876 18120 : state.dataWeather->wvarsMissing.WindDir = WindDir;
2877 18120 : state.dataWeather->wvarsMissing.WindSpeed = WindSpeed;
2878 18120 : state.dataWeather->wvarsMissing.TotalSkyCover = TotalSkyCover;
2879 18120 : state.dataWeather->wvarsMissing.OpaqueSkyCover = OpaqueSkyCover;
2880 18120 : state.dataWeather->wvarsMissing.Visibility = Visibility;
2881 18120 : state.dataWeather->wvarsMissing.Ceiling = CeilHeight;
2882 18120 : state.dataWeather->wvarsMissing.WaterPrecip = PrecipWater;
2883 18120 : state.dataWeather->wvarsMissing.AerOptDepth = AerosolOptDepth;
2884 18120 : state.dataWeather->wvarsMissing.SnowDepth = SnowDepth;
2885 18120 : state.dataWeather->wvarsMissing.DaysLastSnow = DaysSinceLastSnow;
2886 18120 : state.dataWeather->wvarsMissing.Albedo = Albedo;
2887 :
2888 18120 : } // for (CurTimeStep)
2889 :
2890 : } // for (Hour)
2891 :
2892 : } // Try Again While Loop
2893 :
2894 755 : if (BackSpaceAfterRead) {
2895 4 : state.files.inputWeatherFile.backspace();
2896 : }
2897 :
2898 755 : if (state.dataWeather->NumIntervalsPerHour == 1 && state.dataGlobal->TimeStepsInHour > 1) {
2899 : // Create interpolated weather for timestep orientation
2900 : // First copy ts=1 (hourly) from data arrays to Wthr structure
2901 18850 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
2902 18096 : wvarsHr(hour) = state.dataWeather->wvarsHrTsTomorrow(1, hour);
2903 : }
2904 :
2905 754 : if (!state.dataWeather->LastHourSet) {
2906 : // For first day of weather, all time steps of the first hour will be
2907 : // equal to the first hour's value.
2908 : // 2021-06: An additional input is added to here to allow the user to have chosen which hour to use
2909 27 : int HrUsedtoInterp = thisEnviron.firstHrInterpUseHr1 ? 1 : 24;
2910 27 : state.dataWeather->wvarsLastHr = wvarsHr(HrUsedtoInterp);
2911 27 : state.dataWeather->LastHourSet = true;
2912 : }
2913 :
2914 18850 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
2915 :
2916 18096 : int NextHr = (hour == Constant::iHoursInDay) ? 1 : hour + 1;
2917 :
2918 18096 : state.dataWeather->wvarsNextHr.BeamSolarRad = wvarsHr(NextHr).BeamSolarRad;
2919 18096 : state.dataWeather->wvarsNextHr.DifSolarRad = wvarsHr(NextHr).DifSolarRad;
2920 18096 : state.dataWeather->wvarsNextHr.LiquidPrecip = wvarsHr(NextHr).LiquidPrecip;
2921 :
2922 90480 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
2923 :
2924 72384 : Real64 wgtCurrHr = state.dataWeather->Interpolation(ts);
2925 72384 : Real64 wgtPrevHr = 1.0 - wgtCurrHr;
2926 :
2927 : // Do Solar "weighting"
2928 :
2929 72384 : Real64 wgtCurrHrSolar = state.dataWeather->SolarInterpolation(ts);
2930 : Real64 wgtPrevHrSolar;
2931 : Real64 wgtNextHrSolar;
2932 :
2933 72384 : if (state.dataGlobal->TimeStepsInHour == 1) {
2934 0 : wgtNextHrSolar = 1.0 - wgtCurrHr;
2935 0 : wgtPrevHrSolar = 0.0;
2936 72384 : } else if (wgtCurrHrSolar == 1.0) {
2937 : // It's at the half hour
2938 18096 : wgtPrevHrSolar = 0.0;
2939 18096 : wgtNextHrSolar = 0.0;
2940 54288 : } else if (ts * state.dataWeather->TimeStepFraction < 0.5) {
2941 18096 : wgtPrevHrSolar = 1.0 - wgtCurrHrSolar;
2942 18096 : wgtNextHrSolar = 0.0;
2943 : } else { // After the half hour
2944 36192 : wgtPrevHrSolar = 0.0;
2945 36192 : wgtNextHrSolar = 1.0 - wgtCurrHrSolar;
2946 : }
2947 :
2948 72384 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
2949 72384 : auto const &wvarsH = wvarsHr(hour);
2950 72384 : tomorrowTs.OutDryBulbTemp = state.dataWeather->wvarsLastHr.OutDryBulbTemp * wgtPrevHr + wvarsH.OutDryBulbTemp * wgtCurrHr;
2951 72384 : tomorrowTs.OutBaroPress = state.dataWeather->wvarsLastHr.OutBaroPress * wgtPrevHr + wvarsH.OutBaroPress * wgtCurrHr;
2952 72384 : tomorrowTs.OutDewPointTemp = state.dataWeather->wvarsLastHr.OutDewPointTemp * wgtPrevHr + wvarsH.OutDewPointTemp * wgtCurrHr;
2953 72384 : tomorrowTs.OutRelHum = state.dataWeather->wvarsLastHr.OutRelHum * wgtPrevHr + wvarsH.OutRelHum * wgtCurrHr;
2954 72384 : tomorrowTs.WindSpeed = state.dataWeather->wvarsLastHr.WindSpeed * wgtPrevHr + wvarsH.WindSpeed * wgtCurrHr;
2955 72384 : tomorrowTs.WindDir = interpolateWindDirection(state.dataWeather->wvarsLastHr.WindDir, wvarsH.WindDir, wgtCurrHr);
2956 72384 : tomorrowTs.TotalSkyCover = state.dataWeather->wvarsLastHr.TotalSkyCover * wgtPrevHr + wvarsH.TotalSkyCover * wgtCurrHr;
2957 72384 : tomorrowTs.OpaqueSkyCover = state.dataWeather->wvarsLastHr.OpaqueSkyCover * wgtPrevHr + wvarsH.OpaqueSkyCover * wgtCurrHr;
2958 : // Sky emissivity now takes interpolated timestep inputs rather than interpolated calculation esky results
2959 72384 : calcSky(state,
2960 72384 : tomorrowTs.HorizIRSky,
2961 72384 : tomorrowTs.SkyTemp,
2962 : tomorrowTs.OpaqueSkyCover,
2963 : tomorrowTs.OutDryBulbTemp,
2964 : tomorrowTs.OutDewPointTemp,
2965 72384 : tomorrowTs.OutRelHum * 0.01,
2966 72384 : state.dataWeather->wvarsLastHr.HorizIRSky * wgtPrevHr + wvarsH.HorizIRSky * wgtCurrHr);
2967 :
2968 72384 : tomorrowTs.DifSolarRad = state.dataWeather->wvarsLastHr.DifSolarRad * wgtPrevHrSolar + wvarsH.DifSolarRad * wgtCurrHrSolar +
2969 72384 : state.dataWeather->wvarsNextHr.DifSolarRad * wgtNextHrSolar;
2970 72384 : tomorrowTs.BeamSolarRad = state.dataWeather->wvarsLastHr.BeamSolarRad * wgtPrevHrSolar + wvarsH.BeamSolarRad * wgtCurrHrSolar +
2971 72384 : state.dataWeather->wvarsNextHr.BeamSolarRad * wgtNextHrSolar;
2972 :
2973 72384 : tomorrowTs.LiquidPrecip = state.dataWeather->wvarsLastHr.LiquidPrecip * wgtPrevHr + wvarsH.LiquidPrecip * wgtCurrHr;
2974 72384 : tomorrowTs.LiquidPrecip /= double(state.dataGlobal->TimeStepsInHour);
2975 72384 : tomorrowTs.IsRain = tomorrowTs.LiquidPrecip >= state.dataWeather->IsRainThreshold; // Wthr%IsRain
2976 72384 : tomorrowTs.IsSnow = wvarsH.IsSnow;
2977 : } // End of TS Loop
2978 :
2979 18096 : state.dataWeather->wvarsLastHr = wvarsHr(hour);
2980 : } // End of Hour Loop
2981 : }
2982 :
2983 755 : if (thisEnviron.WP_Type1 != 0) {
2984 0 : switch (state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1).skyTempModel) {
2985 0 : case SkyTempModel::ScheduleValue: {
2986 : std::vector<Real64> const &dayVals =
2987 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
2988 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
2989 :
2990 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
2991 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
2992 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
2993 : }
2994 : }
2995 0 : } break;
2996 :
2997 0 : case SkyTempModel::DryBulbDelta: {
2998 : std::vector<Real64> const &dayVals =
2999 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3000 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3001 :
3002 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3003 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3004 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3005 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3006 : }
3007 : }
3008 0 : } break;
3009 :
3010 0 : case SkyTempModel::DewPointDelta: {
3011 : std::vector<Real64> const &dayVals =
3012 0 : state.dataWeather->WPSkyTemperature(thisEnviron.WP_Type1)
3013 0 : .sched->getDayVals(state, state.dataWeather->TomorrowVariables.DayOfYear_Schedule, state.dataWeather->CurDayOfWeek);
3014 :
3015 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3016 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3017 0 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3018 0 : tomorrowTs.SkyTemp = tomorrowTs.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3019 : }
3020 : }
3021 0 : } break;
3022 :
3023 0 : default:
3024 0 : break;
3025 : }
3026 : }
3027 755 : }
3028 :
3029 72433 : Real64 interpolateWindDirection(Real64 const prevHrWindDir, Real64 const curHrWindDir, Real64 const curHrWeight)
3030 : {
3031 : // adapted from http://stackoverflow.com/questions/2708476/rotation-interpolation
3032 72433 : Real64 curAng = curHrWindDir;
3033 72433 : Real64 prevAng = prevHrWindDir;
3034 72433 : Real64 diff = std::abs(curAng - prevAng);
3035 72433 : if (diff > 180.) {
3036 4148 : if (curAng > prevAng) {
3037 1792 : prevAng += 360.;
3038 : } else {
3039 2356 : curAng += 360.;
3040 : }
3041 : }
3042 72433 : Real64 interpAng = prevAng + (curAng - prevAng) * curHrWeight;
3043 72433 : return (fmod(interpAng, 360.)); // fmod is float modulus function
3044 : }
3045 :
3046 68408 : Real64 CalcSkyEmissivity(
3047 : EnergyPlusData &state, SkyTempModel const ESkyCalcType, Real64 const OSky, Real64 const DryBulb, Real64 const DewPoint, Real64 const RelHum)
3048 : {
3049 : // Calculate Sky Emissivity
3050 : // References:
3051 : // M. Li, Y. Jiang and C. F. M. Coimbra,
3052 : // "On the determination of atmospheric longwave irradiance under all-sky conditions,"
3053 : // Solar Energy 144, 2017, pp. 40–48,
3054 : // G. Clark and C. Allen, "The Estimation of Atmospheric Radiation for Clear and
3055 : // Cloudy Skies," Proc. 2nd National Passive Solar Conference (AS/ISES), 1978, pp. 675-678.
3056 :
3057 : Real64 ESky;
3058 :
3059 68408 : if (ESkyCalcType == SkyTempModel::Brunt) {
3060 2 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3061 2 : ESky = 0.618 + 0.056 * pow(PartialPress, 0.5);
3062 68406 : } else if (ESkyCalcType == SkyTempModel::Idso) {
3063 2 : double const PartialPress = RelHum * Psychrometrics::PsyPsatFnTemp(state, DryBulb) * 0.01;
3064 2 : ESky = 0.685 + 0.000032 * PartialPress * exp(1699 / (DryBulb + Constant::Kelvin));
3065 68404 : } else if (ESkyCalcType == SkyTempModel::BerdahlMartin) {
3066 2 : double const TDewC = min(DryBulb, DewPoint);
3067 2 : ESky = 0.758 + 0.521 * (TDewC / 100) + 0.625 * pow_2(TDewC / 100);
3068 : } else {
3069 68402 : ESky = 0.787 + 0.764 * std::log((min(DryBulb, DewPoint) + Constant::Kelvin) / Constant::Kelvin);
3070 : }
3071 68408 : return ESky * (1.0 + 0.0224 * OSky - 0.0035 * pow_2(OSky) + 0.00028 * pow_3(OSky));
3072 : }
3073 :
3074 27 : void SetDayOfWeekInitialValues(int const EnvironDayOfWeek, // Starting Day of Week for the (Weather) RunPeriod (User Input)
3075 : int ¤tDayOfWeek // Current Day of Week
3076 : )
3077 : {
3078 :
3079 : // SUBROUTINE INFORMATION:
3080 : // AUTHOR Linda Lawrie
3081 : // DATE WRITTEN March 2012
3082 :
3083 : // PURPOSE OF THIS SUBROUTINE:
3084 : // Set of begin day of week for an environment. Similar sets but slightly different
3085 : // conditions. Improve code readability by having three routine calls instead of three
3086 : // IF blocks.
3087 :
3088 27 : if (EnvironDayOfWeek != 0) {
3089 27 : if (EnvironDayOfWeek <= 7) {
3090 27 : currentDayOfWeek = EnvironDayOfWeek - 1;
3091 : } else {
3092 0 : currentDayOfWeek = EnvironDayOfWeek;
3093 : }
3094 : }
3095 27 : }
3096 :
3097 0 : void ErrorInterpretWeatherDataLine(EnergyPlusData &state,
3098 : int const WYear,
3099 : int const WMonth,
3100 : int const WDay,
3101 : int const WHour,
3102 : int const WMinute,
3103 : std::string_view SaveLine,
3104 : std::string_view Line)
3105 : {
3106 0 : ShowSevereError(state, fmt::format("Invalid Weather Line at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3107 0 : ShowContinueError(state, fmt::format("Full Data Line={}", SaveLine));
3108 0 : ShowContinueError(state, fmt::format("Remainder of line={}", Line));
3109 0 : ShowFatalError(state, "Error in Reading Weather Data");
3110 0 : }
3111 :
3112 27632 : void InterpretWeatherDataLine(EnergyPlusData &state,
3113 : std::string_view Line,
3114 : bool &ErrorFound, // True if an error is found, false otherwise
3115 : int &WYear,
3116 : int &WMonth,
3117 : int &WDay,
3118 : int &WHour,
3119 : int &WMinute,
3120 : Real64 &DryBulb,
3121 : Real64 &DewPoint,
3122 : Real64 &RelHum,
3123 : Real64 &AtmPress,
3124 : Real64 Ðoriz,
3125 : Real64 &ETDirect,
3126 : Real64 &IRHoriz,
3127 : Real64 &GLBHoriz,
3128 : Real64 &DirectRad,
3129 : Real64 &DiffuseRad,
3130 : Real64 &GLBHorizIllum,
3131 : Real64 &DirectNrmIllum,
3132 : Real64 &DiffuseHorizIllum,
3133 : Real64 &ZenLum,
3134 : Real64 &WindDir,
3135 : Real64 &WindSpeed,
3136 : Real64 &TotalSkyCover,
3137 : Real64 &OpaqueSkyCover,
3138 : Real64 &Visibility,
3139 : Real64 &CeilHeight,
3140 : int &WObs, // PresWeathObs
3141 : Array1D_int &WCodesArr, // PresWeathConds
3142 : Real64 &PrecipWater,
3143 : Real64 &AerosolOptDepth,
3144 : Real64 &SnowDepth,
3145 : Real64 &DaysSinceLastSnow,
3146 : Real64 &Albedo,
3147 : Real64 &LiquidPrecip)
3148 : {
3149 :
3150 : // SUBROUTINE INFORMATION:
3151 : // AUTHOR Linda Lawrie
3152 : // DATE WRITTEN April 2001
3153 :
3154 : // PURPOSE OF THIS SUBROUTINE:
3155 : // This subroutine interprets the EPW weather data line because comma delimited fields
3156 : // may cause problems with some compilers. (Particularly character variables in
3157 : // comma delimited lines.
3158 :
3159 : // METHODOLOGY EMPLOYED:
3160 : // Field by field interpretation, eliminating the "data source field" which is also
3161 : // likely to contain blanks. Note that the "Weatherconditions" must be a 9 character
3162 : // alpha field with no intervening blanks.
3163 :
3164 27632 : EP_SIZE_CHECK(WCodesArr, 9); // NOLINT(misc-static-assert)
3165 :
3166 : static constexpr std::string_view ValidDigits("0123456789");
3167 :
3168 27632 : std::string_view::size_type pos = 0;
3169 27632 : std::string_view current_line = Line;
3170 :
3171 27632 : ErrorFound = false;
3172 :
3173 : // Do the first five. (To get to the DataSource field)
3174 : {
3175 27632 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 5); // Returns the position **after** the nth occurrence of ','
3176 27632 : const bool succeeded = readList(current_line.substr(pos, (nth_pos - 1) - pos), WYear, WMonth, WDay, WHour, WMinute);
3177 27632 : if (!succeeded) {
3178 0 : ShowSevereError(state, "Invalid Date info in Weather Line");
3179 0 : ShowContinueError(state, fmt::format("Entire Data Line={}", Line));
3180 0 : ShowFatalError(state, "Error in Reading Weather Data");
3181 : }
3182 : }
3183 :
3184 27632 : bool DateInError = false;
3185 27632 : if (WMonth >= 1 && WMonth <= 12) {
3186 : // Month number is valid
3187 27632 : if (WMonth != 2) {
3188 25560 : if (WDay > state.dataWeather->EndDayOfMonth(WMonth)) {
3189 0 : DateInError = true;
3190 : }
3191 2072 : } else if (WDay > state.dataWeather->EndDayOfMonth(WMonth) + 1) { // Whether actually used is determined by calling routine.
3192 0 : DateInError = true;
3193 : }
3194 : } else {
3195 0 : DateInError = true;
3196 : }
3197 :
3198 27632 : if (DateInError) {
3199 0 : ShowSevereError(state, format("Reading Weather Data Line, Invalid Date, Year={}, Month={}, Day={}", WYear, WMonth, WDay));
3200 0 : ShowFatalError(state, "Program terminates due to previous condition.");
3201 : }
3202 :
3203 : // index, unlike nth_occurrence returns the position of the search char, not the position after it
3204 27632 : pos = index(Line, ','); // WYear
3205 27632 : if (pos == std::string::npos) {
3206 0 : ShowSevereError(
3207 0 : state, format("Invalid Weather Line (no commas) at date={:4}/{:2}/{:2} Hour#={:2} Min#={:2}", WYear, WMonth, WDay, WHour, WMinute));
3208 0 : ShowContinueError(state, fmt::format("Full Data Line={}", Line));
3209 0 : ShowFatalError(state, "Error in Reading Weather Data");
3210 : }
3211 27632 : current_line.remove_prefix(nth_occurrence(Line, ',', 6)); // remove WYear,WMonth,WDay,WHour,WMinute,Data Source/Integrity
3212 :
3213 : // Now read more numerics with List Directed I/O (note there is another "character" field lurking)
3214 : Real64 RField21;
3215 : {
3216 27632 : std::string_view::size_type nth_pos = nth_occurrence(current_line, ',', 21);
3217 :
3218 27632 : const bool succeeded = readList(current_line.substr(0, nth_pos - 1),
3219 : DryBulb,
3220 : DewPoint,
3221 : RelHum,
3222 : AtmPress,
3223 : ETHoriz,
3224 : ETDirect,
3225 : IRHoriz,
3226 : GLBHoriz,
3227 : DirectRad,
3228 : DiffuseRad,
3229 : GLBHorizIllum,
3230 : DirectNrmIllum,
3231 : DiffuseHorizIllum,
3232 : ZenLum,
3233 : WindDir,
3234 : WindSpeed,
3235 : TotalSkyCover,
3236 : OpaqueSkyCover,
3237 : Visibility,
3238 : CeilHeight,
3239 : RField21);
3240 :
3241 27632 : if (!succeeded) ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3242 27632 : current_line.remove_prefix(nth_pos);
3243 : }
3244 27632 : pos = index(current_line, ',');
3245 27632 : std::string PresWeathCodes;
3246 27632 : if (pos != std::string::npos && pos != 0) {
3247 27632 : PresWeathCodes = current_line.substr(0, pos);
3248 : } else {
3249 0 : PresWeathCodes = "999999999";
3250 : }
3251 27632 : current_line.remove_prefix(pos + 1);
3252 :
3253 27632 : auto readNextNumber = // (AUTO_OK_LAMBDA)
3254 165792 : [reachedEndOfCommands = false, &state, &WYear, &WMonth, &WDay, &WHour, &WMinute, &Line, ¤t_line]() mutable -> Real64 {
3255 165792 : if (reachedEndOfCommands) {
3256 4 : return 999.0;
3257 : }
3258 : Real64 target;
3259 165788 : std::string_view::size_type pos = index(current_line, ',');
3260 : // We found a comma
3261 165788 : if (pos != std::string::npos) {
3262 : // Content is not empty
3263 165787 : if (pos != 0) {
3264 165787 : bool error = false;
3265 165787 : target = Util::ProcessNumber(current_line.substr(0, pos), error);
3266 165787 : if (error) {
3267 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3268 : }
3269 : } else {
3270 0 : target = 999.0;
3271 : }
3272 165787 : current_line.remove_prefix(pos + 1);
3273 : } else {
3274 : // Couldn't find next comma, but we need to process the potential current number
3275 1 : reachedEndOfCommands = true;
3276 1 : if (current_line.empty()) {
3277 0 : target = 999.0;
3278 : } else {
3279 1 : bool error = false;
3280 1 : target = Util::ProcessNumber(current_line, error);
3281 1 : if (error) {
3282 0 : ErrorInterpretWeatherDataLine(state, WYear, WMonth, WDay, WHour, WMinute, Line, current_line);
3283 : }
3284 : }
3285 : }
3286 165788 : return target;
3287 27632 : };
3288 :
3289 27632 : PrecipWater = readNextNumber();
3290 27632 : AerosolOptDepth = readNextNumber();
3291 27632 : SnowDepth = readNextNumber();
3292 27632 : DaysSinceLastSnow = readNextNumber();
3293 27632 : Albedo = readNextNumber();
3294 27632 : LiquidPrecip = readNextNumber();
3295 :
3296 27632 : WObs = nint(RField21);
3297 27632 : if (WObs == 0) { // Obs Indicator indicates Weather Codes valid
3298 : // Check for miscellaneous characters
3299 2 : pos = index(PresWeathCodes, '\'');
3300 2 : while (pos != std::string::npos) {
3301 0 : PresWeathCodes[pos] = ' ';
3302 0 : pos = index(PresWeathCodes, '\'');
3303 : }
3304 2 : pos = index(PresWeathCodes, '"');
3305 2 : while (pos != std::string::npos) {
3306 0 : PresWeathCodes[pos] = ' ';
3307 0 : pos = index(PresWeathCodes, '"');
3308 : }
3309 2 : strip(PresWeathCodes);
3310 2 : if (len(PresWeathCodes) == 9) {
3311 20 : for (pos = 0; pos < 9; ++pos) {
3312 18 : if (!has(ValidDigits, PresWeathCodes[pos])) PresWeathCodes[pos] = '9';
3313 : }
3314 :
3315 : // we are trying to read a string of 9 integers with no spaces, each
3316 : // into its own integer, like:
3317 : // "123456789"
3318 : // becomes
3319 : // std::vector<int>{1,2,3,4,5,6,7,8,9};
3320 2 : std::stringstream reader = stringReader(PresWeathCodes);
3321 20 : for (auto &value : WCodesArr) {
3322 18 : char c[2] = {0, 0}; // a string of 2 characters, init both to 0
3323 18 : reader >> c[0]; // read next char into the first byte
3324 18 : value = std::atoi(c); // convert this short string into the appropriate int to read
3325 : }
3326 2 : } else {
3327 0 : ++state.dataWeather->wvarsMissedCounts.WeathCodes;
3328 0 : WCodesArr = 9;
3329 : }
3330 : } else {
3331 27630 : WCodesArr = 9;
3332 : }
3333 27632 : }
3334 :
3335 517 : void SetUpDesignDay(EnergyPlusData &state, int const EnvrnNum) // Environment number passed into the routine
3336 : {
3337 :
3338 : // SUBROUTINE INFORMATION:
3339 : // AUTHOR Linda Lawrie
3340 : // DATE WRITTEN February 1977
3341 : // MODIFIED June 1997 (RKS); May 2013 (LKL) add temperature profile for drybulb.
3342 : // RE-ENGINEERED August 2003;LKL -- to generate timestep weather for design days.
3343 :
3344 : // PURPOSE OF THIS SUBROUTINE:
3345 : // This purpose of this subroutine is to convert the user supplied input
3346 : // values for the design day parameters into an entire weather day
3347 : // record. This now bypasses any file I/O by keeping all of the
3348 : // weather day record information in the local module level derived type
3349 : // called DesignDay.
3350 :
3351 517 : constexpr Real64 GlobalSolarConstant = 1367.0;
3352 517 : constexpr Real64 ZHGlobalSolarConstant = 1355.0;
3353 :
3354 517 : Real64 constexpr ZhangHuang_C0 = 0.5598; // 37.6865d0
3355 517 : Real64 constexpr ZhangHuang_C1 = 0.4982; // 13.9263d0
3356 517 : Real64 constexpr ZhangHuang_C2 = -0.6762; // -20.2354d0
3357 517 : Real64 constexpr ZhangHuang_C3 = 0.02842; // 0.9695d0
3358 517 : Real64 constexpr ZhangHuang_C4 = -0.00317; // -0.2046d0
3359 517 : Real64 constexpr ZhangHuang_C5 = 0.014; // -0.0980d0
3360 517 : Real64 constexpr ZhangHuang_D = -17.853; // -10.8568d0
3361 517 : Real64 constexpr ZhangHuang_K = 0.843; // 49.3112d0
3362 : static constexpr std::string_view RoutineNamePsyWFnTdbTwbPb("SetUpDesignDay:PsyWFnTdbTwbPb");
3363 : static constexpr std::string_view RoutineNamePsyWFnTdpPb("SetUpDesignDay:PsyWFnTdpPb");
3364 : static constexpr std::string_view RoutineNamePsyWFnTdbH("SetUpDesignDay:PsyWFnTdbH");
3365 : static constexpr std::string_view WeatherManager("WeatherManager");
3366 : static constexpr std::string_view RoutineNameLong("WeatherManager.cc subroutine SetUpDesignDay");
3367 :
3368 517 : std::string StringOut;
3369 : // For reporting purposes, set year to current system year
3370 :
3371 : struct HourlyWeatherData
3372 : {
3373 : // Members
3374 : Array1D<Real64> BeamSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly direct normal solar irradiance
3375 : Array1D<Real64> DifSolarRad = Array1D<Real64>(Constant::iHoursInDay, 0.0); // Hourly sky diffuse horizontal solar irradiance
3376 : };
3377 :
3378 : // Object Data
3379 517 : HourlyWeatherData Wthr;
3380 :
3381 517 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
3382 :
3383 517 : bool SaveWarmupFlag = state.dataGlobal->WarmupFlag;
3384 517 : state.dataGlobal->WarmupFlag = true;
3385 :
3386 517 : Array1D_int Date0(8);
3387 517 : date_and_time(_, _, _, Date0);
3388 517 : int CurrentYear = Date0(1);
3389 :
3390 517 : if (state.dataGlobal->BeginSimFlag) {
3391 103 : state.dataWeather->PrintDDHeader = true;
3392 : }
3393 :
3394 517 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
3395 517 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
3396 517 : designDay.Year = CurrentYear; // f90 date_and_time implemented. full 4 digit year !+ 1900
3397 517 : designDay.Month = desDayInput.Month;
3398 517 : designDay.DayOfMonth = desDayInput.DayOfMonth;
3399 517 : designDay.DayOfYear = General::OrdinalDay(designDay.Month, designDay.DayOfMonth, 0);
3400 : static constexpr std::string_view MnDyFmt("{:02}/{:02}");
3401 517 : state.dataEnvrn->CurMnDy = format(MnDyFmt, desDayInput.Month, desDayInput.DayOfMonth);
3402 : // EnvironmentName = DesDayInput( EnvrnNum ).Title;
3403 517 : state.dataEnvrn->RunPeriodEnvironment = false;
3404 : // Following builds Environment start/end for ASHRAE 55 warnings
3405 517 : state.dataEnvrn->EnvironmentStartEnd = state.dataEnvrn->CurMnDy + " - " + state.dataEnvrn->CurMnDy;
3406 :
3407 : // Check that barometric pressure is within range
3408 517 : if (desDayInput.PressureEntered) {
3409 517 : if (std::abs((desDayInput.PressBarom - state.dataEnvrn->StdBaroPress) / state.dataEnvrn->StdBaroPress) > 0.1) { // 10% off
3410 68 : ShowWarningError(state,
3411 68 : format("SetUpDesignDay: Entered DesignDay Barometric Pressure={:.0R} differs by more than 10% from Standard "
3412 : "Barometric Pressure={:.0R}.",
3413 34 : desDayInput.PressBarom,
3414 34 : state.dataEnvrn->StdBaroPress));
3415 68 : ShowContinueError(
3416 : state,
3417 68 : format("...occurs in DesignDay={}, Standard Pressure (based on elevation) will be used.", state.dataEnvrn->EnvironmentName));
3418 34 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3419 : }
3420 : } else {
3421 0 : desDayInput.PressBarom = state.dataEnvrn->StdBaroPress;
3422 : }
3423 :
3424 : // verify that design WB or DP <= design DB
3425 517 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint && desDayInput.DewPointNeedsSet) {
3426 : // dew-point
3427 0 : Real64 testval = Psychrometrics::PsyWFnTdbRhPb(state, desDayInput.MaxDryBulb, 1.0, desDayInput.PressBarom);
3428 0 : desDayInput.HumIndValue = Psychrometrics::PsyTdpFnWPb(state, testval, desDayInput.PressBarom);
3429 : }
3430 :
3431 : // Day of week defaults to Monday, if day type specified, then that is used.
3432 517 : designDay.DayOfWeek = 2;
3433 517 : if (desDayInput.DayType <= 7) designDay.DayOfWeek = desDayInput.DayType;
3434 :
3435 : // set Holiday as indicated by user input
3436 517 : designDay.HolidayIndex = 0;
3437 517 : if (desDayInput.DayType > 7) designDay.HolidayIndex = desDayInput.DayType;
3438 :
3439 517 : designDay.DaylightSavingIndex = desDayInput.DSTIndicator;
3440 :
3441 : // Set up Solar parameters for day
3442 : Real64 A; // Apparent solar irradiation at air mass = 0
3443 : Real64 B; // Atmospheric extinction coefficient
3444 : Real64 C; // ASHRAE diffuse radiation factor
3445 : Real64 AVSC; // Annual variation in the solar constant
3446 517 : CalculateDailySolarCoeffs(
3447 517 : state, designDay.DayOfYear, A, B, C, AVSC, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle);
3448 :
3449 517 : if (state.dataWeather->PrintDDHeader && state.dataReportFlag->DoWeatherInitReporting) {
3450 : static constexpr std::string_view EnvDDHdFormat(
3451 : "! <Environment:Design Day Data>, Max Dry-Bulb Temp {C}, Temp Range {dC}, Temp Range Ind Type, "
3452 : "Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units, Pressure {Pa}, Wind Direction {deg CW from N}, Wind "
3453 : "Speed {m/s}, Clearness, Rain, Snow");
3454 66 : print(state.files.eio, "{}\n", EnvDDHdFormat);
3455 : static constexpr std::string_view DDayMiscHdFormat(
3456 : "! <Environment:Design Day Misc>,DayOfYear,ASHRAE A Coeff,ASHRAE B Coeff,ASHRAE C Coeff,Solar "
3457 : "Constant-Annual Variation,Eq of Time {minutes}, Solar Declination Angle {deg}, Solar Model");
3458 66 : print(state.files.eio, "{}\n", DDayMiscHdFormat);
3459 66 : state.dataWeather->PrintDDHeader = false;
3460 : }
3461 517 : if (state.dataReportFlag->DoWeatherInitReporting) {
3462 113 : std::string_view const AlpUseRain = (desDayInput.RainInd == 1) ? "Yes" : "No";
3463 113 : std::string_view const AlpUseSnow = (desDayInput.SnowInd == 1) ? "Yes" : "No";
3464 113 : print(state.files.eio, "Environment:Design Day Data,");
3465 113 : print(state.files.eio, "{:.2R},", desDayInput.MaxDryBulb);
3466 113 : print(state.files.eio, "{:.2R},", desDayInput.DailyDBRange);
3467 :
3468 : static constexpr std::array<std::string_view, (int)DesDayDryBulbRangeType::Num> DesDayDryBulbRangeTypeStrings = {
3469 : "DefaultMultipliers,", "MultiplierSchedule,", "DifferenceSchedule,", "TemperatureProfile,"};
3470 :
3471 113 : print(state.files.eio, "{}", DesDayDryBulbRangeTypeStrings[(int)desDayInput.dryBulbRangeType]);
3472 :
3473 : static constexpr std::array<std::string_view, (int)DesDayHumIndType::Num> DesDayHumIndTypeStrings = {
3474 : "Wetbulb,{:.2R},{{C}},",
3475 : "Dewpoint,{:.2R},{{C}},",
3476 : "Enthalpy,{:.2R},{{J/kgDryAir}},",
3477 : "HumidityRatio,{:.4R},{{kgWater/kgDryAir}},",
3478 : "Schedule,<schedule values from 0.0 to 100.0>,{{percent}},",
3479 : "WetBulbProfileDefaultMultipliers,{:.2R},{{C}},",
3480 : "WetBulbProfileDifferenceSchedule,{:.2R},{{C}},",
3481 : "WetBulbProfileMultiplierSchedule,{:.2R},{{C}},"};
3482 :
3483 : // Hum Ind Type, Hum Ind Value at Max Temp, Hum Ind Units
3484 113 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch) {
3485 0 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType]);
3486 113 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
3487 0 : print(state.files.eio,
3488 0 : DesDayHumIndTypeStrings[(int)desDayInput.HumIndType],
3489 0 : state.dataWeather->DesDayInput(state.dataWeather->Envrn).HumIndValue);
3490 : } else {
3491 113 : print(state.files.eio, DesDayHumIndTypeStrings[(int)desDayInput.HumIndType], desDayInput.HumIndValue);
3492 : }
3493 :
3494 113 : print(state.files.eio, "{:.0R},", desDayInput.PressBarom);
3495 113 : print(state.files.eio, "{:.0R},", desDayInput.WindDir);
3496 113 : print(state.files.eio, "{:.1R},", desDayInput.WindSpeed);
3497 113 : print(state.files.eio, "{:.2R},", desDayInput.SkyClear);
3498 :
3499 113 : print(state.files.eio, "{},{}\n", AlpUseRain, AlpUseSnow);
3500 :
3501 : static constexpr std::string_view DDayMiscFormat("Environment:Design Day Misc,{:3},");
3502 113 : print(state.files.eio, DDayMiscFormat, designDay.DayOfYear);
3503 113 : print(state.files.eio, "{:.1R},", A);
3504 113 : print(state.files.eio, "{:.4R},", B);
3505 113 : print(state.files.eio, "{:.4R},", C);
3506 113 : print(state.files.eio, "{:.1R},", AVSC);
3507 113 : print(state.files.eio, "{:.2R},", designDay.EquationOfTime * 60.0);
3508 113 : print(state.files.eio, "{:.1R},", std::asin(designDay.SinSolarDeclinAngle) / Constant::DegToRad);
3509 :
3510 : // Why have a different string for "Schedule" here than the one used for input? Really, why?
3511 : static constexpr std::array<std::string_view, (int)DesDaySolarModel::Num> DesDaySolarModelStrings = {
3512 : "ASHRAEClearSky", "ZhangHuang", "User supplied beam/diffuse from schedules", "ASHRAETau", "ASHRAETau2017"};
3513 :
3514 113 : print(state.files.eio, "{}\n", DesDaySolarModelStrings[(int)desDayInput.solarModel]);
3515 : }
3516 :
3517 : // Must set up weather values for Design Day. User can specify the "humidity indicator" as
3518 : // Wetbulb, DewPoint or input the relative humidity schedule. For both wetbulb and dewpoint indicators, the
3519 : // humidity for the day will be constant, using the drybulb (max) and humidity indicator temperature to
3520 : // set the values. For the scheduled values, these are already set in the DDxxx array.
3521 :
3522 517 : state.dataGlobal->CurrentTime = 25.0;
3523 : Real64 HumidityRatio; // Humidity Ratio -- when constant for day
3524 : bool ConstantHumidityRatio;
3525 :
3526 517 : switch (desDayInput.HumIndType) {
3527 514 : case DesDayHumIndType::WetBulb: {
3528 514 : HumidityRatio = Psychrometrics::PsyWFnTdbTwbPb(
3529 : state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdbTwbPb);
3530 514 : ConstantHumidityRatio = true;
3531 514 : } break;
3532 0 : case DesDayHumIndType::DewPoint: {
3533 0 : HumidityRatio = Psychrometrics::PsyWFnTdpPb(state, desDayInput.HumIndValue, desDayInput.PressBarom, RoutineNamePsyWFnTdpPb);
3534 0 : ConstantHumidityRatio = true;
3535 0 : } break;
3536 0 : case DesDayHumIndType::HumRatio: {
3537 0 : HumidityRatio = desDayInput.HumIndValue;
3538 0 : ConstantHumidityRatio = true;
3539 0 : } break;
3540 1 : case DesDayHumIndType::Enthalpy: {
3541 : // HumIndValue is already in J/kg, so no conversions needed
3542 1 : HumidityRatio = Psychrometrics::PsyWFnTdbH(state, desDayInput.MaxDryBulb, desDayInput.HumIndValue, RoutineNamePsyWFnTdbH);
3543 1 : ConstantHumidityRatio = true;
3544 1 : } break;
3545 0 : case DesDayHumIndType::RelHumSch: {
3546 : // nothing to do -- DDHumIndModifier already contains the scheduled Relative Humidity
3547 0 : ConstantHumidityRatio = false;
3548 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr)
3549 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts)
3550 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).OutRelHum =
3551 0 : state.dataWeather->desDayMods(EnvrnNum)(ts + 1, hr + 1).OutRelHum;
3552 0 : } break;
3553 2 : case DesDayHumIndType::WBProfDef:
3554 : case DesDayHumIndType::WBProfDif:
3555 : case DesDayHumIndType::WBProfMul: {
3556 2 : ConstantHumidityRatio = false;
3557 2 : } break;
3558 0 : default: {
3559 0 : ShowSevereError(state, "SetUpDesignDay: Invalid Humidity Indicator type");
3560 0 : ShowContinueError(state, format("Occurred in Design Day={}", desDayInput.Title));
3561 0 : } break;
3562 : } // switch
3563 :
3564 : int OSky; // Opaque Sky Cover (tenths)
3565 517 : if (desDayInput.RainInd != 0) {
3566 0 : OSky = 10;
3567 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3568 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3569 0 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3570 0 : wvars.IsRain = true;
3571 0 : wvars.LiquidPrecip = 3.0;
3572 : }
3573 : }
3574 : } else {
3575 517 : OSky = 0;
3576 12925 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3577 80424 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3578 68016 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3579 68016 : wvars.IsRain = false;
3580 68016 : wvars.LiquidPrecip = 0.0;
3581 : }
3582 : }
3583 : }
3584 :
3585 : Real64 GndReflet; // Ground Reflectivity
3586 517 : if (desDayInput.SnowInd == 0) {
3587 517 : GndReflet = 0.2;
3588 12925 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3589 80424 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3590 68016 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3591 68016 : wvars.IsSnow = false;
3592 : }
3593 : }
3594 : } else { // Snow
3595 0 : GndReflet = 0.7;
3596 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3597 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3598 0 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3599 0 : wvars.IsSnow = true;
3600 : }
3601 : }
3602 : }
3603 :
3604 : // Some values are constant
3605 :
3606 12925 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3607 80424 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3608 68016 : auto &wvars = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3609 68016 : wvars.OutBaroPress = desDayInput.PressBarom;
3610 68016 : wvars.WindSpeed = desDayInput.WindSpeed;
3611 68016 : wvars.WindDir = desDayInput.WindDir;
3612 68016 : wvars.Albedo = 0.0;
3613 : }
3614 : }
3615 :
3616 : // resolve daily ranges
3617 : Real64 DBRange; // working copy of dry-bulb daily range, C (or 1 if input is difference)
3618 517 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
3619 0 : DBRange = 1.0; // use unscaled multiplier values if difference
3620 517 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
3621 0 : DBRange = 0.0;
3622 : } else {
3623 517 : DBRange = desDayInput.DailyDBRange;
3624 : }
3625 : Real64 WBRange; // working copy of wet-bulb daily range. C (or 1 if input is difference)
3626 517 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
3627 0 : WBRange = 1.0; // use unscaled multiplier values if difference
3628 : } else {
3629 517 : WBRange = desDayInput.DailyWBRange;
3630 : }
3631 :
3632 517 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3633 12925 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3634 80424 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
3635 68016 : auto const &desDayModsTS = desDayModsEnvrn(ts, hour);
3636 68016 : auto &tomorrowTs = state.dataWeather->wvarsHrTsTomorrow(ts, hour);
3637 68016 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
3638 : // dry-bulb profile
3639 68016 : tomorrowTs.OutDryBulbTemp = desDayInput.MaxDryBulb - desDayModsTS.OutDryBulbTemp * DBRange;
3640 : } else { // DesDayInput(EnvrnNum)%DBTempRangeType == DesDayDryBulbRangeType::Profile
3641 0 : tomorrowTs.OutDryBulbTemp = desDayModsTS.OutDryBulbTemp;
3642 : }
3643 :
3644 : // wet-bulb - generate from profile, humidity ratio, or dew point
3645 68016 : if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef || desDayInput.HumIndType == DesDayHumIndType::WBProfDif ||
3646 67824 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul) {
3647 192 : Real64 WetBulb = desDayInput.HumIndValue - desDayModsTS.OutRelHum * WBRange;
3648 192 : WetBulb = min(WetBulb, tomorrowTs.OutDryBulbTemp); // WB must be <= DB
3649 192 : Real64 OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3650 192 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3651 192 : tomorrowTs.OutRelHum =
3652 192 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3653 68016 : } else if (ConstantHumidityRatio) {
3654 : // Need Dew Point Temperature. Use Relative Humidity to get Humidity Ratio, unless Humidity Ratio is constant
3655 : // BG 9-26-07 moved following inside this IF statment; when HumIndType is 'Schedule' HumidityRatio wasn't being initialized
3656 : Real64 WetBulb =
3657 67824 : Psychrometrics::PsyTwbFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, RoutineNameLong);
3658 :
3659 67824 : Real64 OutHumRat = Psychrometrics::PsyWFnTdpPb(state, tomorrowTs.OutDryBulbTemp, desDayInput.PressBarom);
3660 67824 : if (HumidityRatio > OutHumRat) {
3661 18445 : WetBulb = tomorrowTs.OutDryBulbTemp;
3662 : } else {
3663 49379 : OutHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, tomorrowTs.OutDryBulbTemp, WetBulb, desDayInput.PressBarom);
3664 : }
3665 67824 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, OutHumRat, desDayInput.PressBarom);
3666 67824 : tomorrowTs.OutRelHum =
3667 67824 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, OutHumRat, desDayInput.PressBarom, WeatherManager) * 100.0;
3668 : } else {
3669 : HumidityRatio =
3670 0 : Psychrometrics::PsyWFnTdbRhPb(state, tomorrowTs.OutDryBulbTemp, desDayModsTS.OutRelHum / 100.0, desDayInput.PressBarom);
3671 0 : tomorrowTs.OutRelHum =
3672 0 : Psychrometrics::PsyRhFnTdbWPb(state, tomorrowTs.OutDryBulbTemp, HumidityRatio, desDayInput.PressBarom, WeatherManager) *
3673 : 100.0;
3674 : // TomorrowOutRelHum values set earlier
3675 0 : tomorrowTs.OutDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, HumidityRatio, desDayInput.PressBarom);
3676 : }
3677 :
3678 68016 : double DryBulb = tomorrowTs.OutDryBulbTemp;
3679 68016 : double RelHum = tomorrowTs.OutRelHum * 0.01;
3680 : Real64 ESky =
3681 68016 : CalcSkyEmissivity(state, envCurr.skyTempModel, OSky, DryBulb, tomorrowTs.OutDewPointTemp, RelHum); // Emissivitity of Sky
3682 68016 : tomorrowTs.HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
3683 :
3684 68016 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
3685 68016 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
3686 : // Design day not scheduled
3687 68016 : tomorrowTs.SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
3688 : }
3689 : // Generate solar values for timestep
3690 : // working results = BeamRad and DiffRad
3691 : // stored to program globals at end of loop
3692 : Real64 BeamRad;
3693 : Real64 DiffRad;
3694 68016 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
3695 : // scheduled: set value unconditionally (whether sun up or not)
3696 0 : BeamRad = desDayModsTS.BeamSolarRad;
3697 0 : DiffRad = desDayModsTS.DifSolarRad;
3698 : } else {
3699 :
3700 : // calc time = fractional hour of day
3701 : Real64 CurTime;
3702 68016 : if (state.dataGlobal->TimeStepsInHour != 1) {
3703 67392 : CurTime = double(hour - 1) + double(ts) * state.dataWeather->TimeStepFraction;
3704 : } else {
3705 624 : CurTime = double(hour) + state.dataEnvrn->TS1TimeOffset;
3706 : }
3707 :
3708 68016 : Vector3<Real64> SUNCOS; // Sun direction cosines
3709 68016 : CalculateSunDirectionCosines(
3710 : state, CurTime, designDay.EquationOfTime, designDay.SinSolarDeclinAngle, designDay.CosSolarDeclinAngle, SUNCOS);
3711 68016 : Real64 CosZenith = SUNCOS.z; // Cosine of Zenith Angle of Sun
3712 68016 : if (CosZenith < DataEnvironment::SunIsUpValue) {
3713 34134 : BeamRad = 0.0;
3714 34134 : DiffRad = 0.0;
3715 : } else {
3716 33882 : Real64 SinSolarAltitude = SUNCOS.z;
3717 :
3718 33882 : switch (desDayInput.solarModel) {
3719 31727 : case DesDaySolarModel::ASHRAE_ClearSky: {
3720 31727 : Real64 Exponent = B / CosZenith;
3721 : Real64 TotHoriz; // Total Radiation on Horizontal Surface
3722 31727 : if (Exponent > 700.0) {
3723 4 : TotHoriz = 0.0;
3724 : } else {
3725 31723 : TotHoriz = desDayInput.SkyClear * A * (C + CosZenith) * std::exp(-B / CosZenith);
3726 : }
3727 : // Radiation on an extraterrestial horizontal surface
3728 31727 : Real64 HO = GlobalSolarConstant * AVSC * CosZenith;
3729 31727 : Real64 KT = TotHoriz / HO; // Radiation ratio
3730 31727 : KT = min(KT, 0.75);
3731 31727 : DiffRad = TotHoriz * (1.0045 + KT * (0.04349 + KT * (-3.5227 + 2.6313 * KT)));
3732 31727 : if (desDayInput.SkyClear > 0.70) DiffRad = TotHoriz * C / (C + CosZenith);
3733 31727 : BeamRad = (TotHoriz - DiffRad) / CosZenith;
3734 31727 : DiffRad = max(0.0, DiffRad);
3735 31727 : BeamRad = max(0.0, BeamRad);
3736 :
3737 31727 : } break;
3738 2155 : case DesDaySolarModel::ASHRAE_Tau:
3739 : case DesDaySolarModel::ASHRAE_Tau2017: {
3740 2155 : Real64 ETR = GlobalSolarConstant * AVSC; // radiation of an extraterrestrial normal surface, W/m2
3741 : Real64 GloHorzRad;
3742 2155 : ASHRAETauModel(
3743 : state, desDayInput.solarModel, ETR, CosZenith, desDayInput.TauB, desDayInput.TauD, BeamRad, DiffRad, GloHorzRad);
3744 2155 : } break;
3745 0 : case DesDaySolarModel::Zhang_Huang: {
3746 0 : int Hour3Ago = mod(hour + 20, 24) + 1; // hour 3 hours before
3747 0 : Real64 const TotSkyCover = max(1.0 - desDayInput.SkyClear, 0.0);
3748 0 : Real64 GloHorzRad = (ZHGlobalSolarConstant * SinSolarAltitude *
3749 0 : (ZhangHuang_C0 + ZhangHuang_C1 * TotSkyCover + ZhangHuang_C2 * pow_2(TotSkyCover) +
3750 0 : ZhangHuang_C3 * (tomorrowTs.OutDryBulbTemp -
3751 0 : state.dataWeather->wvarsHrTsTomorrow(ts, Hour3Ago).OutDryBulbTemp) +
3752 0 : ZhangHuang_C4 * tomorrowTs.OutRelHum + ZhangHuang_C5 * tomorrowTs.WindSpeed) +
3753 : ZhangHuang_D) /
3754 0 : ZhangHuang_K;
3755 0 : GloHorzRad = max(GloHorzRad, 0.0);
3756 0 : Real64 ClearnessIndex_kt = GloHorzRad / (GlobalSolarConstant * SinSolarAltitude);
3757 : // ClearnessIndex_kt=DesDayInput(EnvrnNum)%SkyClear
3758 0 : Real64 ClearnessIndex_ktc = 0.4268 + 0.1934 * SinSolarAltitude;
3759 : Real64 ClearnessIndex_kds;
3760 0 : if (ClearnessIndex_kt < ClearnessIndex_ktc) {
3761 0 : ClearnessIndex_kds = (3.996 - 3.862 * SinSolarAltitude + 1.54 * pow_2(SinSolarAltitude)) * pow_3(ClearnessIndex_kt);
3762 : } else {
3763 0 : ClearnessIndex_kds = ClearnessIndex_kt - (1.107 + 0.03569 * SinSolarAltitude + 1.681 * pow_2(SinSolarAltitude)) *
3764 0 : pow_3(1.0 - ClearnessIndex_kt);
3765 : }
3766 : // Calculate direct normal radiation, W/m2
3767 0 : BeamRad = ZHGlobalSolarConstant * SinSolarAltitude * ClearnessIndex_kds *
3768 0 : ((1.0 - ClearnessIndex_kt) / (1.0 - ClearnessIndex_kds));
3769 : // Calculation diffuse horizontal radiation, W/m2
3770 0 : DiffRad =
3771 0 : ZHGlobalSolarConstant * SinSolarAltitude * ((ClearnessIndex_kt - ClearnessIndex_kds) / (1.0 - ClearnessIndex_kds));
3772 :
3773 0 : } break;
3774 0 : default:
3775 0 : break;
3776 : }
3777 : }
3778 68016 : }
3779 :
3780 : // override result to 0 per environment var (for testing)
3781 68016 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreBeamRadiation) BeamRad = 0.0;
3782 68016 : if (state.dataEnvrn->IgnoreSolarRadiation || state.dataEnvrn->IgnoreDiffuseRadiation) DiffRad = 0.0;
3783 :
3784 68016 : tomorrowTs.BeamSolarRad = BeamRad;
3785 68016 : tomorrowTs.DifSolarRad = DiffRad;
3786 :
3787 : } // Timestep (TS) Loop
3788 : } // Hour Loop
3789 :
3790 : // back-fill hour values from timesteps
3791 : // hour values = integrated over hour ending at time of hour
3792 : // insurance: hourly values not known to be needed
3793 12925 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
3794 12408 : int Hour1Ago = mod(hour + 22, Constant::iHoursInDay) + 1;
3795 12408 : auto const &tomorrowHr = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, hour);
3796 12408 : auto const &tomorrowHr1Ago = state.dataWeather->wvarsHrTsTomorrow(state.dataGlobal->TimeStepsInHour, Hour1Ago);
3797 :
3798 12408 : Real64 BeamRad = (tomorrowHr1Ago.BeamSolarRad + tomorrowHr.BeamSolarRad) / 2.0;
3799 12408 : Real64 DiffRad = (tomorrowHr1Ago.DifSolarRad + tomorrowHr.DifSolarRad) / 2.0;
3800 12408 : if (state.dataGlobal->TimeStepsInHour > 1) {
3801 67392 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour - 1; ++iTS) {
3802 55608 : BeamRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).BeamSolarRad;
3803 55608 : DiffRad += state.dataWeather->wvarsHrTsTomorrow(iTS, hour).DifSolarRad;
3804 : }
3805 : }
3806 12408 : Wthr.BeamSolarRad(hour) = BeamRad / state.dataGlobal->TimeStepsInHour;
3807 12408 : Wthr.DifSolarRad(hour) = DiffRad / state.dataGlobal->TimeStepsInHour;
3808 : }
3809 :
3810 517 : if (envCurr.WP_Type1 != 0) {
3811 :
3812 0 : switch (state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel) {
3813 0 : case SkyTempModel::ScheduleValue: {
3814 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3815 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3816 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3817 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3818 0 : state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1).SkyTemp = desDayModsEnvrn(ts + 1, hr + 1).SkyTemp =
3819 0 : dayVals[hr * state.dataGlobal->TimeStepsInHour];
3820 : }
3821 : }
3822 0 : } break;
3823 :
3824 0 : case SkyTempModel::DryBulbDelta: {
3825 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3826 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3827 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3828 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3829 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3830 0 : desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3831 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDryBulbTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3832 : }
3833 : }
3834 0 : } break;
3835 :
3836 0 : case SkyTempModel::DewPointDelta: {
3837 0 : std::vector<Real64> const &dayVals = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).sched->getDayVals(state);
3838 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
3839 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
3840 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
3841 0 : auto &tomorrowTS = state.dataWeather->wvarsHrTsTomorrow(ts + 1, hr + 1);
3842 0 : desDayModsEnvrn(ts + 1, hr + 1).SkyTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3843 0 : tomorrowTS.SkyTemp = tomorrowTS.OutDewPointTemp - dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
3844 : }
3845 : }
3846 0 : } break;
3847 :
3848 0 : default: {
3849 0 : } break;
3850 : } // switch (skyTempModel)
3851 : } // if (envCurr.WP_Type1 != 0)
3852 :
3853 517 : state.dataGlobal->WarmupFlag = SaveWarmupFlag;
3854 517 : }
3855 :
3856 2159 : Real64 AirMass(Real64 const CosZen) // COS( solar zenith), 0 - 1
3857 : {
3858 :
3859 : // SUBROUTINE INFORMATION:
3860 : // AUTHOR C Barnaby
3861 : // DATE WRITTEN Nov 2010
3862 :
3863 : // PURPOSE OF THIS SUBROUTINE:
3864 : // Calculate relative air mass using Kasten and Young approximation
3865 :
3866 : // METHODOLOGY EMPLOYED:
3867 : // Eqn (16), ASHRAE HOF 2009, p. 14.9
3868 :
3869 : // REFERENCES:
3870 : // ASHRAE HOF 2009 Chapter 14
3871 : // Kasten, F and T. Young. 1989. Revised optical air mass tables
3872 : // and approximating formula. Applied Optics 28:4735-4738.
3873 :
3874 : Real64 AirMass;
3875 : Real64 SunAltD;
3876 :
3877 2159 : if (CosZen <= 0.001) {
3878 5 : AirMass = 37.07837343; // limit value calc'd with Excel
3879 : // value increases little as CosZen -> 0
3880 2154 : } else if (CosZen >= 1.0) {
3881 4 : AirMass = 1.0;
3882 : } else {
3883 : // note: COS( Zen) = SIN( Alt)
3884 2150 : SunAltD = std::asin(CosZen) / Constant::DegToRad; // altitude, degrees
3885 2150 : AirMass = 1.0 / (CosZen + 0.50572 * std::pow(6.07995 + SunAltD, -1.6364));
3886 : }
3887 2159 : return AirMass;
3888 : }
3889 :
3890 : //------------------------------------------------------------------------------
3891 :
3892 2157 : void ASHRAETauModel([[maybe_unused]] EnergyPlusData &state,
3893 : DesDaySolarModel const TauModel, // ASHRAETau solar model type ASHRAE_Tau or ASHRAE_Tau2017
3894 : Real64 const ETR, // extraterrestrial normal irradiance, W/m2
3895 : Real64 const CosZen, // COS( solar zenith angle), 0 - 1
3896 : Real64 const TauB, // beam tau factor
3897 : Real64 const TauD, // dif tau factor
3898 : Real64 &IDirN, // returned: direct (beam) irradiance on normal surface, W/m2
3899 : Real64 &IDifH, // returned: diffuse irradiance on horiz surface, W/m2
3900 : Real64 &IGlbH // returned: global irradiance on horiz surface, W/m2
3901 : )
3902 : {
3903 :
3904 : // SUBROUTINE INFORMATION:
3905 : // AUTHOR C Barnaby
3906 : // DATE WRITTEN Nov 2010
3907 :
3908 : // PURPOSE OF THIS SUBROUTINE:
3909 : // Calculate clear-sky direct and diffuse irradiance using ASHRAE "tau" model
3910 :
3911 : // METHODOLOGY EMPLOYED:
3912 : // Eqns (17-18), ASHRAE HOF 2009, p. 14.9
3913 : // Eqns (19-20), ASHRAE HOF 2013 p. 14.9 and 2017 p. 14.10
3914 :
3915 : // REFERENCES:
3916 : // ASHRAE HOF 2009 Chapter 14
3917 :
3918 : Real64 AB; // air mass exponents
3919 : Real64 AD;
3920 : Real64 M; // air mass
3921 :
3922 2157 : if (CosZen < DataEnvironment::SunIsUpValue || TauB <= 0.0 || TauD <= 0.0) {
3923 0 : IDirN = 0.0;
3924 0 : IDifH = 0.0;
3925 0 : IGlbH = 0.0;
3926 : } else {
3927 2157 : if (TauModel == DesDaySolarModel::ASHRAE_Tau) {
3928 2045 : AB = 1.219 - 0.043 * TauB - 0.151 * TauD - 0.204 * TauB * TauD;
3929 2045 : AD = 0.202 + 0.852 * TauB - 0.007 * TauD - 0.357 * TauB * TauD;
3930 : } else {
3931 : // TauModelType == ASHRAE_Tau2017
3932 112 : AB = 1.454 - 0.406 * TauB - 0.268 * TauD + 0.021 * TauB * TauD;
3933 112 : AD = 0.507 + 0.205 * TauB - 0.080 * TauD - 0.190 * TauB * TauD;
3934 : }
3935 2157 : M = AirMass(CosZen);
3936 2157 : IDirN = ETR * std::exp(-TauB * std::pow(M, AB));
3937 2157 : IDifH = ETR * std::exp(-TauD * std::pow(M, AD));
3938 2157 : IGlbH = IDirN * CosZen + IDifH;
3939 : }
3940 2157 : }
3941 :
3942 117 : void AllocateWeatherData(EnergyPlusData &state)
3943 : {
3944 :
3945 : // SUBROUTINE INFORMATION:
3946 : // AUTHOR Linda Lawrie
3947 : // DATE WRITTEN December 2000
3948 :
3949 : // PURPOSE OF THIS SUBROUTINE:
3950 : // This subroutine allocates the weather data structures (Today, Tomorrow,
3951 : // Design Day) to the proper number of "time steps in hour" requested by the user.
3952 : // Interpolation of data is done later after either setting up the design day (hourly
3953 : // data) or reading in hourly weather data.
3954 :
3955 117 : state.dataWeather->wvarsHrTsToday.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
3956 117 : state.dataWeather->wvarsHrTsTomorrow.allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
3957 117 : }
3958 :
3959 1272 : void CalculateDailySolarCoeffs(EnergyPlusData const &state,
3960 : int const DayOfYear, // Day of year (1 - 366)
3961 : Real64 &A, // ASHRAE "A" - Apparent solar irradiation at air mass = 0 [W/M**2]
3962 : Real64 &B, // ASHRAE "B" - Atmospheric extinction coefficient
3963 : Real64 &C, // ASHRAE "C" - Diffuse radiation factor
3964 : Real64 &AnnVarSolConstant, // Annual variation in the solar constant
3965 : Real64 &EquationOfTime, // Equation of Time
3966 : Real64 &SineSolarDeclination, // Sine of Solar Declination
3967 : Real64 &CosineSolarDeclination // Cosine of Solar Declination
3968 : )
3969 : {
3970 :
3971 : // SUBROUTINE INFORMATION:
3972 : // AUTHOR George Walton
3973 : // DATE WRITTEN May 1985
3974 : // MODIFIED 1999 for EnergyPlus
3975 : // RE-ENGINEERED 2001; LKL; Remove need for English -> SI conversion
3976 : // Implement Tarp "fix" for Southern Hemisphere
3977 :
3978 : // PURPOSE OF THIS SUBROUTINE:
3979 : // This subroutine computes the daily solar coefficients used in other
3980 : // calculations. Specifically, this routine computes values of the solar declination, equation
3981 : // of time, and ashrae sky coefficients a, b, and c for a given
3982 : // day of the year.
3983 :
3984 : // METHODOLOGY EMPLOYED:
3985 : // The method is the same as that recommended in the ASHRAE loads
3986 : // algorithms manual, except that the fourier series expressions
3987 : // have been extended by two terms for greater accuracy.
3988 : // coefficients for the new expressions were determined at USACERL
3989 : // using data from the cited references.
3990 :
3991 : // REFERENCES:
3992 : // J. L. Threlkeld, "Thermal Environmental Engineering", 1970,
3993 : // p.316, for declination and equation of time.
3994 : // "ASHRAE Handbook of Fundamentals", 1972, p.387 for sky
3995 : // coefficients a, b, and c.
3996 : // See SUN3 in SolarShading. See SUN2 in BLAST. See SUN3 in Tarp.
3997 :
3998 1272 : Real64 const DayCorrection(Constant::Pi * 2.0 / 366.0);
3999 :
4000 : // Fitted coefficients of Fourier series | Sine of declination coefficients
4001 : static constexpr std::array<Real64, 9> SineSolDeclCoef = {
4002 : 0.00561800, 0.0657911, -0.392779, 0.00064440, -0.00618495, -0.00010101, -0.00007951, -0.00011691, 0.00002096};
4003 : // Fitted coefficients of Fourier Series | Equation of Time coefficients
4004 : static constexpr std::array<Real64, 9> EqOfTimeCoef = {
4005 : 0.00021971, -0.122649, 0.00762856, -0.156308, -0.0530028, -0.00388702, -0.00123978, -0.00270502, -0.00167992};
4006 : // Fitted coefficients of Fourier Series | ASHRAE A Factor coefficients
4007 : 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};
4008 : // Fitted coefficients of Fourier Series | ASHRAE B Factor coefficients
4009 : static constexpr std::array<Real64, 9> ASHRAE_B_Coef = {
4010 : 0.171631, -0.00400448, -0.0344923, 0.00000209, 0.00325428, -0.00085429, 0.00229562, 0.0009034, -0.0011867};
4011 : // Fitted coefficients of Fourier Series | ASHRAE C Factor coefficients
4012 : static constexpr std::array<Real64, 9> ASHRAE_C_Coef = {
4013 : 0.0905151, -0.00322522, -0.0407966, 0.000104164, 0.00745899, -0.00086461, 0.0013111, 0.000808275, -0.00170515};
4014 :
4015 : // Day of Year in Radians (Computed from Input DayOfYear)
4016 1272 : Real64 X = DayCorrection * DayOfYear; // Convert Julian date (Day of Year) to angle X
4017 :
4018 : // Calculate sines and cosines of X
4019 1272 : Real64 SinX = std::sin(X);
4020 1272 : Real64 CosX = std::cos(X);
4021 :
4022 1272 : SineSolarDeclination = SineSolDeclCoef[0] + SineSolDeclCoef[1] * SinX + SineSolDeclCoef[2] * CosX + SineSolDeclCoef[3] * (SinX * CosX * 2.0) +
4023 1272 : SineSolDeclCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4024 1272 : SineSolDeclCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4025 1272 : SineSolDeclCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4026 1272 : SineSolDeclCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4027 1272 : SineSolDeclCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4028 1272 : CosineSolarDeclination = std::sqrt(1.0 - pow_2(SineSolarDeclination));
4029 :
4030 1272 : EquationOfTime = EqOfTimeCoef[0] + EqOfTimeCoef[1] * SinX + EqOfTimeCoef[2] * CosX + EqOfTimeCoef[3] * (SinX * CosX * 2.0) +
4031 1272 : EqOfTimeCoef[4] * (pow_2(CosX) - pow_2(SinX)) +
4032 1272 : EqOfTimeCoef[5] * (SinX * (pow_2(CosX) - pow_2(SinX)) + CosX * (SinX * CosX * 2.0)) +
4033 1272 : EqOfTimeCoef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4034 1272 : EqOfTimeCoef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4035 1272 : EqOfTimeCoef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4036 :
4037 1272 : AnnVarSolConstant = 1.000047 + 0.000352615 * SinX + 0.0334454 * CosX;
4038 :
4039 1272 : A = ASHRAE_A_Coef[0] + ASHRAE_A_Coef[1] * SinX + ASHRAE_A_Coef[2] * CosX + ASHRAE_A_Coef[3] * (SinX * CosX * 2.0) +
4040 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)) +
4041 1272 : ASHRAE_A_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4042 1272 : ASHRAE_A_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4043 1272 : ASHRAE_A_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4044 :
4045 : // Compute B and C coefficients
4046 :
4047 1272 : if (state.dataEnvrn->Latitude < 0.0) {
4048 : // If in southern hemisphere, compute B and C with a six month time shift.
4049 0 : X -= Constant::Pi;
4050 0 : SinX = std::sin(X);
4051 0 : CosX = std::cos(X);
4052 : }
4053 :
4054 1272 : B = ASHRAE_B_Coef[0] + ASHRAE_B_Coef[1] * SinX + ASHRAE_B_Coef[2] * CosX + ASHRAE_B_Coef[3] * (SinX * CosX * 2.0) +
4055 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)) +
4056 1272 : ASHRAE_B_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4057 1272 : ASHRAE_B_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4058 1272 : ASHRAE_B_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4059 :
4060 1272 : C = ASHRAE_C_Coef[0] + ASHRAE_C_Coef[1] * SinX + ASHRAE_C_Coef[2] * CosX + ASHRAE_C_Coef[3] * (SinX * CosX * 2.0) +
4061 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)) +
4062 1272 : ASHRAE_C_Coef[6] * (CosX * (pow_2(CosX) - pow_2(SinX)) - SinX * (SinX * CosX * 2.0)) +
4063 1272 : ASHRAE_C_Coef[7] * (2.0 * (SinX * CosX * 2.0) * (pow_2(CosX) - pow_2(SinX))) +
4064 1272 : ASHRAE_C_Coef[8] * (pow_2(pow_2(CosX) - pow_2(SinX)) - pow_2(SinX * CosX * 2.0));
4065 1272 : }
4066 :
4067 68016 : void CalculateSunDirectionCosines(EnergyPlusData const &state,
4068 : Real64 const TimeValue, // Current Time of Day
4069 : Real64 const EqOfTime, // Equation of Time
4070 : Real64 const SinSolDeclin, // Sine of Solar Declination
4071 : Real64 const CosSolDeclin, // Cosine of Solar Declination
4072 : Vector3<Real64> &SUNCOS)
4073 : {
4074 :
4075 : // SUBROUTINE INFORMATION:
4076 : // AUTHOR George Walton
4077 : // DATE WRITTEN May 1975
4078 : // MODIFIED 1999 for EnergyPlus
4079 :
4080 : // PURPOSE OF THIS SUBROUTINE:
4081 : // This routine computes the solar direction cosines for hourly
4082 : // radiation calculations.
4083 :
4084 : // REFERENCES:
4085 : // "NECAP Engineering Manual", 1974, p.3-117
4086 :
4087 68016 : EP_SIZE_CHECK(SUNCOS, 3); // NOLINT(misc-static-assert)
4088 :
4089 : // COMPUTE THE HOUR ANGLE
4090 68016 : Real64 H = (15.0 * (12.0 - (TimeValue + EqOfTime)) + (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude)) * Constant::DegToRad;
4091 68016 : Real64 COSH = std::cos(H);
4092 : // COMPUTE THE COSINE OF THE SOLAR ZENITH ANGLE.
4093 : // This is also the Sine of the Solar Altitude Angle
4094 :
4095 68016 : SUNCOS.z = SinSolDeclin * state.dataEnvrn->SinLatitude + CosSolDeclin * state.dataEnvrn->CosLatitude * COSH;
4096 :
4097 68016 : if (SUNCOS.z >= DataEnvironment::SunIsUpValue) { // If Sun above horizon, compute other direction cosines
4098 33882 : SUNCOS.y = SinSolDeclin * state.dataEnvrn->CosLatitude - CosSolDeclin * state.dataEnvrn->SinLatitude * COSH;
4099 33882 : SUNCOS.x = CosSolDeclin * std::sin(H);
4100 : } else { // Sun is down, set to 0.0
4101 34134 : SUNCOS.x = 0.0;
4102 34134 : SUNCOS.y = 0.0;
4103 : }
4104 68016 : }
4105 :
4106 326122 : void DetermineSunUpDown(EnergyPlusData &state, Vector3<Real64> &SunCOS)
4107 : {
4108 :
4109 : // SUBROUTINE INFORMATION:
4110 : // AUTHOR Linda Lawrie
4111 : // DATE WRITTEN 1999
4112 :
4113 : // PURPOSE OF THIS SUBROUTINE:
4114 : // This subroutine determines if the sun is up or down for the current
4115 : // hour/timestep.
4116 :
4117 : // REFERENCES:
4118 : // Sun routines from IBLAST, authored by Walton.
4119 :
4120 : // COMPUTE THE HOUR ANGLE
4121 326122 : if (state.dataGlobal->TimeStepsInHour != 1) {
4122 643576 : state.dataWeather->HrAngle = (15.0 * (12.0 - (state.dataGlobal->CurrentTime + state.dataWeather->TodayVariables.EquationOfTime)) +
4123 321788 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4124 : } else {
4125 4334 : state.dataWeather->HrAngle =
4126 4334 : (15.0 *
4127 4334 : (12.0 - ((state.dataGlobal->CurrentTime + state.dataEnvrn->TS1TimeOffset) + state.dataWeather->TodayVariables.EquationOfTime)) +
4128 4334 : (state.dataEnvrn->TimeZoneMeridian - state.dataEnvrn->Longitude));
4129 : }
4130 326122 : Real64 H = state.dataWeather->HrAngle * Constant::DegToRad;
4131 :
4132 : // Compute the Cosine of the Solar Zenith (Altitude) Angle.
4133 326122 : Real64 CosZenith = state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle +
4134 326122 : state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H);
4135 :
4136 326122 : Real64 SolarZenith = std::acos(CosZenith);
4137 326122 : Real64 SinAltitude = state.dataEnvrn->CosLatitude * state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::cos(H) +
4138 326122 : state.dataEnvrn->SinLatitude * state.dataWeather->TodayVariables.SinSolarDeclinAngle;
4139 326122 : Real64 SolarAltitude = std::asin(SinAltitude);
4140 326122 : Real64 CosAzimuth = -(state.dataEnvrn->SinLatitude * CosZenith - state.dataWeather->TodayVariables.SinSolarDeclinAngle) /
4141 326122 : (state.dataEnvrn->CosLatitude * std::sin(SolarZenith));
4142 : // Following because above can yield invalid cos value. (e.g. at south pole)
4143 326122 : CosAzimuth = max(CosAzimuth, -1.0);
4144 326122 : CosAzimuth = min(1.0, CosAzimuth);
4145 326122 : Real64 SolarAzimuth = std::acos(CosAzimuth);
4146 :
4147 326122 : state.dataWeather->SolarAltitudeAngle = SolarAltitude / Constant::DegToRad;
4148 326122 : state.dataWeather->SolarAzimuthAngle = SolarAzimuth / Constant::DegToRad;
4149 326122 : if (state.dataWeather->HrAngle < 0.0) {
4150 163219 : state.dataWeather->SolarAzimuthAngle = 360.0 - state.dataWeather->SolarAzimuthAngle;
4151 : }
4152 :
4153 326122 : SunCOS.z = CosZenith;
4154 326122 : state.dataEnvrn->SunIsUpPrevTS = state.dataEnvrn->SunIsUp;
4155 326122 : if (CosZenith < DataEnvironment::SunIsUpValue) {
4156 166429 : state.dataEnvrn->SunIsUp = false;
4157 166429 : SunCOS.y = 0.0;
4158 166429 : SunCOS.x = 0.0;
4159 : } else {
4160 159693 : state.dataEnvrn->SunIsUp = true;
4161 159693 : SunCOS.y = state.dataWeather->TodayVariables.SinSolarDeclinAngle * state.dataEnvrn->CosLatitude -
4162 159693 : state.dataWeather->TodayVariables.CosSolarDeclinAngle * state.dataEnvrn->SinLatitude * std::cos(H);
4163 159693 : SunCOS.x = state.dataWeather->TodayVariables.CosSolarDeclinAngle * std::sin(H);
4164 : }
4165 326122 : }
4166 :
4167 115 : void OpenWeatherFile(EnergyPlusData &state, bool &ErrorsFound)
4168 : {
4169 :
4170 : // SUBROUTINE INFORMATION:
4171 : // AUTHOR Linda Lawrie
4172 : // DATE WRITTEN June 1999
4173 :
4174 : // PURPOSE OF THIS SUBROUTINE:
4175 : // This subroutine checks to see if a weather file and what kind of weather file
4176 : // exists in the working directory and calls appropriate routines to
4177 : // open the files and set up for use.
4178 :
4179 115 : state.dataWeather->WeatherFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
4180 115 : if (state.dataWeather->WeatherFileExists) {
4181 29 : OpenEPlusWeatherFile(state, ErrorsFound, true);
4182 : }
4183 115 : }
4184 :
4185 73 : void OpenEPlusWeatherFile(EnergyPlusData &state,
4186 : bool &ErrorsFound, // Will be set to true if errors found
4187 : bool const ProcessHeader // Set to true when headers should be processed (rather than just read)
4188 : )
4189 : {
4190 :
4191 : // SUBROUTINE INFORMATION:
4192 : // AUTHOR Linda K. Lawrie
4193 : // DATE WRITTEN June 1999
4194 :
4195 : // PURPOSE OF THIS SUBROUTINE:
4196 : // This subroutine opens the EnergyPlus Weather File (in.epw) and processes
4197 : // the initial header records.
4198 :
4199 : // METHODOLOGY EMPLOYED:
4200 : // List directed reads, as possible.
4201 :
4202 73 : state.files.inputWeatherFile.close();
4203 73 : state.files.inputWeatherFile.filePath = state.files.inputWeatherFilePath.filePath;
4204 73 : state.files.inputWeatherFile.open();
4205 73 : if (!state.files.inputWeatherFile.good()) {
4206 0 : ShowFatalError(state, "OpenWeatherFile: Could not OPEN EPW Weather File", OptionalOutputFileRef(state.files.eso));
4207 : }
4208 :
4209 73 : if (ProcessHeader) {
4210 : // Read in Header Information
4211 :
4212 : // Headers should come in order
4213 261 : for (int typeNum = static_cast<int>(EpwHeaderType::Location); typeNum < static_cast<int>(EpwHeaderType::Num); ++typeNum) {
4214 232 : auto Line = state.files.inputWeatherFile.readLine();
4215 232 : if (Line.eof) {
4216 0 : ShowFatalError(
4217 : state,
4218 0 : format("OpenWeatherFile: Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}",
4219 0 : epwHeaders[typeNum]),
4220 0 : OptionalOutputFileRef(state.files.eso));
4221 : }
4222 :
4223 232 : int endcol = len(Line.data);
4224 232 : if (endcol > 0) {
4225 232 : if (int(Line.data[endcol - 1]) == DataSystemVariables::iUnicode_end) {
4226 0 : ShowSevereError(state,
4227 : "OpenWeatherFile: EPW Weather File appears to be a Unicode or binary file.",
4228 0 : OptionalOutputFileRef(state.files.eso));
4229 0 : ShowContinueError(state, "...This file cannot be read by this program. Please save as PC or Unix file and try again");
4230 0 : ShowFatalError(state, "Program terminates due to previous condition.");
4231 : }
4232 : }
4233 232 : std::string::size_type const Pos = FindNonSpace(Line.data);
4234 232 : std::string::size_type const HdPos = index(Line.data, epwHeaders[typeNum]);
4235 232 : if (Pos != HdPos) continue;
4236 232 : ProcessEPWHeader(state, static_cast<EpwHeaderType>(typeNum), Line.data, ErrorsFound);
4237 232 : }
4238 : } else { // Header already processed, just read
4239 44 : SkipEPlusWFHeader(state);
4240 : }
4241 73 : }
4242 :
4243 961 : void CloseWeatherFile(EnergyPlusData &state)
4244 : {
4245 961 : state.files.inputWeatherFile.close();
4246 961 : }
4247 :
4248 112 : void ResolveLocationInformation(EnergyPlusData &state, bool &ErrorsFound) // Set to true if no location evident
4249 : {
4250 :
4251 : // SUBROUTINE INFORMATION:
4252 : // AUTHOR Rick Strand
4253 : // DATE WRITTEN June 1997
4254 :
4255 : // PURPOSE OF THIS SUBROUTINE:
4256 : // This subroutine is currently the main interface between the old data
4257 : // structure on the BLAST Weather file and the new data structure contained
4258 : // in this module. At some point, this subroutine will be converted
4259 : // to read information directly from the new input file.
4260 :
4261 164 : if (state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather &&
4262 52 : state.dataWeather->WeatherFileExists) {
4263 27 : if (state.dataWeather->LocationGathered) {
4264 : // See if "matching" location
4265 26 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4266 32 : if (std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude) > 1.0 ||
4267 14 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude) > 1.0 ||
4268 39 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone) > 0.0 ||
4269 7 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) / max(state.dataEnvrn->Elevation, 1.0) >
4270 : 0.10) {
4271 36 : ShowWarningError(state, "Weather file location will be used rather than entered (IDF) Location object.");
4272 18 : ShowContinueError(state, format("..Location object={}", state.dataWeather->LocationTitle));
4273 18 : ShowContinueError(state, format("..Weather File Location={}", state.dataEnvrn->WeatherFileLocationTitle));
4274 36 : ShowContinueError(
4275 : state,
4276 36 : format("..due to location differences, Latitude difference=[{:.2R}] degrees, Longitude difference=[{:.2R}] degrees.",
4277 18 : std::abs(state.dataEnvrn->Latitude - state.dataWeather->WeatherFileLatitude),
4278 18 : std::abs(state.dataEnvrn->Longitude - state.dataWeather->WeatherFileLongitude)));
4279 36 : ShowContinueError(state,
4280 36 : format("..Time Zone difference=[{:.1R}] hour(s), Elevation difference=[{:.2R}] percent, [{:.2R}] meters.",
4281 18 : std::abs(state.dataEnvrn->TimeZoneNumber - state.dataWeather->WeatherFileTimeZone),
4282 36 : std::abs((state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation) /
4283 18 : max(state.dataEnvrn->Elevation, 1.0) * 100.0),
4284 36 : std::abs(state.dataEnvrn->Elevation - state.dataWeather->WeatherFileElevation)));
4285 : }
4286 : }
4287 : }
4288 27 : if (!state.dataWeather->keepUserSiteLocationDefinition) {
4289 26 : state.dataWeather->LocationTitle = state.dataEnvrn->WeatherFileLocationTitle;
4290 26 : state.dataEnvrn->Latitude = state.dataWeather->WeatherFileLatitude;
4291 26 : state.dataEnvrn->Longitude = state.dataWeather->WeatherFileLongitude;
4292 26 : state.dataEnvrn->TimeZoneNumber = state.dataWeather->WeatherFileTimeZone;
4293 26 : state.dataEnvrn->Elevation = state.dataWeather->WeatherFileElevation;
4294 : }
4295 85 : } else if (!state.dataWeather->LocationGathered) {
4296 1 : state.dataWeather->LocationTitle = "Not Entered";
4297 2 : ShowSevereError(state, "No Location given. Must have location information for simulation.");
4298 1 : ErrorsFound = true;
4299 : }
4300 :
4301 112 : if (!ErrorsFound) {
4302 111 : state.dataEnvrn->StdBaroPress = DataEnvironment::StdPressureSeaLevel * std::pow(1.0 - 2.25577e-05 * state.dataEnvrn->Elevation, 5.2559);
4303 222 : state.dataEnvrn->StdRhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(
4304 111 : state, state.dataEnvrn->StdBaroPress, DataPrecisionGlobals::constant_twenty, DataPrecisionGlobals::constant_zero);
4305 : // Write Final Location Information to the initialization output file
4306 : static constexpr std::string_view LocHdFormat(
4307 : "! <Site:Location>, Location Name, Latitude {N+/S- Deg}, Longitude {E+/W- Deg}, Time Zone Number "
4308 : "{GMT+/-}, Elevation {m}, Standard Pressure at Elevation {Pa}, Standard RhoAir at Elevation\n");
4309 111 : print(state.files.eio, "{}", LocHdFormat);
4310 :
4311 : static constexpr std::string_view LocFormat("Site:Location,{},{:.2R},{:.2R},{:.2R},{:.2R},{:.0R},{:.4R}\n");
4312 111 : print(state.files.eio,
4313 : LocFormat,
4314 111 : state.dataWeather->LocationTitle,
4315 111 : state.dataEnvrn->Latitude,
4316 111 : state.dataEnvrn->Longitude,
4317 111 : state.dataEnvrn->TimeZoneNumber,
4318 111 : state.dataEnvrn->Elevation,
4319 111 : state.dataEnvrn->StdBaroPress,
4320 111 : state.dataEnvrn->StdRhoAir);
4321 : }
4322 112 : }
4323 :
4324 113 : void CheckLocationValidity(EnergyPlusData &state)
4325 : {
4326 :
4327 : // SUBROUTINE INFORMATION:
4328 : // AUTHOR Rick Strand
4329 : // DATE WRITTEN June 1997
4330 :
4331 : // PURPOSE OF THIS SUBROUTINE:
4332 : // This subroutine is checks to see whether the user specified location
4333 : // or the weather file location (if one exists) is valid. The standard
4334 : // time meridian is also calculated and compared to the user supplied
4335 : // or weather file time zone number.
4336 :
4337 113 : bool LocationError = false; // Set to true if there is a problem detected
4338 :
4339 113 : if ((state.dataEnvrn->Latitude == -999.0) && (state.dataEnvrn->Longitude == -999.0) && (state.dataEnvrn->TimeZoneNumber != -999.0)) {
4340 0 : ShowSevereError(state, "No location specified");
4341 0 : LocationError = true;
4342 : }
4343 :
4344 113 : if ((state.dataEnvrn->Latitude < -90.0) || (state.dataEnvrn->Latitude > 90.0)) {
4345 0 : ShowSevereError(state, format("Latitude must be between -90 and 90; Entered={:.2R}", state.dataEnvrn->Latitude));
4346 0 : LocationError = true;
4347 : }
4348 :
4349 113 : if ((state.dataEnvrn->Longitude < -180.0) || (state.dataEnvrn->Longitude > 180.0)) {
4350 0 : ShowSevereError(state, format("Longitude must be between -180 and 180; Entered={:.2R}", state.dataEnvrn->Longitude));
4351 0 : LocationError = true;
4352 : }
4353 :
4354 113 : if ((state.dataEnvrn->TimeZoneNumber < -12.00) || (state.dataEnvrn->TimeZoneNumber > 14.00)) {
4355 0 : ShowSevereError(state, format("Time Zone must be between -12 and +14; Entered={:.2R}", state.dataEnvrn->TimeZoneNumber));
4356 0 : LocationError = true;
4357 : }
4358 :
4359 113 : Real64 const StdTimeMerid = GetSTM(state.dataEnvrn->Longitude); // Standard time meridian.
4360 :
4361 : // Compare the standard time meridian with the time zone number. If
4362 : // different, notify the user. If StdTimeMerid couldn't be calculated,
4363 : // produce an error message.
4364 :
4365 113 : if (state.dataEnvrn->varyingLocationLatSched != nullptr || state.dataEnvrn->varyingLocationLongSched != nullptr) {
4366 : // don't do any warnings, the building is moving
4367 113 : } else if (StdTimeMerid >= -12.0 && StdTimeMerid <= 12.0) {
4368 113 : if (state.dataEnvrn->TimeZoneNumber != StdTimeMerid) {
4369 : // Difference between Standard Time Meridian and TimeZone
4370 6 : Real64 const DiffCalc = std::abs(state.dataEnvrn->TimeZoneNumber - StdTimeMerid);
4371 6 : if (DiffCalc > 1.0 && DiffCalc < 24.0) {
4372 0 : if (DiffCalc < 3.0) {
4373 0 : ShowWarningError(state,
4374 0 : format("Standard Time Meridian and Time Zone differ by more than 1, Difference=\"{:.1R}\"", DiffCalc));
4375 0 : ShowContinueError(state, "Solar Positions may be incorrect");
4376 : } else {
4377 0 : ShowSevereError(state, format("Standard Time Meridian and Time Zone differ by more than 2, Difference=\"{:.1R}\"", DiffCalc));
4378 0 : ShowContinueError(state, "Solar Positions will be incorrect");
4379 : // LocationError=.TRUE.
4380 : }
4381 : }
4382 : }
4383 113 : } else {
4384 0 : ShowSevereError(state, "Unable to calculate the standard time meridian");
4385 0 : LocationError = true;
4386 : }
4387 :
4388 : // Error handling: if there are any errors in the location information
4389 : // the simulation must be terminated
4390 :
4391 113 : if (LocationError) {
4392 0 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4393 : }
4394 :
4395 113 : if (state.dataEnvrn->TimeZoneNumber <= 12.00) {
4396 113 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0;
4397 : } else {
4398 0 : state.dataEnvrn->TimeZoneMeridian = state.dataEnvrn->TimeZoneNumber * 15.0 - 360.0;
4399 : }
4400 113 : state.dataEnvrn->SinLatitude = std::sin(Constant::DegToRad * state.dataEnvrn->Latitude);
4401 113 : state.dataEnvrn->CosLatitude = std::cos(Constant::DegToRad * state.dataEnvrn->Latitude);
4402 :
4403 113 : if (state.dataEnvrn->Latitude == 0.0 && state.dataEnvrn->Longitude == 0.0 && state.dataEnvrn->TimeZoneNumber == 0.0) {
4404 3 : ShowWarningError(state,
4405 : "Did you realize that you have Latitude=0.0, Longitude=0.0 and TimeZone=0.0? Your building site is in the middle of "
4406 : "the Atlantic Ocean.");
4407 : }
4408 113 : }
4409 :
4410 50 : void CheckWeatherFileValidity(EnergyPlusData &state)
4411 : {
4412 :
4413 : // SUBROUTINE INFORMATION:
4414 : // AUTHOR Linda Lawrie
4415 : // DATE WRITTEN February 1977
4416 : // MODIFIED June 1997 (RKS)
4417 :
4418 : // PURPOSE OF THIS SUBROUTINE:
4419 : // This subroutine contains a portion of the legacy subroutine CKBLDE.
4420 : // The main purpose of this routine is to check the validity of the
4421 : // weather dates provided by the user and the attached weather file.
4422 : // These functions may eventually be pushed to an interface. This
4423 : // routine also sends the weather file header information at the
4424 : // Environment derived type.
4425 :
4426 50 : if (!state.dataWeather->WeatherFileExists) { // No weather file exists but the user requested one--print error message
4427 :
4428 25 : if (state.dataGlobal->DoWeathSim) {
4429 2 : ShowSevereError(state, "GetNextEnvironment: Weather Environment(s) requested, but no weather file found");
4430 3 : ShowFatalError(state, "Due to previous error condition, simulation terminated");
4431 : }
4432 :
4433 : } // ... end of WeatherFileExists IF-THEN
4434 49 : }
4435 :
4436 107 : void ReportOutputFileHeaders(EnergyPlusData &state)
4437 : {
4438 :
4439 : // SUBROUTINE INFORMATION:
4440 : // AUTHOR Rick Strand
4441 : // DATE WRITTEN June 1997
4442 : // MODIFIED December 2017; Jason DeGraw
4443 :
4444 : // PURPOSE OF THIS SUBROUTINE:
4445 : // This subroutine prints out the necessary header information required
4446 : // by the EnergyPlus output file format. This subroutine can be
4447 : // replicated in any other modules which must send data to the output
4448 : // file.
4449 :
4450 : // METHODOLOGY EMPLOYED:
4451 : // For each report, the report flag integer must be saved from the
4452 : // global report number counter. Then, the report counter must be
4453 : // incremented. Finally, the header information for the report must
4454 : // be sent to the output file.
4455 :
4456 : using OutputProcessor::ReportFreq;
4457 :
4458 : static constexpr std::string_view EnvironmentString(",5,Environment Title[],Latitude[deg],Longitude[deg],Time Zone[],Elevation[m]");
4459 :
4460 : static constexpr std::array<std::string_view, (int)ReportFreq::Num> freqStrings = {
4461 : "", // No EachCall string
4462 : ",8,Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],Hour[],StartMinute[],EndMinute[],DayType",
4463 : "", // No Hour string
4464 : ",5,Cumulative Day of Simulation[],Month[],Day of Month[],DST Indicator[1=yes 0=no],DayType ! When Daily ",
4465 : ",2,Cumulative Days of Simulation[],Month[] ! When Monthly ",
4466 : ",1,Cumulative Days of Simulation[] ! When Run Period ",
4467 : ",1,Calendar Year of Simulation[] ! When Annual "};
4468 :
4469 107 : auto &op = state.dataOutputProcessor;
4470 :
4471 107 : state.dataWeather->EnvironmentReportNbr = ++op->ReportNumberCounter;
4472 107 : if (state.dataWeather->EnvironmentReportNbr != 1) { // problem
4473 0 : ShowFatalError(state, "ReportOutputFileHeaders: Assigned report number for Environment title is not 1. Contact Support.");
4474 : }
4475 107 : state.dataWeather->EnvironmentReportChr = fmt::to_string(state.dataWeather->EnvironmentReportNbr);
4476 107 : strip(state.dataWeather->EnvironmentReportChr);
4477 107 : print(state.files.eso, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4478 107 : print(state.files.mtr, "{}{}\n", state.dataWeather->EnvironmentReportChr, EnvironmentString);
4479 :
4480 : // TImeStep and Hour share a stamp
4481 107 : op->freqStampReportNums[(int)ReportFreq::Hour] = op->freqStampReportNums[(int)ReportFreq::TimeStep] = ++op->ReportNumberCounter;
4482 107 : print(state.files.eso, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4483 107 : print(state.files.mtr, "{}{}\n", op->freqStampReportNums[(int)ReportFreq::TimeStep], freqStrings[(int)ReportFreq::TimeStep]);
4484 :
4485 535 : for (ReportFreq freq : {ReportFreq::Day, ReportFreq::Month, ReportFreq::Simulation, ReportFreq::Year}) {
4486 428 : op->freqStampReportNums[(int)freq] = ++op->ReportNumberCounter;
4487 428 : print(state.files.eso, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Report Variables Requested");
4488 428 : print(state.files.mtr, "{}{}{}\n", op->freqStampReportNums[(int)freq], freqStrings[(int)freq], "Meters Requested");
4489 : }
4490 107 : }
4491 :
4492 326115 : void ReportWeatherAndTimeInformation(EnergyPlusData &state, bool &printEnvrnStamp) // Set to true when the environment header should be printed
4493 : {
4494 :
4495 : // SUBROUTINE INFORMATION:
4496 : // AUTHOR Rick Strand
4497 : // DATE WRITTEN June 1997
4498 :
4499 : // PURPOSE OF THIS SUBROUTINE:
4500 : // This subroutine is the main driver of the weather reporting. This
4501 : // routine is also responsible for printing the time and environment
4502 : // stamps.
4503 :
4504 : // METHODOLOGY EMPLOYED:
4505 : // Reporting is only done for non-warmup days. The environment stamp
4506 : // is only reported at the beginning of an environment, but after the
4507 : // warmup days (to allow all modules to print the report headers to the
4508 : // output file. This is controlled by the PrintEnvrnStamp variable
4509 : // which is passed in and reset if necessary.
4510 :
4511 : // Report the time stamp and the current weather to the output file
4512 :
4513 326115 : if (!state.dataGlobal->WarmupFlag && !state.dataWeather->RPReadAllWeatherData) { // Write the required output information
4514 :
4515 : // The first time through in a non-warmup day, the environment header
4516 : // must be printed. This must be done here and not in the generic
4517 : // DataGlobals::BeginEnvrnFlag block above because other modules in the simulation
4518 : // must also print out header information. This can be done during
4519 : // the simulation warmup if the environment stamp printing is delayed
4520 : // until the warmup is completed. The stamp should only be printed once
4521 : // per environment (set/reset of PrintEnvrnStamp). In addition, before
4522 : // the first environment, the end of the header block flag must also be
4523 : // sent to the output file.
4524 :
4525 34684 : if (printEnvrnStamp) {
4526 :
4527 16044 : if (state.dataReportFlag->PrintEndDataDictionary && state.dataGlobal->DoOutputReporting) {
4528 : static constexpr std::string_view EndOfHeaderString("End of Data Dictionary"); // End of data dictionary marker
4529 73 : print(state.files.eso, "{}\n", EndOfHeaderString);
4530 73 : print(state.files.mtr, "{}\n", EndOfHeaderString);
4531 73 : state.dataReportFlag->PrintEndDataDictionary = false;
4532 : }
4533 16044 : if (state.dataGlobal->DoOutputReporting) {
4534 129 : std::string const &Title = state.dataWeather->Environment(state.dataWeather->Envrn).Title;
4535 : static constexpr std::string_view EnvironmentStampFormatStr(
4536 : "{},{},{:7.2F},{:7.2F},{:7.2F},{:7.2F}\n"); // Format descriptor for environ stamp
4537 129 : print(state.files.eso,
4538 : EnvironmentStampFormatStr,
4539 129 : state.dataWeather->EnvironmentReportChr,
4540 : Title,
4541 129 : state.dataEnvrn->Latitude,
4542 129 : state.dataEnvrn->Longitude,
4543 129 : state.dataEnvrn->TimeZoneNumber,
4544 129 : state.dataEnvrn->Elevation);
4545 129 : print(state.files.mtr,
4546 : EnvironmentStampFormatStr,
4547 129 : state.dataWeather->EnvironmentReportChr,
4548 : Title,
4549 129 : state.dataEnvrn->Latitude,
4550 129 : state.dataEnvrn->Longitude,
4551 129 : state.dataEnvrn->TimeZoneNumber,
4552 129 : state.dataEnvrn->Elevation);
4553 129 : printEnvrnStamp = false;
4554 : }
4555 : }
4556 : } // ... end of .NOT.WarmupFlag IF-THEN block.
4557 326115 : }
4558 :
4559 111 : void ReadUserWeatherInput(EnergyPlusData &state)
4560 : {
4561 :
4562 : // SUBROUTINE INFORMATION:
4563 : // AUTHOR Richard Liesen
4564 : // DATE WRITTEN September 1997
4565 :
4566 : // PURPOSE OF THIS SUBROUTINE:
4567 : // This subroutine is the main driver of the weather manager module.
4568 : // It controls the assignment of weather related global variables as
4569 : // well as the reads and writes for retrieving weather information.
4570 :
4571 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4572 111 : bool ErrorsFound(false);
4573 :
4574 : // Get the number of design days and annual runs from user inpout
4575 111 : state.dataEnvrn->TotDesDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:DesignDay");
4576 111 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
4577 111 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
4578 111 : state.dataWeather->TotRunPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriod");
4579 111 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4580 111 : state.dataGlobal->WeathSimReq = state.dataWeather->TotRunPers > 0;
4581 111 : state.dataWeather->TotReportPers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Output:Table:ReportPeriod");
4582 : #ifdef GET_OUT
4583 : state.dataWeather->SPSiteScheduleNamePtr.allocate(state.dataEnvrn->TotDesDays * 5);
4584 : state.dataWeather->SPSiteScheduleUnits.allocate(state.dataEnvrn->TotDesDays * 5);
4585 :
4586 : state.dataWeather->SPSiteScheduleNamePtr = 0;
4587 : state.dataWeather->SPSiteScheduleUnits = "";
4588 : #endif //
4589 : // Allocate the Design Day and Environment array to the # of DD's or/and
4590 : // Annual runs on input file
4591 111 : state.dataWeather->DesignDay.allocate(state.dataEnvrn->TotDesDays);
4592 111 : state.dataWeather->Environment.allocate(state.dataWeather->NumOfEnvrn);
4593 :
4594 : // Set all Environments to DesignDay and then the weather environment will be set
4595 : // in the get annual run data subroutine
4596 288 : for (int Env = 1; Env <= state.dataEnvrn->TotDesDays; ++Env) {
4597 177 : state.dataWeather->Environment(Env).KindOfEnvrn = Constant::KindOfSim::DesignDay;
4598 : }
4599 111 : for (int Env = 1; Env <= RPD1 + RPD2; ++Env) {
4600 0 : if (!state.dataSysVars->DDOnly) {
4601 0 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
4602 : } else {
4603 0 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4604 : }
4605 : }
4606 164 : for (int Env = 1; Env <= state.dataWeather->TotRunPers; ++Env) {
4607 53 : state.dataWeather->Environment(state.dataEnvrn->TotDesDays + RPD1 + RPD2 + Env).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
4608 : }
4609 :
4610 111 : if (state.dataEnvrn->TotDesDays >= 1) {
4611 103 : GetDesignDayData(state, state.dataEnvrn->TotDesDays, ErrorsFound);
4612 : }
4613 :
4614 111 : if (RPD1 >= 1 || RPD2 >= 1) {
4615 0 : GetRunPeriodDesignData(state, ErrorsFound);
4616 : }
4617 :
4618 : // the last environment(s) is designated the weather environment if an annual run
4619 : // is selected. All of the design systems is done from the design day info
4620 : // which will have to be completed to run the annual run.
4621 111 : if (state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4622 50 : GetRunPeriodData(state, state.dataWeather->TotRunPers, ErrorsFound);
4623 : }
4624 :
4625 111 : if (state.dataWeather->TotReportPers > 0) {
4626 0 : GetReportPeriodData(state, state.dataWeather->TotReportPers, ErrorsFound);
4627 0 : GroupReportPeriodByType(state, state.dataWeather->TotReportPers);
4628 : }
4629 :
4630 111 : if (state.dataSysVars->FullAnnualRun) {
4631 : // GetRunPeriodData may have reset the value of TotRunPers
4632 0 : state.dataWeather->NumOfEnvrn = state.dataEnvrn->TotDesDays + state.dataWeather->TotRunPers + RPD1 + RPD2;
4633 : }
4634 :
4635 111 : if (RPD1 >= 1 || RPD2 >= 1 || state.dataWeather->TotRunPers >= 1 || state.dataSysVars->FullAnnualRun) {
4636 50 : GetSpecialDayPeriodData(state, ErrorsFound);
4637 50 : GetDSTData(state, ErrorsFound);
4638 50 : if (state.dataWeather->IDFDaylightSaving) {
4639 0 : state.dataWeather->DST = state.dataWeather->IDFDST;
4640 : }
4641 : }
4642 :
4643 111 : GetLocationInfo(state, ErrorsFound);
4644 :
4645 111 : GetGroundTemps(state);
4646 :
4647 111 : GetGroundReflectances(state, ErrorsFound);
4648 :
4649 111 : GetSnowGroundRefModifiers(state, ErrorsFound);
4650 :
4651 111 : GetWaterMainsTemperatures(state, ErrorsFound);
4652 :
4653 111 : GetWeatherStation(state, ErrorsFound);
4654 :
4655 111 : SetupEnvironmentTypes(state);
4656 :
4657 111 : GetWeatherProperties(state, ErrorsFound);
4658 : #ifdef GET_OUT
4659 : // Deallocate ones used for schedule pointers
4660 : state.dataWeather->SPSiteScheduleNamePtr.deallocate();
4661 : state.dataWeather->SPSiteScheduleUnits.deallocate();
4662 : #endif //
4663 111 : if (ErrorsFound) {
4664 0 : ShowFatalError(state, "GetWeatherInput: Above errors cause termination");
4665 : }
4666 111 : }
4667 :
4668 55 : static int findYearForWeekday(int const month, int const day, Sched::DayType const weekday)
4669 : {
4670 : // Find a year that goes with a month/day and a weekday. A lookup table is used with the most recent year that includes
4671 : // the date with the weekday specified.
4672 :
4673 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4674 : static std::array<int, 13> const defaultYear{{2013, 2014, 2015, 2010, 2011, 2017, 2007, 2013, 2014, 2015, 2010, 2011, 2017}};
4675 :
4676 55 : int rem = calculateDayOfYear(month, day) % 7;
4677 55 : return defaultYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4678 : }
4679 :
4680 2 : static int findLeapYearForWeekday(int const month, int const day, Sched::DayType const weekday)
4681 : {
4682 : // 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
4683 : // the date with the weekday specified.
4684 :
4685 : // Tu, W, Th, F, Sa, Su, M, Tu, W, Th, F, Sa, Su
4686 : static std::array<int, 13> const defaultLeapYear{{2008, 1992, 2004, 2016, 2000, 2012, 1996, 2008, 1992, 2004, 2016, 2000, 2012}};
4687 :
4688 2 : int rem = calculateDayOfYear(month, day, true) % 7;
4689 2 : return defaultLeapYear[static_cast<int>(weekday) - rem + 5]; // static_cast<int>(weekday) - rem + 1 + 4
4690 : }
4691 :
4692 5 : void GetReportPeriodData(EnergyPlusData &state,
4693 : int nReportPeriods, // Total number of Report Periods requested
4694 : bool &ErrorsFound)
4695 : {
4696 5 : constexpr std::string_view routineName = "GetReportPeriodData";
4697 5 : state.dataWeather->ReportPeriodInput.allocate(nReportPeriods);
4698 :
4699 5 : auto const &ipsc = state.dataIPShortCut;
4700 5 : ipsc->cCurrentModuleObject = "Output:Table:ReportPeriod";
4701 5 : int Count = 0;
4702 : int NumAlpha; // Number of alphas being input
4703 : int NumNumeric; // Number of numbers being input
4704 : int IOStat; // IO Status when calling get input subroutine
4705 15 : for (int i = 1; i <= nReportPeriods; ++i) {
4706 20 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4707 10 : ipsc->cCurrentModuleObject,
4708 : i,
4709 10 : ipsc->cAlphaArgs,
4710 : NumAlpha,
4711 10 : ipsc->rNumericArgs,
4712 : NumNumeric,
4713 : IOStat,
4714 10 : ipsc->lNumericFieldBlanks,
4715 10 : ipsc->lAlphaFieldBlanks,
4716 10 : ipsc->cAlphaFieldNames,
4717 10 : ipsc->cNumericFieldNames);
4718 :
4719 10 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
4720 10 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
4721 : // A1, \field Name
4722 10 : if (std::find_if(state.dataWeather->ReportPeriodInput.begin(),
4723 10 : state.dataWeather->ReportPeriodInput.end(),
4724 30 : [&newName](ReportPeriodData const &rpd) { return newName == rpd.title; }) !=
4725 10 : state.dataWeather->ReportPeriodInput.end()) {
4726 1 : ShowSevereDuplicateName(state, eoh);
4727 1 : ErrorsFound = true;
4728 : }
4729 :
4730 10 : ++Count;
4731 :
4732 10 : auto &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4733 : // Loop = RP + Ptr;
4734 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "ReportPeriod 1" anyways
4735 : // which is fine for our reporting below
4736 10 : reportPeriodInput.title = newName;
4737 : // A2, \field Report Name
4738 10 : reportPeriodInput.reportName = ipsc->cAlphaArgs(2);
4739 :
4740 : // set the start and end day of month from user input
4741 : // N1, \field Begin Year
4742 : // N2, \field Begin Month
4743 : // N3, \field Begin Day of Month
4744 : // N4, \field Begin Hour of Day
4745 : // N5, \field End Year
4746 : // N6, \field End Month
4747 : // N7, \field End Day of Month
4748 : // N8; \field End Hour of Day
4749 10 : reportPeriodInput.startYear = int(ipsc->rNumericArgs(1));
4750 10 : reportPeriodInput.startMonth = int(ipsc->rNumericArgs(2));
4751 10 : reportPeriodInput.startDay = int(ipsc->rNumericArgs(3));
4752 10 : reportPeriodInput.startHour = int(ipsc->rNumericArgs(4));
4753 10 : reportPeriodInput.endYear = int(ipsc->rNumericArgs(5));
4754 10 : reportPeriodInput.endMonth = int(ipsc->rNumericArgs(6));
4755 10 : reportPeriodInput.endDay = int(ipsc->rNumericArgs(7));
4756 10 : reportPeriodInput.endHour = int(ipsc->rNumericArgs(8));
4757 :
4758 : // Validate year inputs
4759 10 : if (reportPeriodInput.startYear == 0) {
4760 10 : if (reportPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
4761 0 : ShowSevereError(state,
4762 0 : format("{}: object={}, end year cannot be specified if the start year is not.",
4763 0 : ipsc->cCurrentModuleObject,
4764 0 : reportPeriodInput.title));
4765 0 : ErrorsFound = true;
4766 : }
4767 0 : } else if (reportPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
4768 0 : ShowSevereError(state,
4769 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
4770 0 : ipsc->cCurrentModuleObject,
4771 0 : reportPeriodInput.title,
4772 0 : reportPeriodInput.startYear));
4773 0 : ErrorsFound = true;
4774 : }
4775 :
4776 10 : if (reportPeriodInput.endYear != 0 && reportPeriodInput.startYear > reportPeriodInput.endYear) {
4777 0 : ShowSevereError(state,
4778 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
4779 0 : ipsc->cCurrentModuleObject,
4780 0 : reportPeriodInput.title,
4781 0 : reportPeriodInput.startYear,
4782 0 : reportPeriodInput.endYear));
4783 0 : ErrorsFound = true;
4784 : }
4785 :
4786 10 : reportPeriodInput.startJulianDate =
4787 10 : computeJulianDate(reportPeriodInput.startYear, reportPeriodInput.startMonth, reportPeriodInput.startDay);
4788 10 : reportPeriodInput.endJulianDate = computeJulianDate(reportPeriodInput.endYear, reportPeriodInput.endMonth, reportPeriodInput.endDay);
4789 10 : }
4790 5 : }
4791 :
4792 4 : void GroupReportPeriodByType(EnergyPlusData &state, const int nReportPeriods)
4793 : {
4794 : // transfer data from the reporting period object to the corresponding report period type arrays
4795 : // ThermalResilienceSummary, CO2ResilienceSummary, VisualResilienceSummary, and AllResilienceSummaries
4796 13 : for (auto const &reportPeriodInput : state.dataWeather->ReportPeriodInput) {
4797 :
4798 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4799 4 : ++state.dataWeather->TotThermalReportPers;
4800 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4801 3 : ++state.dataWeather->TotCO2ReportPers;
4802 2 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4803 2 : ++state.dataWeather->TotVisualReportPers;
4804 0 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4805 0 : ++state.dataWeather->TotThermalReportPers;
4806 0 : ++state.dataWeather->TotCO2ReportPers;
4807 0 : ++state.dataWeather->TotVisualReportPers;
4808 : }
4809 : }
4810 :
4811 4 : state.dataWeather->ThermalReportPeriodInput.allocate(state.dataWeather->TotThermalReportPers);
4812 4 : state.dataWeather->CO2ReportPeriodInput.allocate(state.dataWeather->TotCO2ReportPers);
4813 4 : state.dataWeather->VisualReportPeriodInput.allocate(state.dataWeather->TotVisualReportPers);
4814 :
4815 13 : for (int i = 1, iThermal = 1, iVisual = 1, iCO2 = 1; i <= nReportPeriods; ++i) {
4816 9 : auto const &reportPeriodInput = state.dataWeather->ReportPeriodInput(i);
4817 9 : if (reportPeriodInput.reportName == "THERMALRESILIENCESUMMARY") {
4818 4 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4819 4 : ++iThermal;
4820 5 : } else if (reportPeriodInput.reportName == "CO2RESILIENCESUMMARY") {
4821 3 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4822 3 : ++iCO2;
4823 2 : } else if (reportPeriodInput.reportName == "VISUALRESILIENCESUMMARY") {
4824 2 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4825 2 : ++iVisual;
4826 0 : } else if (reportPeriodInput.reportName == "ALLRESILIENCESUMMARIES") {
4827 0 : state.dataWeather->ThermalReportPeriodInput(iThermal) = reportPeriodInput;
4828 0 : ++iThermal;
4829 0 : state.dataWeather->CO2ReportPeriodInput(iCO2) = reportPeriodInput;
4830 0 : ++iCO2;
4831 0 : state.dataWeather->VisualReportPeriodInput(iVisual) = reportPeriodInput;
4832 0 : ++iVisual;
4833 : }
4834 : }
4835 4 : }
4836 :
4837 55 : void GetRunPeriodData(EnergyPlusData &state,
4838 : int nRunPeriods, // Total number of Run Periods requested
4839 : bool &ErrorsFound)
4840 : {
4841 :
4842 : // SUBROUTINE INFORMATION:
4843 : // AUTHOR Richard Liesen
4844 : // DATE WRITTEN October 1997
4845 : // MODIFIED February 1999, Add multiple run periods, Change name.
4846 : // March 2012, LKL, Add features to object; New "actual weather" object;
4847 :
4848 : // PURPOSE OF THIS SUBROUTINE:
4849 : // This subroutine gets the run period info from User input and the
4850 : // simulation dates
4851 :
4852 55 : constexpr std::string_view routineName = "GetRunPeriodData";
4853 : // Call Input Get routine to retrieve annual run data
4854 55 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
4855 :
4856 55 : auto const &ipsc = state.dataIPShortCut;
4857 55 : ipsc->cCurrentModuleObject = "RunPeriod";
4858 55 : 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 119 : for (int i = 1; i <= nRunPeriods; ++i) {
4863 128 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4864 64 : ipsc->cCurrentModuleObject,
4865 : i,
4866 64 : ipsc->cAlphaArgs,
4867 : NumAlpha,
4868 64 : ipsc->rNumericArgs,
4869 : NumNumeric,
4870 : IOStat,
4871 64 : ipsc->lNumericFieldBlanks,
4872 64 : ipsc->lAlphaFieldBlanks,
4873 64 : ipsc->cAlphaFieldNames,
4874 64 : ipsc->cNumericFieldNames);
4875 :
4876 : // A1, \field Name
4877 64 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
4878 64 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
4879 :
4880 64 : if (std::find_if(state.dataWeather->RunPeriodInput.begin(),
4881 64 : state.dataWeather->RunPeriodInput.end(),
4882 178 : [&newName](RunPeriodData const &rpd) { return rpd.title == newName; }) != state.dataWeather->RunPeriodInput.end()) {
4883 0 : ShowSevereDuplicateName(state, eoh);
4884 0 : ErrorsFound = true;
4885 : }
4886 :
4887 64 : ++Count;
4888 : // Loop = RP + Ptr;
4889 : // Note JM 2018-11-20: IDD allows blank name, but input processor will create a name such as "RUNPERIOD 1" anyways
4890 : // which is fine for our reporting below
4891 64 : auto &runPeriodInput = state.dataWeather->RunPeriodInput(i);
4892 64 : runPeriodInput.title = ipsc->cAlphaArgs(1);
4893 :
4894 : // set the start and end day of month from user input
4895 : // N1 , \field Begin Month
4896 : // N2 , \field Begin Day of Month
4897 : // N3, \field Start Year
4898 : // N4 , \field End Month
4899 : // N5 , \field End Day of Month
4900 : // N6, \field End Year
4901 64 : runPeriodInput.startMonth = int(ipsc->rNumericArgs(1));
4902 64 : runPeriodInput.startDay = int(ipsc->rNumericArgs(2));
4903 64 : runPeriodInput.startYear = int(ipsc->rNumericArgs(3));
4904 64 : runPeriodInput.endMonth = int(ipsc->rNumericArgs(4));
4905 64 : runPeriodInput.endDay = int(ipsc->rNumericArgs(5));
4906 64 : runPeriodInput.endYear = int(ipsc->rNumericArgs(6));
4907 64 : runPeriodInput.TreatYearsAsConsecutive = true;
4908 :
4909 64 : if (state.dataSysVars->FullAnnualRun && i == 1) {
4910 0 : runPeriodInput.startMonth = 1;
4911 0 : runPeriodInput.startDay = 1;
4912 0 : runPeriodInput.endMonth = 12;
4913 0 : runPeriodInput.endDay = 31;
4914 : }
4915 :
4916 : // Validate year inputs
4917 64 : if (runPeriodInput.startYear == 0) {
4918 58 : if (runPeriodInput.endYear != 0) { // Have to have an input start year to input an end year
4919 2 : ShowSevereError(state,
4920 2 : format("{}: object={}, end year cannot be specified if the start year is not.",
4921 1 : ipsc->cCurrentModuleObject,
4922 1 : runPeriodInput.title));
4923 1 : ErrorsFound = true;
4924 : }
4925 6 : } else if (runPeriodInput.startYear < 1583) { // Bail on the proleptic Gregorian calendar
4926 0 : ShowSevereError(state,
4927 0 : format("{}: object={}, start year ({}) is too early, please choose a date after 1582.",
4928 0 : ipsc->cCurrentModuleObject,
4929 0 : runPeriodInput.title,
4930 0 : runPeriodInput.startYear));
4931 0 : ErrorsFound = true;
4932 : }
4933 :
4934 64 : if (runPeriodInput.endYear != 0 && runPeriodInput.startYear > runPeriodInput.endYear) {
4935 0 : ShowSevereError(state,
4936 0 : format("{}: object={}, start year ({}) is after the end year ({}).",
4937 0 : ipsc->cCurrentModuleObject,
4938 0 : runPeriodInput.title,
4939 0 : runPeriodInput.startYear,
4940 0 : runPeriodInput.endYear));
4941 0 : ErrorsFound = true;
4942 : }
4943 :
4944 : // A2 , \field Day of Week for Start Day
4945 64 : bool inputWeekday = false;
4946 64 : if (!state.dataIPShortCut->lAlphaFieldBlanks(2)) { // Have input
4947 61 : int dayType = getEnumValue(Sched::dayTypeNamesUC, state.dataIPShortCut->cAlphaArgs(2));
4948 61 : if (dayType < 1) {
4949 0 : ShowWarningError(state,
4950 0 : format("{}: object={}{} invalid (Day of Week) [{}] for Start is not valid, Sunday will be used.",
4951 0 : state.dataIPShortCut->cCurrentModuleObject,
4952 0 : state.dataWeather->RunPeriodInput(i).title,
4953 0 : state.dataIPShortCut->cAlphaFieldNames(2),
4954 0 : state.dataIPShortCut->cAlphaArgs(2)));
4955 0 : runPeriodInput.startWeekDay = Sched::DayType::Sunday;
4956 : } else {
4957 61 : runPeriodInput.startWeekDay = static_cast<Sched::DayType>(dayType);
4958 61 : inputWeekday = true;
4959 : }
4960 : } else { // No input, set the default as Sunday. This may get overriden below
4961 3 : runPeriodInput.startWeekDay = Sched::DayType::Sunday;
4962 : }
4963 :
4964 : // Validate the dates now that the weekday field has been looked at
4965 64 : if (runPeriodInput.startMonth == 2 && runPeriodInput.startDay == 29) {
4966 : // Requested start date is a leap year
4967 4 : if (runPeriodInput.startYear == 0) { // No input starting year
4968 2 : if (inputWeekday) {
4969 2 : runPeriodInput.startYear =
4970 2 : findLeapYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
4971 : } else {
4972 : // 2012 is the default year, 1/1 is a Sunday
4973 0 : runPeriodInput.startYear = 2012;
4974 0 : runPeriodInput.startWeekDay =
4975 0 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
4976 : }
4977 : } else { // Have an input start year
4978 2 : if (!isLeapYear(runPeriodInput.startYear)) { // Start year is not a leap year
4979 2 : ShowSevereError(state,
4980 2 : format("{}: object={}, start year ({}) is not a leap year but the requested start date is 2/29.",
4981 1 : ipsc->cCurrentModuleObject,
4982 1 : runPeriodInput.title,
4983 1 : runPeriodInput.startYear));
4984 1 : ErrorsFound = true;
4985 : } else { // Start year is a leap year
4986 : Sched::DayType weekday =
4987 1 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
4988 1 : if (inputWeekday) { // Check for correctness of input
4989 1 : if (weekday != runPeriodInput.startWeekDay) {
4990 0 : ShowWarningError(state,
4991 0 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
4992 0 : ipsc->cCurrentModuleObject,
4993 0 : runPeriodInput.title,
4994 0 : ipsc->cAlphaArgs(2),
4995 0 : runPeriodInput.startYear,
4996 0 : Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
4997 0 : runPeriodInput.startWeekDay = weekday;
4998 : }
4999 : } else { // Set the weekday if it was not input
5000 0 : runPeriodInput.startWeekDay = weekday;
5001 : }
5002 : }
5003 : }
5004 4 : } else {
5005 : // Non leap-day start date
5006 60 : if (!validMonthDay(runPeriodInput.startMonth, runPeriodInput.startDay)) {
5007 0 : ShowSevereError(state,
5008 0 : format("{}: object={}, Invalid input start month/day ({}/{})",
5009 0 : ipsc->cCurrentModuleObject,
5010 0 : runPeriodInput.title,
5011 0 : runPeriodInput.startMonth,
5012 0 : runPeriodInput.startDay));
5013 0 : ErrorsFound = true;
5014 : } else { // Month/day is valid
5015 60 : if (runPeriodInput.startYear == 0) { // No input starting year
5016 56 : if (inputWeekday) {
5017 55 : runPeriodInput.startYear =
5018 55 : findYearForWeekday(runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.startWeekDay);
5019 : } else {
5020 : // 2017 is the default year, 1/1 is a Sunday
5021 1 : runPeriodInput.startYear = 2017;
5022 1 : runPeriodInput.startWeekDay =
5023 1 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5024 : }
5025 : } else { // Have an input starting year
5026 : Sched::DayType weekday =
5027 4 : calculateDayOfWeek(state, runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5028 4 : if (inputWeekday) { // Check for correctness of input
5029 2 : if (weekday != runPeriodInput.startWeekDay) {
5030 2 : ShowWarningError(state,
5031 3 : format("{}: object={}, start weekday ({}) does not match the start year ({}), corrected to {}.",
5032 1 : ipsc->cCurrentModuleObject,
5033 1 : runPeriodInput.title,
5034 1 : ipsc->cAlphaArgs(2),
5035 1 : runPeriodInput.startYear,
5036 1 : Sched::dayTypeNamesUC[static_cast<int>(weekday)]));
5037 1 : runPeriodInput.startWeekDay = weekday;
5038 : }
5039 : } else { // Set the weekday if it was not input
5040 2 : runPeriodInput.startWeekDay = weekday;
5041 : }
5042 : }
5043 : }
5044 : }
5045 :
5046 : // Compute the Julian date of the start date
5047 64 : runPeriodInput.startJulianDate = computeJulianDate(runPeriodInput.startYear, runPeriodInput.startMonth, runPeriodInput.startDay);
5048 :
5049 : // Validate the end date
5050 64 : if (runPeriodInput.endMonth == 2 && runPeriodInput.endDay == 29) {
5051 : // Requested end date is a leap year
5052 0 : if (runPeriodInput.endYear == 0) { // No input end year
5053 0 : if (isLeapYear(runPeriodInput.startYear) && runPeriodInput.startMonth < 3) {
5054 : // The run period is from some date on or before 2/29 through 2/29
5055 0 : runPeriodInput.endYear = runPeriodInput.startYear;
5056 : } else {
5057 : // There might be a better approach here, but for now just loop forward for the next leap year
5058 0 : for (int yr = runPeriodInput.startYear + 1; yr < runPeriodInput.startYear + 10; yr++) {
5059 0 : if (isLeapYear(yr)) {
5060 0 : runPeriodInput.endYear = yr;
5061 0 : break;
5062 : }
5063 : }
5064 : }
5065 : } else { // Have an input end year
5066 0 : if (!isLeapYear(runPeriodInput.endYear)) { // End year is not a leap year
5067 0 : ShowSevereError(state,
5068 0 : format("{}: object={}, end year ({}) is not a leap year but the requested end date is 2/29.",
5069 0 : ipsc->cCurrentModuleObject,
5070 0 : runPeriodInput.title,
5071 0 : runPeriodInput.startYear));
5072 0 : ErrorsFound = true;
5073 : } else {
5074 0 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5075 0 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5076 0 : ShowSevereError(state,
5077 0 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5078 0 : ipsc->cCurrentModuleObject,
5079 0 : runPeriodInput.title,
5080 0 : runPeriodInput.startJulianDate,
5081 0 : runPeriodInput.endJulianDate));
5082 0 : ErrorsFound = true;
5083 : }
5084 : }
5085 : }
5086 0 : } else {
5087 : // Non leap-day end date
5088 64 : if (!validMonthDay(runPeriodInput.endMonth, runPeriodInput.endDay)) {
5089 0 : ShowSevereError(state,
5090 0 : format("{}: object={}, Invalid input end month/day ({}/{})",
5091 0 : ipsc->cCurrentModuleObject,
5092 0 : runPeriodInput.title,
5093 0 : runPeriodInput.startMonth,
5094 0 : runPeriodInput.startDay));
5095 0 : ErrorsFound = true;
5096 : } else { // Month/day is valid
5097 64 : if (runPeriodInput.endYear == 0) { // No input end year
5098 : // Assume same year as start year
5099 61 : runPeriodInput.endYear = runPeriodInput.startYear;
5100 61 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5101 61 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5102 0 : runPeriodInput.endJulianDate = 0; // Force recalculation later
5103 0 : runPeriodInput.endYear += 1;
5104 : }
5105 : } else { // Have an input end year
5106 3 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5107 3 : if (runPeriodInput.startJulianDate > runPeriodInput.endJulianDate) {
5108 2 : ShowSevereError(state,
5109 2 : format("{}: object={}, start Julian date ({}) is after the end Julian date ({}).",
5110 1 : ipsc->cCurrentModuleObject,
5111 1 : runPeriodInput.title,
5112 1 : runPeriodInput.startJulianDate,
5113 1 : runPeriodInput.endJulianDate));
5114 1 : ErrorsFound = true;
5115 : }
5116 : }
5117 : }
5118 : }
5119 :
5120 64 : if (runPeriodInput.endJulianDate == 0) {
5121 0 : runPeriodInput.endJulianDate = computeJulianDate(runPeriodInput.endYear, runPeriodInput.endMonth, runPeriodInput.endDay);
5122 : }
5123 :
5124 64 : runPeriodInput.numSimYears = runPeriodInput.endYear - runPeriodInput.startYear + 1;
5125 :
5126 : // A3, \field Use Weather File Holidays and Special Days
5127 : BooleanSwitch b;
5128 64 : if (ipsc->lAlphaFieldBlanks(3)) {
5129 0 : runPeriodInput.useHolidays = true;
5130 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5131 64 : runPeriodInput.useHolidays = static_cast<bool>(b);
5132 : } else {
5133 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5134 0 : ErrorsFound = true;
5135 : }
5136 :
5137 : // A4, \field Use Weather File Daylight Saving Period
5138 64 : if (ipsc->lAlphaFieldBlanks(4)) {
5139 0 : runPeriodInput.useDST = true;
5140 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5141 64 : runPeriodInput.useDST = static_cast<bool>(b);
5142 : } else {
5143 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5144 0 : ErrorsFound = true;
5145 : }
5146 :
5147 : // A5, \field Apply Weekend Holiday Rule
5148 64 : if (ipsc->lAlphaFieldBlanks(5)) {
5149 0 : runPeriodInput.applyWeekendRule = true;
5150 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5151 64 : runPeriodInput.applyWeekendRule = static_cast<bool>(b);
5152 : } else {
5153 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5154 0 : ErrorsFound = true;
5155 : }
5156 :
5157 : // A6, \field Use Weather File Rain Indicators
5158 64 : if (ipsc->lAlphaFieldBlanks(6)) {
5159 0 : runPeriodInput.useRain = true;
5160 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(6)))) != BooleanSwitch::Invalid) {
5161 64 : runPeriodInput.useRain = static_cast<bool>(b);
5162 : } else {
5163 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6));
5164 0 : ErrorsFound = true;
5165 : }
5166 :
5167 : // A7, \field Use Weather File Snow Indicators
5168 64 : if (ipsc->lAlphaFieldBlanks(7)) {
5169 0 : runPeriodInput.useSnow = true;
5170 64 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
5171 64 : runPeriodInput.useSnow = static_cast<bool>(b);
5172 : } else {
5173 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7));
5174 0 : ErrorsFound = true;
5175 : }
5176 :
5177 : // A8, \field Treat Weather as Actual
5178 64 : if (ipsc->lAlphaFieldBlanks(8)) {
5179 62 : runPeriodInput.actualWeather = false;
5180 2 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
5181 2 : runPeriodInput.actualWeather = static_cast<bool>(b);
5182 : } else {
5183 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8));
5184 0 : ErrorsFound = true;
5185 : }
5186 :
5187 : // A9, \field First Hour Interpolation Starting Values
5188 64 : if (ipsc->lAlphaFieldBlanks(9) || Util::SameString(ipsc->cAlphaArgs(8), "Hour24")) {
5189 62 : runPeriodInput.firstHrInterpUsingHr1 = false;
5190 2 : } else if (Util::SameString(ipsc->cAlphaArgs(9), "Hour1")) {
5191 2 : runPeriodInput.firstHrInterpUsingHr1 = true;
5192 : } else {
5193 : // fail-safe default
5194 0 : runPeriodInput.firstHrInterpUsingHr1 = false;
5195 : }
5196 :
5197 64 : runPeriodInput.dayOfWeek = static_cast<int>(runPeriodInput.startWeekDay);
5198 64 : runPeriodInput.isLeapYear = isLeapYear(runPeriodInput.startYear);
5199 :
5200 : // calculate the annual start and end days from the user inputted month and day
5201 64 : runPeriodInput.monWeekDay = 0;
5202 64 : if (runPeriodInput.dayOfWeek != 0 && !ErrorsFound) {
5203 62 : SetupWeekDaysByMonth(state, runPeriodInput.startMonth, runPeriodInput.startDay, runPeriodInput.dayOfWeek, runPeriodInput.monWeekDay);
5204 : }
5205 64 : }
5206 :
5207 55 : if (nRunPeriods == 0 && state.dataSysVars->FullAnnualRun) {
5208 0 : ShowWarningError(state, "No Run Periods input but Full Annual Simulation selected. Adding Run Period to 1/1 through 12/31.");
5209 0 : state.dataWeather->Environment.redimension(++state.dataWeather->NumOfEnvrn);
5210 0 : state.dataWeather->Environment(state.dataWeather->NumOfEnvrn).KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
5211 0 : nRunPeriods = 1;
5212 0 : state.dataGlobal->WeathSimReq = true;
5213 0 : state.dataWeather->RunPeriodInput.allocate(nRunPeriods);
5214 0 : auto &runPerInput1 = state.dataWeather->RunPeriodInput(1);
5215 0 : runPerInput1.startJulianDate = General::OrdinalDay(runPerInput1.startMonth, runPerInput1.startDay, state.dataWeather->LeapYearAdd);
5216 0 : runPerInput1.endJulianDate = General::OrdinalDay(runPerInput1.endMonth, runPerInput1.endDay, state.dataWeather->LeapYearAdd);
5217 0 : runPerInput1.monWeekDay = 0;
5218 0 : if (runPerInput1.dayOfWeek != 0 && !ErrorsFound) {
5219 0 : SetupWeekDaysByMonth(state, runPerInput1.startMonth, runPerInput1.startDay, runPerInput1.dayOfWeek, runPerInput1.monWeekDay);
5220 : }
5221 55 : } else if (nRunPeriods > 1 && state.dataSysVars->FullAnnualRun) {
5222 0 : nRunPeriods = 1;
5223 : }
5224 55 : }
5225 :
5226 1 : void GetRunPeriodDesignData(EnergyPlusData &state, bool &ErrorsFound)
5227 : {
5228 :
5229 : // SUBROUTINE INFORMATION:
5230 : // AUTHOR Linda Lawrie
5231 : // DATE WRITTEN March 2008
5232 :
5233 : // PURPOSE OF THIS SUBROUTINE:
5234 : // This subroutine gets the run period design info from User input and the
5235 : // simulation dates
5236 :
5237 1 : constexpr std::string_view routineName = "GetRunPeriodDesignData";
5238 : // Call Input Get routine to retrieve annual run data
5239 1 : int RPD1 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileDays");
5240 1 : int RPD2 = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SizingPeriod:WeatherFileConditionType");
5241 1 : state.dataWeather->TotRunDesPers = RPD1 + RPD2;
5242 :
5243 1 : state.dataWeather->RunPeriodDesignInput.allocate(RPD1 + RPD2);
5244 :
5245 1 : int Count = 0;
5246 1 : auto const &ipsc = state.dataIPShortCut;
5247 1 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileDays";
5248 2 : for (int i = 1; i <= RPD1; ++i) {
5249 : int NumAlphas; // Number of alphas being input
5250 : int NumNumerics; // Number of Numerics being input
5251 : int IOStat; // IO Status when calling get input subroutine
5252 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5253 1 : ipsc->cCurrentModuleObject,
5254 : i,
5255 1 : ipsc->cAlphaArgs,
5256 : NumAlphas,
5257 1 : ipsc->rNumericArgs,
5258 : NumNumerics,
5259 : IOStat,
5260 1 : ipsc->lNumericFieldBlanks,
5261 1 : ipsc->lAlphaFieldBlanks,
5262 1 : ipsc->cAlphaFieldNames,
5263 1 : ipsc->cNumericFieldNames);
5264 :
5265 1 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5266 1 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5267 1 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5268 1 : state.dataWeather->RunPeriodDesignInput.end(),
5269 2 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5270 2 : state.dataWeather->RunPeriodDesignInput.end()) {
5271 0 : ShowSevereDuplicateName(state, eoh);
5272 0 : ErrorsFound = true;
5273 : }
5274 :
5275 1 : ++Count;
5276 :
5277 1 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5278 1 : runPerDesInput.title = newName;
5279 1 : runPerDesInput.periodType = "User Selected WeatherFile RunPeriod (Design)";
5280 :
5281 : // set the start and end day of month from user input
5282 1 : runPerDesInput.startMonth = int(ipsc->rNumericArgs(1));
5283 1 : runPerDesInput.startDay = int(ipsc->rNumericArgs(2));
5284 1 : runPerDesInput.endMonth = int(ipsc->rNumericArgs(3));
5285 1 : runPerDesInput.endDay = int(ipsc->rNumericArgs(4));
5286 :
5287 1 : switch (runPerDesInput.startMonth) {
5288 0 : case 1:
5289 : case 3:
5290 : case 5:
5291 : case 7:
5292 : case 8:
5293 : case 10:
5294 : case 12: {
5295 0 : if (runPerDesInput.startDay > 31) {
5296 0 : ShowSevereError(state,
5297 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5298 0 : ipsc->cCurrentModuleObject,
5299 0 : runPerDesInput.title,
5300 0 : ipsc->cNumericFieldNames(2),
5301 0 : runPerDesInput.startDay));
5302 0 : ErrorsFound = true;
5303 : }
5304 0 : } break;
5305 1 : case 4:
5306 : case 6:
5307 : case 9:
5308 : case 11: {
5309 1 : if (runPerDesInput.startDay > 30) {
5310 2 : ShowSevereError(state,
5311 3 : format("{}: object={} {} invalid (Day of Month) [{}]",
5312 1 : ipsc->cCurrentModuleObject,
5313 1 : runPerDesInput.title,
5314 1 : ipsc->cNumericFieldNames(2),
5315 1 : runPerDesInput.startDay));
5316 1 : ErrorsFound = true;
5317 : }
5318 1 : } break;
5319 0 : case 2: {
5320 0 : if (runPerDesInput.startDay > 28 + state.dataWeather->LeapYearAdd) {
5321 0 : ShowSevereError(state,
5322 0 : format("{}: object={} {} invalid (Day of Month) [{}]",
5323 0 : ipsc->cCurrentModuleObject,
5324 0 : runPerDesInput.title,
5325 0 : ipsc->cNumericFieldNames(2),
5326 0 : runPerDesInput.startDay));
5327 0 : ErrorsFound = true;
5328 : }
5329 0 : } break;
5330 0 : default: {
5331 0 : ShowSevereError(state,
5332 0 : format("{}: object={} {} invalid (Month) [{}]",
5333 0 : ipsc->cCurrentModuleObject,
5334 0 : runPerDesInput.title,
5335 0 : ipsc->cNumericFieldNames(1),
5336 0 : runPerDesInput.startMonth));
5337 0 : ErrorsFound = true;
5338 0 : } break;
5339 : } // switch
5340 :
5341 1 : if (ipsc->lAlphaFieldBlanks(2)) {
5342 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5343 : } else {
5344 1 : runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
5345 1 : if (runPerDesInput.dayOfWeek < 1 || runPerDesInput.dayOfWeek == 8) {
5346 0 : ShowWarningError(state,
5347 0 : format("{}: object={} {} invalid (Day of Week) [{} for Start is not Valid, Monday will be Used.",
5348 0 : ipsc->cCurrentModuleObject,
5349 0 : runPerDesInput.title,
5350 0 : ipsc->cAlphaFieldNames(1),
5351 0 : ipsc->cAlphaArgs(1)));
5352 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5353 : }
5354 : }
5355 :
5356 : BooleanSwitch b;
5357 1 : if (ipsc->lAlphaFieldBlanks(3)) {
5358 0 : runPerDesInput.useDST = true;
5359 1 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(3)))) != BooleanSwitch::Invalid) {
5360 1 : runPerDesInput.useDST = static_cast<bool>(b);
5361 : } else {
5362 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5363 0 : ErrorsFound = true;
5364 : }
5365 :
5366 1 : if (ipsc->lAlphaFieldBlanks(4)) {
5367 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5368 1 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5369 1 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5370 : } else {
5371 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5372 0 : ErrorsFound = true;
5373 : }
5374 :
5375 : // calculate the annual start and end days from the user inputted month and day
5376 1 : runPerDesInput.startJulianDate = General::OrdinalDay(runPerDesInput.startMonth, runPerDesInput.startDay, state.dataWeather->LeapYearAdd);
5377 1 : runPerDesInput.endJulianDate = General::OrdinalDay(runPerDesInput.endMonth, runPerDesInput.endDay, state.dataWeather->LeapYearAdd);
5378 1 : if (runPerDesInput.startJulianDate <= runPerDesInput.endJulianDate) {
5379 1 : runPerDesInput.totalDays = (runPerDesInput.endJulianDate - runPerDesInput.startJulianDate + 1) * runPerDesInput.numSimYears;
5380 : } else {
5381 0 : runPerDesInput.totalDays = (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - runPerDesInput.startJulianDate + 1 +
5382 0 : runPerDesInput.endJulianDate) *
5383 0 : runPerDesInput.numSimYears;
5384 : }
5385 1 : runPerDesInput.monWeekDay = 0;
5386 1 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5387 1 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5388 0 : SetupWeekDaysByMonth(state,
5389 : runPeriodDesignInput1.startMonth,
5390 : runPeriodDesignInput1.startDay,
5391 : runPeriodDesignInput1.dayOfWeek,
5392 0 : runPeriodDesignInput1.monWeekDay);
5393 : }
5394 1 : }
5395 :
5396 1 : ipsc->cCurrentModuleObject = "SizingPeriod:WeatherFileConditionType";
5397 1 : for (int i = 1; i <= RPD2; ++i) {
5398 : int NumAlphas; // Number of alphas being input
5399 : int NumNumerics; // Number of Numerics being input
5400 : int IOStat; // IO Status when calling get input subroutine
5401 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5402 0 : ipsc->cCurrentModuleObject,
5403 : i,
5404 0 : ipsc->cAlphaArgs,
5405 : NumAlphas,
5406 0 : ipsc->rNumericArgs,
5407 : NumNumerics,
5408 : IOStat,
5409 0 : ipsc->lNumericFieldBlanks,
5410 0 : ipsc->lAlphaFieldBlanks,
5411 0 : ipsc->cAlphaFieldNames,
5412 0 : ipsc->cNumericFieldNames);
5413 0 : std::string newName = Util::makeUPPER(ipsc->cAlphaArgs(1));
5414 :
5415 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, newName};
5416 0 : if (std::find_if(state.dataWeather->RunPeriodDesignInput.begin(),
5417 0 : state.dataWeather->RunPeriodDesignInput.end(),
5418 0 : [&newName](RunPeriodData const &rpd) { return newName == rpd.title; }) !=
5419 0 : state.dataWeather->RunPeriodDesignInput.end()) {
5420 0 : ShowSevereDuplicateName(state, eoh);
5421 0 : ErrorsFound = true;
5422 : }
5423 :
5424 0 : ++Count;
5425 0 : auto &runPerDesInput = state.dataWeather->RunPeriodDesignInput(Count);
5426 0 : runPerDesInput.title = ipsc->cAlphaArgs(1);
5427 0 : runPerDesInput.periodType = "User Selected WeatherFile Typical/Extreme Period (Design)=" + ipsc->cAlphaArgs(2);
5428 :
5429 : // Period Selection
5430 0 : if (ipsc->lAlphaFieldBlanks(2)) {
5431 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
5432 0 : ErrorsFound = true;
5433 : } else {
5434 0 : int WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue);
5435 0 : if (WhichPeriod == 0) {
5436 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue1);
5437 : if (WhichPeriod != 0) {
5438 : }
5439 : }
5440 0 : if (WhichPeriod == 0) {
5441 0 : WhichPeriod = Util::FindItem(ipsc->cAlphaArgs(2), state.dataWeather->TypicalExtremePeriods, &TypicalExtremeData::MatchValue2);
5442 0 : if (WhichPeriod != 0) {
5443 0 : ShowWarningError(state,
5444 0 : format("{}: object={} {}={} matched to {}",
5445 0 : ipsc->cCurrentModuleObject,
5446 0 : runPerDesInput.title,
5447 0 : ipsc->cAlphaFieldNames(2),
5448 0 : ipsc->cAlphaArgs(2),
5449 0 : state.dataWeather->TypicalExtremePeriods(WhichPeriod).MatchValue2));
5450 : }
5451 : }
5452 0 : if (WhichPeriod == 0) {
5453 0 : ShowSevereError(state,
5454 0 : format("{}: object={} {} invalid (not on Weather File)={}",
5455 0 : ipsc->cCurrentModuleObject,
5456 0 : runPerDesInput.title,
5457 0 : ipsc->cAlphaFieldNames(2),
5458 0 : ipsc->cAlphaArgs(2)));
5459 0 : ErrorsFound = true;
5460 : } else {
5461 0 : auto const &typicalExtPer = state.dataWeather->TypicalExtremePeriods(WhichPeriod);
5462 0 : runPerDesInput.startDay = typicalExtPer.StartDay;
5463 0 : runPerDesInput.startMonth = typicalExtPer.StartMonth;
5464 0 : runPerDesInput.startJulianDate = typicalExtPer.StartJDay;
5465 0 : runPerDesInput.endDay = typicalExtPer.EndDay;
5466 0 : runPerDesInput.endMonth = typicalExtPer.EndMonth;
5467 0 : runPerDesInput.endJulianDate = typicalExtPer.EndJDay;
5468 0 : runPerDesInput.totalDays = typicalExtPer.TotalDays;
5469 : }
5470 : }
5471 :
5472 0 : if (ipsc->lAlphaFieldBlanks(3)) {
5473 0 : runPerDesInput.dayOfWeek = (int)Sched::DayType::Monday; // Defaults to Monday
5474 : } else {
5475 0 : runPerDesInput.dayOfWeek = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(3));
5476 0 : if (runPerDesInput.dayOfWeek < (int)Sched::DayType::Sunday || runPerDesInput.dayOfWeek == (int)Sched::DayType::Holiday) {
5477 : // Sunday-Saturday, SummerDesignDay, WinterDesignDay, CustomDay1, and CustomDay2 are all valid. Holiday is not valid.
5478 : // The input processor should trap invalid key choices, so this should never trip.
5479 0 : assert(false);
5480 : }
5481 : }
5482 :
5483 : BooleanSwitch b;
5484 0 : if (ipsc->lAlphaFieldBlanks(4)) {
5485 0 : runPerDesInput.useDST = true;
5486 0 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(4)))) != BooleanSwitch::Invalid) {
5487 0 : runPerDesInput.useDST = static_cast<bool>(b);
5488 : } else {
5489 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5490 0 : ErrorsFound = true;
5491 : }
5492 :
5493 0 : if (ipsc->lAlphaFieldBlanks(5)) {
5494 0 : runPerDesInput.useRain = runPerDesInput.useSnow = true;
5495 0 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(5)))) != BooleanSwitch::Invalid) {
5496 0 : runPerDesInput.useRain = runPerDesInput.useSnow = static_cast<bool>(b);
5497 : } else {
5498 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
5499 0 : ErrorsFound = true;
5500 : }
5501 0 : auto &runPeriodDesignInput1 = state.dataWeather->RunPeriodDesignInput(1);
5502 0 : runPeriodDesignInput1.monWeekDay = 0;
5503 0 : if (runPeriodDesignInput1.dayOfWeek != 0 && !ErrorsFound) {
5504 0 : SetupWeekDaysByMonth(state,
5505 : runPeriodDesignInput1.startMonth,
5506 : runPeriodDesignInput1.startDay,
5507 : runPeriodDesignInput1.dayOfWeek,
5508 0 : runPeriodDesignInput1.monWeekDay);
5509 : }
5510 0 : }
5511 1 : }
5512 :
5513 50 : void GetSpecialDayPeriodData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5514 : {
5515 :
5516 : // SUBROUTINE INFORMATION:
5517 : // AUTHOR Linda Lawrie
5518 : // DATE WRITTEN June 2000
5519 :
5520 : // PURPOSE OF THIS SUBROUTINE:
5521 : // This subroutine reads any special day period data from the IDF and
5522 : // processes it into the data structure that will drive the values
5523 : // in the SpecialDayTypes array.
5524 :
5525 : // METHODOLOGY EMPLOYED:
5526 : // Processes the following IDD definition:
5527 : // SpecialDayPeriod,
5528 : // \memo This object sets up holidays/special days to be used during weather file
5529 : // \memo run periods. (These are not used with DesignDay objects.)
5530 : // \memo Depending on the value in the run period, days on the weather file may also
5531 : // \memo be used. However, the weather file specification will take precedence over
5532 : // \memo any specification shown here. (No error message on duplicate days or overlapping
5533 : // \memo days).
5534 : // A1, \field Holiday Name
5535 : // A2, \field StartDate
5536 : // \memo Dates can be several formats:
5537 : // \memo <number>/<number> (month/day)
5538 : // \memo <number> Month
5539 : // \memo Month <number>
5540 : // \memo Months are January, February, March, April, May, June, July, August, September, October, November, December
5541 : // \memo Months can be the first 3 letters of the month
5542 : // \note will eventually allow: 3 Monday April (meaning 3rd Monday in April)
5543 : // N1, \field duration (number of days)
5544 : // A3; \field SpecialDayType
5545 : // \note SpecialDayType selects the schedules appropriate for each day so labeled
5546 : // \type choice
5547 : // \key Holiday
5548 : // \key SummerDesignDay
5549 : // \key WinterDesignDay
5550 : // \key CustomDay1
5551 : // \key CustomDay2
5552 :
5553 50 : constexpr std::string_view routineName = "GetSpecialDayPeriodData";
5554 :
5555 50 : auto const &ipsc = state.dataIPShortCut;
5556 50 : ipsc->cCurrentModuleObject = "RunPeriodControl:SpecialDays";
5557 50 : int NumSpecDays = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5558 : int Count;
5559 50 : if (allocated(state.dataWeather->SpecialDays)) { // EPW already allocated the array
5560 25 : Count = state.dataWeather->NumSpecialDays - NumSpecDays + 1;
5561 : } else {
5562 25 : state.dataWeather->SpecialDays.allocate(NumSpecDays);
5563 25 : state.dataWeather->NumSpecialDays = NumSpecDays;
5564 25 : Count = 1;
5565 : }
5566 :
5567 50 : Array1D_string AlphArray(3);
5568 : int NumAlphas;
5569 50 : Array1D<Real64> Duration(1);
5570 : int NumNumbers;
5571 : int IOStat;
5572 :
5573 50 : for (int i = 1; i <= NumSpecDays; ++i, ++Count) {
5574 :
5575 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
5576 0 : state, ipsc->cCurrentModuleObject, i, AlphArray, NumAlphas, Duration, NumNumbers, IOStat);
5577 :
5578 0 : auto &specialDay = state.dataWeather->SpecialDays(Count);
5579 :
5580 0 : specialDay.Name = AlphArray(1);
5581 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, specialDay.Name};
5582 :
5583 : int PMonth;
5584 : int PDay;
5585 : int PWeekDay;
5586 : DateType dateType;
5587 0 : General::ProcessDateString(state, AlphArray(2), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
5588 0 : if (dateType == DateType::MonthDay) {
5589 0 : specialDay.dateType = dateType;
5590 0 : specialDay.Month = PMonth;
5591 0 : specialDay.Day = PDay;
5592 0 : specialDay.WeekDay = 0;
5593 0 : specialDay.CompDate = PMonth * 32 + PDay;
5594 0 : specialDay.WthrFile = false;
5595 0 : } else if (dateType != DateType::Invalid) {
5596 0 : specialDay.dateType = dateType;
5597 0 : specialDay.Month = PMonth;
5598 0 : specialDay.Day = PDay;
5599 0 : specialDay.WeekDay = PWeekDay;
5600 0 : specialDay.CompDate = 0;
5601 0 : specialDay.WthrFile = false;
5602 : } else {
5603 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
5604 0 : ErrorsFound = true;
5605 : }
5606 :
5607 0 : if (Duration(1) > 0) {
5608 0 : specialDay.Duration = int(Duration(1));
5609 : } else {
5610 0 : ShowSevereError(
5611 0 : state, format("{}: {} Invalid {}={:.0T}", ipsc->cCurrentModuleObject, AlphArray(1), ipsc->cNumericFieldNames(1), Duration(1)));
5612 0 : ErrorsFound = true;
5613 : }
5614 :
5615 0 : int DayType = getEnumValue(Sched::dayTypeNamesUC, AlphArray(3));
5616 0 : if (DayType == 0) {
5617 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), AlphArray(3));
5618 0 : ErrorsFound = true;
5619 : } else {
5620 0 : specialDay.DayType = DayType;
5621 : }
5622 : }
5623 50 : }
5624 :
5625 5 : void CalcSpecialDayTypes(EnergyPlusData &state)
5626 : {
5627 :
5628 : // SUBROUTINE INFORMATION:
5629 : // AUTHOR Linda Lawrie
5630 : // DATE WRITTEN June 2000
5631 :
5632 : // PURPOSE OF THIS SUBROUTINE:
5633 : // This subroutine creates the array of Special Day types used during
5634 : // the simulation.
5635 :
5636 : // METHODOLOGY EMPLOYED:
5637 : // Sets up the SpecialDayTypes array that then is used during simulation.
5638 :
5639 5 : state.dataWeather->SpecialDayTypes = 0; // Initialize/Reset Special Day Types array
5640 :
5641 5 : for (int i = 1; i <= state.dataWeather->NumSpecialDays; ++i) {
5642 0 : auto const &specialDay = state.dataWeather->SpecialDays(i);
5643 0 : if (specialDay.WthrFile) continue;
5644 :
5645 0 : int Warn = 0;
5646 :
5647 0 : int JDay = General::OrdinalDay(specialDay.Month, specialDay.Day, state.dataWeather->LeapYearAdd) - 1;
5648 :
5649 0 : for (int j = 1; j <= specialDay.Duration; ++j) {
5650 0 : ++JDay;
5651 0 : if (JDay > 366) {
5652 0 : ShowWarningError(state, format("SpecialDay={} causes index of more than 366, ignoring those beyond 366", specialDay.Name));
5653 : } else {
5654 0 : if (state.dataWeather->SpecialDayTypes(JDay) != 0 && Warn == 0) {
5655 0 : ShowWarningError(state, format("SpecialDay={} attempted overwrite of previous set special day", specialDay.Name));
5656 0 : Warn = 1;
5657 0 : } else if (state.dataWeather->SpecialDayTypes(JDay) == 0) {
5658 0 : state.dataWeather->SpecialDayTypes(JDay) = specialDay.DayType;
5659 : }
5660 : }
5661 : }
5662 : }
5663 5 : }
5664 :
5665 50 : void GetDSTData(EnergyPlusData &state, bool &ErrorsFound) // will be set to true if severe errors are found in inputs
5666 : {
5667 :
5668 : // SUBROUTINE INFORMATION:
5669 : // AUTHOR Linda Lawrie
5670 : // DATE WRITTEN August 2000
5671 :
5672 : // PURPOSE OF THIS SUBROUTINE:
5673 : // This subroutine gets a possible "Daylight Saving Period" from the IDF. Using this
5674 : // will overwrite any prior DST data.
5675 :
5676 : // METHODOLOGY EMPLOYED:
5677 : // Processes the following IDD definition:
5678 : // DaylightSavingPeriod,
5679 : // \memo This object sets up the Daylight Saving period for any RunPeriod.
5680 : // \memo Ignores any DaylightSavingperiod values on the weather file and uses this definition.
5681 : // \memo (These are not used with DesignDay objects.)
5682 : // A1, \field StartDate
5683 : // A2, \field EndDate
5684 : // \memo Dates can be several formats:
5685 : // \memo <number>/<number> (month/day)
5686 : // \memo <number> <Month>
5687 : // \memo <Month> <number>
5688 : // \memo <Nth> <Weekday> in <Month)
5689 : // \memo Last <WeekDay> in <Month>
5690 : // \memo <Month> can be January, February, March, April, May, June, July, August, September,
5691 : // October, November, December
5692 : // \memo Months can be the first 3 letters of the month
5693 : // \memo <Weekday> can be Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
5694 : // \memo <Nth> can be 1 or 1st, 2 or 2nd, etc. up to 5(?)
5695 :
5696 50 : constexpr std::string_view routineName = "GetDSTData";
5697 :
5698 50 : auto const &ipsc = state.dataIPShortCut;
5699 50 : ipsc->cCurrentModuleObject = "RunPeriodControl:DaylightSavingTime";
5700 50 : int NumFound = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
5701 :
5702 50 : if (NumFound == 1) {
5703 : int NumAlphas;
5704 : int IOStat;
5705 : int NumNumbers;
5706 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5707 0 : ipsc->cCurrentModuleObject,
5708 : 1,
5709 0 : ipsc->cAlphaArgs,
5710 : NumAlphas,
5711 0 : ipsc->rNumericArgs,
5712 : NumNumbers,
5713 : IOStat,
5714 0 : ipsc->lNumericFieldBlanks,
5715 0 : ipsc->lAlphaFieldBlanks,
5716 0 : ipsc->cAlphaFieldNames,
5717 0 : ipsc->cNumericFieldNames);
5718 :
5719 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
5720 :
5721 0 : if (NumAlphas != 2) {
5722 0 : ShowSevereError(state, format("{}: Insufficient fields, must have Start AND End Dates", ipsc->cCurrentModuleObject));
5723 0 : ErrorsFound = true;
5724 : } else { // Correct number of arguments
5725 0 : General::ProcessDateString(state,
5726 0 : ipsc->cAlphaArgs(1),
5727 0 : state.dataWeather->IDFDST.StMon,
5728 0 : state.dataWeather->IDFDST.StDay,
5729 0 : state.dataWeather->IDFDST.StWeekDay,
5730 0 : state.dataWeather->IDFDST.StDateType,
5731 : ErrorsFound);
5732 0 : if (state.dataWeather->IDFDST.StDateType == DateType::Invalid) {
5733 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
5734 0 : ErrorsFound = true;
5735 : }
5736 0 : General::ProcessDateString(state,
5737 0 : ipsc->cAlphaArgs(2),
5738 0 : state.dataWeather->IDFDST.EnMon,
5739 0 : state.dataWeather->IDFDST.EnDay,
5740 0 : state.dataWeather->IDFDST.EnWeekDay,
5741 0 : state.dataWeather->IDFDST.EnDateType,
5742 : ErrorsFound);
5743 0 : if (state.dataWeather->IDFDST.EnDateType == DateType::Invalid) {
5744 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
5745 0 : ErrorsFound = true;
5746 : }
5747 0 : state.dataWeather->IDFDaylightSaving = true;
5748 : }
5749 50 : } else if (NumFound > 1) {
5750 0 : ShowSevereError(state, format("{}: Too many objects in Input File, only one allowed.", ipsc->cCurrentModuleObject));
5751 0 : ErrorsFound = true;
5752 : }
5753 50 : }
5754 :
5755 106 : void GetDesignDayData(EnergyPlusData &state,
5756 : int TotDesDays, // Total number of Design days to Setup
5757 : bool &ErrorsFound)
5758 : {
5759 :
5760 : // SUBROUTINE INFORMATION:
5761 : // AUTHOR Richard Liesen
5762 : // DATE WRITTEN September 1997
5763 :
5764 : // PURPOSE OF THIS SUBROUTINE:
5765 : // This subroutine retrieves the design day info from user input file
5766 : // which is later to be used in the Setup Design Day Routine.
5767 :
5768 : // REFERENCES:
5769 : // SizingPeriod:DesignDay,
5770 : // A1, \field Name
5771 : // N1, \field Month
5772 : // N2, \field Day of Month
5773 : // A2, \field Day Type
5774 : // N3, \field Maximum Dry-Bulb Temperature
5775 : // N4, \field Daily Dry-Bulb Temperature Range
5776 : // A3, \field Dry-Bulb Temperature Range Modifier Type
5777 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
5778 : // A5, \field Humidity Condition Type
5779 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
5780 : // A6, \field Humidity Condition Day Schedule Name
5781 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
5782 : // N7, \field Enthalpy at Maximum Dry-Bulb !will require units transition.
5783 : // N8, \field Daily Wet-Bulb Temperature Range
5784 : // N9, \field Barometric Pressure
5785 : // N10, \field Wind Speed
5786 : // N11, \field Wind Direction
5787 : // A7, \field Rain Indicator
5788 : // A8, \field Snow Indicator
5789 : // A9, \field Daylight Saving Time Indicator
5790 : // A10, \field Solar Model Indicator
5791 : // A11, \field Beam Solar Day Schedule Name
5792 : // A12, \field Diffuse Solar Day Schedule Name
5793 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
5794 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
5795 : // N14; \field Sky Clearness
5796 :
5797 : static constexpr std::array<std::string_view, static_cast<int>(DesDayHumIndType::Num)> DesDayHumIndTypeStringRep = {
5798 : "Wetbulb [C]",
5799 : "Dewpoint [C]",
5800 : "Enthalpy [J/kg]",
5801 : "Humidity Ratio []",
5802 : "Schedule []",
5803 : "WetBulbProfileDefaultMultipliers []",
5804 : "WetBulbProfileDifferenceSchedule []",
5805 : "WetBulbProfileMultiplierSchedule []"};
5806 :
5807 : // Below are the 2009 fractions, HOF, Chap 14, Table 6
5808 : 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,
5809 : 0.05, 0.00, 0.00, 0.06, 0.14, 0.24, 0.39, 0.50, 0.59, 0.68, 0.75, 0.82};
5810 :
5811 : static constexpr std::string_view routineName = "GetDesignDayData";
5812 :
5813 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5814 : Constant::Units unitType;
5815 :
5816 106 : state.dataWeather->DesDayInput.allocate(TotDesDays); // Allocate the array to the # of DD's
5817 106 : state.dataWeather->desDayMods.allocate(TotDesDays);
5818 288 : for (int iDD = 1; iDD <= TotDesDays; ++iDD)
5819 182 : state.dataWeather->desDayMods(iDD).allocate(state.dataGlobal->TimeStepsInHour, Constant::iHoursInDay);
5820 :
5821 106 : state.dataWeather->spSiteSchedules.dimension(TotDesDays, Weather::SPSiteSchedules());
5822 :
5823 106 : if (state.dataSysVars->ReverseDD && TotDesDays <= 1) {
5824 0 : ShowSevereError(state, "GetDesignDayData: Reverse Design Day requested but # Design Days <=1");
5825 : }
5826 :
5827 106 : auto const &ipsc = state.dataIPShortCut;
5828 106 : ipsc->cCurrentModuleObject = "SizingPeriod:DesignDay";
5829 288 : for (int iDesDay = 1; iDesDay <= TotDesDays; ++iDesDay) {
5830 :
5831 : int EnvrnNum;
5832 182 : if (!state.dataSysVars->ReverseDD) {
5833 182 : EnvrnNum = iDesDay;
5834 0 : } else if (iDesDay == 1 && TotDesDays > 1) {
5835 0 : EnvrnNum = 2;
5836 0 : } else if (iDesDay == 2) {
5837 0 : EnvrnNum = 1;
5838 : } else {
5839 0 : EnvrnNum = iDesDay;
5840 : }
5841 :
5842 : // Call Input Get routine to retrieve design day data
5843 182 : bool MaxDryBulbEntered = false;
5844 182 : bool PressureEntered = false;
5845 : int NumAlpha; // Number of material alpha names being passed
5846 : int NumNumerics; // Number of material properties being passed
5847 : int IOStat; // IO Status when calling get input subroutine
5848 364 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
5849 182 : ipsc->cCurrentModuleObject,
5850 : iDesDay,
5851 182 : ipsc->cAlphaArgs,
5852 : NumAlpha,
5853 182 : ipsc->rNumericArgs,
5854 : NumNumerics,
5855 : IOStat,
5856 182 : ipsc->lNumericFieldBlanks,
5857 182 : ipsc->lAlphaFieldBlanks,
5858 182 : ipsc->cAlphaFieldNames,
5859 182 : ipsc->cNumericFieldNames);
5860 :
5861 182 : auto &envCurr = state.dataWeather->Environment(EnvrnNum);
5862 182 : auto &desDayInput = state.dataWeather->DesDayInput(EnvrnNum);
5863 182 : desDayInput.Title = ipsc->cAlphaArgs(1); // Environment name
5864 182 : envCurr.Title = desDayInput.Title;
5865 :
5866 182 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, desDayInput.Title};
5867 :
5868 : // N3, \field Maximum Dry-Bulb Temperature
5869 : // N4, \field Daily Dry-Bulb Temperature Range
5870 : // N9, \field Barometric Pressure
5871 : // N10, \field Wind Speed
5872 : // N11, \field Wind Direction
5873 182 : desDayInput.MaxDryBulb = ipsc->rNumericArgs(3); // Maximum Dry-Bulb Temperature (C)
5874 182 : MaxDryBulbEntered = !ipsc->lNumericFieldBlanks(3);
5875 182 : desDayInput.DailyDBRange = ipsc->rNumericArgs(4); // Daily dry-bulb temperature range (deltaC)
5876 182 : desDayInput.PressBarom = ipsc->rNumericArgs(9); // Atmospheric/Barometric Pressure (Pascals)
5877 182 : PressureEntered = !ipsc->lNumericFieldBlanks(9);
5878 182 : desDayInput.PressureEntered = PressureEntered;
5879 182 : desDayInput.WindSpeed = ipsc->rNumericArgs(10); // Wind Speed (m/s)
5880 182 : desDayInput.WindDir = mod(ipsc->rNumericArgs(11), 360.0); // Wind Direction
5881 : // (degrees clockwise from North, N=0, E=90, S=180, W=270)
5882 : // N1, \field Month
5883 : // N2, \field Day of Month
5884 : // N12, \field ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
5885 : // N13, \field ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
5886 : // N8, \field Daily Wet-Bulb Temperature Range
5887 182 : desDayInput.Month = int(ipsc->rNumericArgs(1)); // Month of Year ( 1 - 12 )
5888 182 : desDayInput.DayOfMonth = int(ipsc->rNumericArgs(2)); // Day of Month ( 1 - 31 )
5889 182 : desDayInput.TauB = ipsc->rNumericArgs(12); // beam tau >= 0
5890 182 : desDayInput.TauD = ipsc->rNumericArgs(13); // diffuse tau >= 0
5891 182 : desDayInput.DailyWBRange = ipsc->rNumericArgs(8); // Daily wet-bulb temperature range (deltaC)
5892 :
5893 : // N14; \field Sky Clearness
5894 182 : desDayInput.SkyClear = ipsc->rNumericArgs(14); // Sky Clearness (0 to 1)
5895 :
5896 : // N15, \field Maximum Warmup Days Between Sizing Periods
5897 182 : if (ipsc->lNumericFieldBlanks(15)) {
5898 : // Default to -1 if not input
5899 182 : desDayInput.maxWarmupDays = -1;
5900 : } else {
5901 0 : desDayInput.maxWarmupDays = int(ipsc->rNumericArgs(15));
5902 : }
5903 : // A13, \field Begin Environment Reset Mode
5904 182 : if (ipsc->lAlphaFieldBlanks(13)) {
5905 180 : desDayInput.suppressBegEnvReset = false;
5906 : } else {
5907 2 : if (Util::SameString(ipsc->cAlphaArgs(13), "FullResetAtBeginEnvironment")) {
5908 2 : desDayInput.suppressBegEnvReset = false;
5909 0 : } else if (Util::SameString(ipsc->cAlphaArgs(13), "SuppressThermalResetAtBeginEnvironment")) {
5910 0 : desDayInput.suppressBegEnvReset = true;
5911 : }
5912 : }
5913 : // for PerformancePrecisionTradeoffs
5914 182 : if (state.dataEnvrn->forceBeginEnvResetSuppress) {
5915 0 : desDayInput.suppressBegEnvReset = true;
5916 : }
5917 : // A7, \field Rain Indicator
5918 : BooleanSwitch b;
5919 :
5920 182 : if (ipsc->lAlphaFieldBlanks(7)) {
5921 0 : desDayInput.RainInd = 0;
5922 182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(7)))) != BooleanSwitch::Invalid) {
5923 182 : desDayInput.RainInd = (int)b;
5924 : } else {
5925 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(7), ipsc->cAlphaArgs(7), "No");
5926 0 : desDayInput.RainInd = 0;
5927 : }
5928 :
5929 : // A8, \field Snow Indicator
5930 182 : if (ipsc->lAlphaFieldBlanks(8)) {
5931 0 : desDayInput.SnowInd = 0;
5932 182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(8)))) != BooleanSwitch::Invalid) {
5933 182 : desDayInput.SnowInd = (int)b;
5934 : } else {
5935 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(8), ipsc->cAlphaArgs(8), "No");
5936 0 : desDayInput.SnowInd = 0;
5937 : }
5938 :
5939 : // A3, \field Dry-Bulb Temperature Range Modifier Type
5940 : // check DB profile input
5941 182 : if (ipsc->lAlphaFieldBlanks(3)) {
5942 166 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
5943 32 : } else if ((desDayInput.dryBulbRangeType = static_cast<DesDayDryBulbRangeType>(
5944 16 : getEnumValue(DesDayDryBulbRangeTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))))) != DesDayDryBulbRangeType::Invalid) {
5945 : } else {
5946 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5947 0 : ErrorsFound = true;
5948 0 : desDayInput.dryBulbRangeType = DesDayDryBulbRangeType::Default;
5949 : }
5950 :
5951 : // std::string units; // not used
5952 182 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
5953 : // units = "[]";
5954 0 : unitType = Constant::Units::None;
5955 182 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) {
5956 : // units = "[deltaC]";
5957 0 : unitType = Constant::Units::deltaC;
5958 182 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
5959 : // units = "[C]";
5960 0 : unitType = Constant::Units::C;
5961 : }
5962 :
5963 182 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile && !MaxDryBulbEntered && ipsc->cAlphaArgs(3) != "invalid field") {
5964 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(3), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
5965 0 : ErrorsFound = true;
5966 : }
5967 :
5968 : // Assume either "multiplier" option will make full use of range...
5969 182 : if (desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Difference &&
5970 182 : desDayInput.dryBulbRangeType != DesDayDryBulbRangeType::Profile) {
5971 182 : Real64 testval = desDayInput.MaxDryBulb - desDayInput.DailyDBRange;
5972 182 : if (testval < -90.0 || testval > 70.0) {
5973 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
5974 0 : ShowContinueError(state, format("{} ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(3), testval));
5975 0 : ErrorsFound = true;
5976 : }
5977 : }
5978 :
5979 : // A4, \field Dry-Bulb Temperature Range Modifier Day Schedule Name
5980 182 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Default) {
5981 : // Default dry-bulb temperature Range
5982 182 : Real64 LastHrValue = DefaultTempRangeMult[23];
5983 4550 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
5984 29208 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
5985 24840 : Real64 WNow = state.dataWeather->Interpolation(ts);
5986 24840 : Real64 WPrev = 1.0 - WNow;
5987 24840 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutDryBulbTemp =
5988 24840 : LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
5989 : }
5990 4368 : LastHrValue = DefaultTempRangeMult[hour - 1];
5991 : }
5992 :
5993 0 : } else if (ipsc->lAlphaFieldBlanks(4)) {
5994 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaFieldNames(3), "SCHEDULE");
5995 0 : ErrorsFound = true;
5996 0 : } else if ((desDayInput.tempRangeSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(4))) == nullptr) {
5997 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
5998 0 : ErrorsFound = true;
5999 :
6000 : } else {
6001 0 : std::vector<Real64> const &dayVals = desDayInput.tempRangeSched->getDayVals(state);
6002 0 : auto &desDayModEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6003 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
6004 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts) {
6005 0 : desDayModEnvrn(ts + 1, hr + 1).OutDryBulbTemp = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6006 : }
6007 : }
6008 :
6009 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6010 0 : state.dataWeather->spSiteSchedNums.end(),
6011 0 : desDayInput.tempRangeSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6012 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.tempRangeSched->Num);
6013 0 : SetupOutputVariable(state,
6014 : "Sizing Period Site Drybulb Temperature Range Modifier Schedule Value",
6015 : unitType,
6016 0 : state.dataWeather->spSiteSchedules(EnvrnNum).OutDryBulbTemp,
6017 : OutputProcessor::TimeStepType::Zone,
6018 : OutputProcessor::StoreType::Average,
6019 0 : ipsc->cAlphaArgs(4));
6020 : }
6021 :
6022 0 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Multiplier) {
6023 0 : if (!desDayInput.tempRangeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
6024 0 : Sched::ShowSevereBadMinMax(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0, Clusive::In, 1.0);
6025 0 : ErrorsFound = true;
6026 : }
6027 0 : } else if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Difference) { // delta, must be > 0.0
6028 0 : if (!desDayInput.tempRangeSched->checkMinVal(state, Clusive::In, 0.0)) {
6029 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4), Clusive::In, 0.0);
6030 0 : ErrorsFound = true;
6031 : }
6032 : }
6033 :
6034 0 : auto const &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6035 0 : Real64 testval = std::numeric_limits<Real64>::min();
6036 0 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
6037 0 : for (int iTS = 1; iTS <= state.dataGlobal->TimeStepsInHour; ++iTS) {
6038 0 : if (desDayModsEnvrn(iTS, iHr).OutDryBulbTemp > testval) testval = desDayModsEnvrn(iTS, iHr).OutDryBulbTemp;
6039 : }
6040 : }
6041 :
6042 0 : if (desDayInput.dryBulbRangeType == DesDayDryBulbRangeType::Profile) {
6043 0 : if (MaxDryBulbEntered) {
6044 0 : ShowWarningError(state, format("{}=\"{}\", data override.", ipsc->cCurrentModuleObject, desDayInput.Title));
6045 0 : ShowContinueError(state, format("..{}=[{:.2R}] will be overwritten.", ipsc->cNumericFieldNames(3), desDayInput.MaxDryBulb));
6046 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3)));
6047 0 : ShowContinueError(state, format("..with max value=[{:.2R}].", testval));
6048 : }
6049 0 : desDayInput.MaxDryBulb = testval;
6050 : }
6051 :
6052 0 : testval = desDayInput.MaxDryBulb - testval;
6053 0 : if (testval < -90.0 || testval > 70.0) {
6054 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6055 : // should this be cNumericFieldNames?
6056 0 : ShowContinueError(state, format("{} = ({:.2R}) is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(4), testval));
6057 0 : ErrorsFound = true;
6058 : }
6059 : }
6060 :
6061 : // A5, \field Humidity Condition Type
6062 182 : desDayInput.HumIndType = static_cast<DesDayHumIndType>(getEnumValue(DesDayHumIndTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
6063 :
6064 182 : switch (desDayInput.HumIndType) {
6065 174 : case DesDayHumIndType::WetBulb: {
6066 : // N5, \field Wetbulb or DewPoint at Maximum Dry-Bulb
6067 174 : if (ipsc->lNumericFieldBlanks(5)) {
6068 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6069 0 : ErrorsFound = true;
6070 : } else {
6071 174 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6072 : }
6073 :
6074 174 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6075 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6076 0 : ShowContinueError(
6077 : state,
6078 0 : format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - WetBulb", desDayInput.HumIndValue));
6079 0 : ErrorsFound = true;
6080 : }
6081 174 : } break;
6082 :
6083 0 : case DesDayHumIndType::DewPoint: {
6084 0 : if (ipsc->lNumericFieldBlanks(5)) {
6085 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6086 0 : ErrorsFound = true;
6087 : } else {
6088 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6089 : }
6090 :
6091 0 : if (desDayInput.HumIndValue < -90.0 || desDayInput.HumIndValue > 70.0) {
6092 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6093 0 : ShowContinueError(
6094 : state,
6095 0 : format("{} = {:.2R} is out of range [-90.0, 70.0]", ipsc->cAlphaFieldNames(5) + " - DewPoint", desDayInput.HumIndValue));
6096 0 : ErrorsFound = true;
6097 : }
6098 0 : } break;
6099 :
6100 0 : case DesDayHumIndType::HumRatio: {
6101 : // N6, \field Humidity Ratio at Maximum Dry-Bulb
6102 0 : if (ipsc->lNumericFieldBlanks(6)) {
6103 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(6), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6104 0 : ErrorsFound = true;
6105 : } else {
6106 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(6); // Humidity Indicating Conditions at Max Dry-Bulb
6107 : }
6108 :
6109 0 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 0.03) {
6110 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6111 0 : ShowContinueError(
6112 : state,
6113 0 : format("{} = {:.2R} is out of range [0.0, 0.03]", ipsc->cAlphaFieldNames(5) + " - Humidity-Ratio", desDayInput.HumIndValue));
6114 0 : ErrorsFound = true;
6115 : }
6116 0 : } break;
6117 :
6118 1 : case DesDayHumIndType::Enthalpy: {
6119 : // N7, \field Enthalpy at Maximum Dry-Bulb {J/kg}.
6120 1 : if (ipsc->lNumericFieldBlanks(7)) {
6121 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(7), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6122 0 : ErrorsFound = true;
6123 : } else {
6124 1 : desDayInput.HumIndValue = ipsc->rNumericArgs(7); // Humidity Indicating Conditions at Max Dry-Bulb
6125 : }
6126 :
6127 1 : desDayInput.HumIndType = DesDayHumIndType::Enthalpy;
6128 1 : if (desDayInput.HumIndValue < 0.0 || desDayInput.HumIndValue > 130000.0) {
6129 0 : ShowSevereError(state, format("{}: {} = {}", routineName, ipsc->cCurrentModuleObject, desDayInput.Title));
6130 0 : ShowContinueError(
6131 : state,
6132 0 : format("{} = {.0R} is out of range [0.0, 130000.0]", ipsc->cAlphaFieldNames(5) + " - Enthalpy", desDayInput.HumIndValue));
6133 0 : ErrorsFound = true;
6134 : }
6135 1 : } break;
6136 :
6137 0 : case DesDayHumIndType::RelHumSch: {
6138 : // units = "[%]";
6139 0 : unitType = Constant::Units::Perc;
6140 0 : } break;
6141 :
6142 0 : case DesDayHumIndType::WBProfMul: {
6143 : // units = "[]";
6144 0 : unitType = Constant::Units::None;
6145 0 : if (ipsc->lNumericFieldBlanks(5)) {
6146 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6147 0 : ErrorsFound = true;
6148 : } else {
6149 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6150 : }
6151 0 : } break;
6152 :
6153 0 : case DesDayHumIndType::WBProfDif: {
6154 : // units = "[]";
6155 0 : unitType = Constant::Units::None;
6156 0 : if (ipsc->lNumericFieldBlanks(5)) {
6157 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6158 0 : ErrorsFound = true;
6159 : } else {
6160 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6161 : }
6162 0 : } break;
6163 :
6164 7 : case DesDayHumIndType::WBProfDef: {
6165 7 : if (ipsc->lNumericFieldBlanks(5)) {
6166 0 : ShowSevereEmptyField(state, eoh, ipsc->cNumericFieldNames(5), ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5));
6167 0 : ErrorsFound = true;
6168 : } else {
6169 7 : desDayInput.HumIndValue = ipsc->rNumericArgs(5); // Humidity Indicating Conditions at Max Dry-Bulb
6170 : }
6171 7 : } break;
6172 :
6173 0 : default: {
6174 0 : ShowWarningError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6175 0 : ShowContinueError(state, format("..invalid field: {}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6176 0 : ShowContinueError(state, "WetBulb will be used. Maximum Dry Bulb will be used as WetBulb at Maximum Dry Bulb.");
6177 0 : desDayInput.HumIndType = DesDayHumIndType::WetBulb;
6178 0 : desDayInput.HumIndValue = ipsc->rNumericArgs(3);
6179 0 : } break;
6180 : } // switch (desDayInput.HumIndType)
6181 :
6182 : // resolve humidity schedule if needed
6183 : // A6, \field Humidity Condition Day Schedule Name
6184 182 : if (desDayInput.HumIndType == DesDayHumIndType::RelHumSch || desDayInput.HumIndType == DesDayHumIndType::WBProfMul ||
6185 182 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6186 0 : if (ipsc->lAlphaFieldBlanks(6)) {
6187 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6188 0 : ErrorsFound = true;
6189 0 : } else if ((desDayInput.humIndSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(6))) == nullptr) {
6190 0 : ShowWarningItemNotFound(state,
6191 : eoh,
6192 0 : ipsc->cAlphaFieldNames(6),
6193 0 : ipsc->cAlphaArgs(6),
6194 : "Default Humidity (constant for day using Humidity Indicator Temp).");
6195 : // reset HumIndType ?
6196 : } else {
6197 0 : std::vector<Real64> const &dayVals = desDayInput.humIndSched->getDayVals(state);
6198 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6199 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr)
6200 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts)
6201 0 : desDayModsEnvrn(ts + 1, hr + 1).OutRelHum = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6202 :
6203 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6204 0 : state.dataWeather->spSiteSchedNums.end(),
6205 0 : desDayInput.humIndSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6206 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.humIndSched->Num);
6207 0 : SetupOutputVariable(state,
6208 : "Sizing Period Site Humidity Condition Schedule Value",
6209 : unitType,
6210 0 : state.dataWeather->spSiteSchedules(EnvrnNum).OutRelHum,
6211 : OutputProcessor::TimeStepType::Zone,
6212 : OutputProcessor::StoreType::Average,
6213 0 : ipsc->cAlphaArgs(6));
6214 : }
6215 :
6216 0 : switch (desDayInput.HumIndType) {
6217 0 : case DesDayHumIndType::RelHumSch: {
6218 0 : if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 100.0)) {
6219 0 : Sched::ShowSevereBadMinMax(
6220 0 : state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 100.0);
6221 0 : ErrorsFound = true;
6222 : }
6223 0 : } break;
6224 0 : case DesDayHumIndType::WBProfMul: {
6225 : // multiplier: use schedule value, check 0 <= v <= 1
6226 0 : if (!desDayInput.humIndSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
6227 0 : Sched::ShowSevereBadMinMax(
6228 0 : state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 1.0);
6229 0 : ErrorsFound = true;
6230 : }
6231 0 : } break;
6232 0 : case DesDayHumIndType::WBProfDif: {
6233 0 : if (!desDayInput.humIndSched->checkMinVal(state, Clusive::In, 0.0)) {
6234 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(6), Clusive::In, 0.0);
6235 0 : ErrorsFound = true;
6236 : }
6237 0 : } break;
6238 0 : default: {
6239 0 : } break;
6240 : } // switch (desDayInput.HumIndType)
6241 : } // if (desDayInput.HumIndSchPtr == 0)
6242 :
6243 182 : } else if (desDayInput.HumIndType == DesDayHumIndType::WBProfDef) {
6244 : // re WetBulbProfileDefaultMultipliers
6245 7 : Real64 LastHrValue = DefaultTempRangeMult[23];
6246 175 : for (int hour = 1; hour <= Constant::iHoursInDay; ++hour) {
6247 456 : for (int ts = 1; ts <= state.dataGlobal->TimeStepsInHour; ++ts) {
6248 288 : Real64 WNow = state.dataWeather->Interpolation(ts);
6249 288 : Real64 WPrev = 1.0 - WNow;
6250 288 : state.dataWeather->desDayMods(EnvrnNum)(ts, hour).OutRelHum = LastHrValue * WPrev + DefaultTempRangeMult[hour - 1] * WNow;
6251 : }
6252 168 : LastHrValue = DefaultTempRangeMult[hour - 1];
6253 : }
6254 : }
6255 :
6256 : // verify that design WB or DP <= design DB
6257 182 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint || desDayInput.HumIndType == DesDayHumIndType::WetBulb ||
6258 8 : desDayInput.HumIndType == DesDayHumIndType::WBProfMul || desDayInput.HumIndType == DesDayHumIndType::WBProfDef ||
6259 1 : desDayInput.HumIndType == DesDayHumIndType::WBProfDif) {
6260 181 : if (desDayInput.HumIndValue > desDayInput.MaxDryBulb) {
6261 0 : ShowWarningError(state, format("{}=\"{}\", range check data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6262 0 : ShowContinueError(state,
6263 0 : format("..Humidity Indicator Temperature at Max Temperature={:.1R} > Max DryBulb={:.1R}",
6264 0 : desDayInput.HumIndValue,
6265 0 : desDayInput.MaxDryBulb));
6266 0 : ShowContinueError(state, format("..{}=\"{}\".", ipsc->cAlphaFieldNames(5), ipsc->cAlphaArgs(5)));
6267 0 : ShowContinueError(state, "..Conditions for day will be set to Relative Humidity = 100%");
6268 0 : if (desDayInput.HumIndType == DesDayHumIndType::DewPoint) {
6269 0 : desDayInput.DewPointNeedsSet = true;
6270 : } else {
6271 : // wet-bulb
6272 0 : desDayInput.HumIndValue = desDayInput.MaxDryBulb;
6273 : }
6274 : }
6275 : }
6276 :
6277 : // A10, \field Solar Model Indicator
6278 182 : if (ipsc->lAlphaFieldBlanks(10)) {
6279 0 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6280 364 : } else if ((desDayInput.solarModel = static_cast<DesDaySolarModel>(
6281 182 : getEnumValue(DesDaySolarModelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(10))))) != DesDaySolarModel::Invalid) {
6282 : } else {
6283 0 : ShowWarningInvalidKey(state, eoh, ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "ASHRAE ClearSky");
6284 0 : desDayInput.solarModel = DesDaySolarModel::ASHRAE_ClearSky;
6285 : }
6286 :
6287 182 : if (desDayInput.solarModel == DesDaySolarModel::SolarModel_Schedule) {
6288 : // A11, \field Beam Solar Day Schedule Name
6289 0 : if (ipsc->lAlphaFieldBlanks(11)) {
6290 : // should have entered beam schedule
6291 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(11));
6292 0 : ErrorsFound = true;
6293 0 : } else if ((desDayInput.beamSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(11))) == nullptr) {
6294 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11));
6295 0 : ErrorsFound = true;
6296 : } else {
6297 0 : std::vector<Real64> const &dayVals = desDayInput.beamSolarSched->getDayVals(state);
6298 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6299 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr)
6300 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts)
6301 0 : desDayModsEnvrn(ts + 1, hr + 1).BeamSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6302 :
6303 0 : unitType = Constant::Units::W_m2;
6304 : // units = "[W/m2]";
6305 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6306 0 : state.dataWeather->spSiteSchedNums.end(),
6307 0 : desDayInput.beamSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6308 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.beamSolarSched->Num);
6309 0 : SetupOutputVariable(state,
6310 : "Sizing Period Site Beam Solar Schedule Value",
6311 : unitType,
6312 0 : state.dataWeather->spSiteSchedules(EnvrnNum).BeamSolarRad,
6313 : OutputProcessor::TimeStepType::Zone,
6314 : OutputProcessor::StoreType::Average,
6315 0 : ipsc->cAlphaArgs(11));
6316 : }
6317 :
6318 0 : if (!desDayInput.beamSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
6319 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(11), ipsc->cAlphaArgs(11), Clusive::In, 0.0);
6320 0 : ErrorsFound = true;
6321 : }
6322 : }
6323 :
6324 : // A12, \field Diffuse Solar Day Schedule Name
6325 0 : if (ipsc->lAlphaFieldBlanks(12)) {
6326 : // should have entered diffuse schedule
6327 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(12));
6328 0 : ErrorsFound = true;
6329 0 : } else if ((desDayInput.diffuseSolarSched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(12))) == nullptr) {
6330 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12));
6331 0 : ErrorsFound = true;
6332 : } else {
6333 0 : std::vector<Real64> const &dayVals = desDayInput.diffuseSolarSched->getDayVals(state);
6334 0 : auto &desDayModsEnvrn = state.dataWeather->desDayMods(EnvrnNum);
6335 0 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr)
6336 0 : for (int ts = 0; ts < state.dataGlobal->TimeStepsInHour; ++ts)
6337 0 : desDayModsEnvrn(ts + 1, hr + 1).DifSolarRad = dayVals[hr * state.dataGlobal->TimeStepsInHour + ts];
6338 :
6339 : // units = "[W/m2]";
6340 0 : unitType = Constant::Units::W_m2;
6341 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6342 0 : state.dataWeather->spSiteSchedNums.end(),
6343 0 : desDayInput.diffuseSolarSched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6344 0 : state.dataWeather->spSiteSchedNums.emplace_back(desDayInput.diffuseSolarSched->Num);
6345 0 : SetupOutputVariable(state,
6346 : "Sizing Period Site Diffuse Solar Schedule Value",
6347 : unitType,
6348 0 : state.dataWeather->spSiteSchedules(EnvrnNum).DifSolarRad,
6349 : OutputProcessor::TimeStepType::Zone,
6350 : OutputProcessor::StoreType::Average,
6351 0 : ipsc->cAlphaArgs(12));
6352 : }
6353 0 : if (!desDayInput.diffuseSolarSched->checkMinVal(state, Clusive::In, 0.0)) {
6354 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(12), ipsc->cAlphaArgs(12), Clusive::In, 0.0);
6355 0 : ErrorsFound = true;
6356 : }
6357 : }
6358 :
6359 182 : } else if (desDayInput.solarModel == DesDaySolarModel::ASHRAE_ClearSky) {
6360 167 : if (ipsc->lNumericFieldBlanks(14)) {
6361 0 : ShowWarningEmptyField(
6362 0 : state, eoh, ipsc->cNumericFieldNames(14), ipsc->cAlphaFieldNames(10), ipsc->cAlphaArgs(10), "Zero clear sky (no solar)");
6363 : }
6364 : }
6365 :
6366 : // Validate Design Day Month
6367 :
6368 182 : switch (desDayInput.Month) {
6369 179 : case 1:
6370 : case 3:
6371 : case 5:
6372 : case 7:
6373 : case 8:
6374 : case 10:
6375 : case 12: {
6376 179 : if (desDayInput.DayOfMonth > 31) {
6377 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6378 0 : ShowContinueError(
6379 : state,
6380 0 : format(".. invalid field: {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6381 0 : ErrorsFound = true;
6382 : }
6383 179 : } break;
6384 1 : case 4:
6385 : case 6:
6386 : case 9:
6387 : case 11: {
6388 1 : if (desDayInput.DayOfMonth > 30) {
6389 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6390 0 : ShowContinueError(
6391 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6392 0 : ErrorsFound = true;
6393 : }
6394 1 : } break;
6395 2 : case 2: {
6396 2 : if (desDayInput.DayOfMonth > 28) {
6397 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6398 0 : ShowContinueError(
6399 0 : state, format(".. invalid {}=[{}], Month=[{}].", ipsc->cNumericFieldNames(2), desDayInput.DayOfMonth, desDayInput.Month));
6400 0 : ErrorsFound = true;
6401 : }
6402 2 : } break;
6403 0 : default: {
6404 0 : ShowSevereError(state, format("{}=\"{}\", invalid data.", ipsc->cCurrentModuleObject, desDayInput.Title));
6405 0 : ShowContinueError(state, format(".. invalid {} invalid (Month) [{}].", ipsc->cNumericFieldNames(1), desDayInput.Month));
6406 0 : ErrorsFound = true;
6407 0 : } break;
6408 : } // switch (desDayInput.Month)
6409 :
6410 : // A9, \field Daylight Saving Time Indicator
6411 182 : if (ipsc->lAlphaFieldBlanks(9)) {
6412 0 : desDayInput.DSTIndicator = 0;
6413 182 : } else if ((b = getYesNoValue(Util::makeUPPER(ipsc->cAlphaArgs(9)))) != BooleanSwitch::Invalid) {
6414 182 : desDayInput.DSTIndicator = (int)b;
6415 : } else {
6416 0 : ShowWarningInvalidBool(state, eoh, ipsc->cAlphaFieldNames(9), ipsc->cAlphaArgs(9), "No");
6417 0 : desDayInput.DSTIndicator = 0;
6418 : }
6419 :
6420 : // A2, \field Day Type
6421 182 : desDayInput.DayType = getEnumValue(Sched::dayTypeNamesUC, ipsc->cAlphaArgs(2));
6422 182 : if (desDayInput.DayType <= 0) {
6423 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
6424 0 : ErrorsFound = true;
6425 : }
6426 :
6427 182 : auto &designDay = state.dataWeather->DesignDay(EnvrnNum);
6428 182 : envCurr.Title = desDayInput.Title;
6429 182 : envCurr.KindOfEnvrn = Constant::KindOfSim::DesignDay;
6430 182 : envCurr.DesignDayNum = EnvrnNum;
6431 182 : envCurr.RunPeriodDesignNum = 0;
6432 182 : envCurr.TotalDays = 1;
6433 182 : envCurr.StartMonth = desDayInput.Month;
6434 182 : envCurr.StartDay = desDayInput.DayOfMonth;
6435 182 : envCurr.EndMonth = envCurr.StartMonth;
6436 182 : envCurr.EndDay = envCurr.StartDay;
6437 182 : envCurr.DayOfWeek = 0;
6438 182 : envCurr.UseDST = false;
6439 182 : envCurr.UseHolidays = false;
6440 182 : envCurr.StartJDay = designDay.DayOfYear;
6441 182 : envCurr.EndJDay = envCurr.StartJDay;
6442 :
6443 : // create predefined report on design day
6444 182 : std::string envTitle = desDayInput.Title;
6445 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDmaxDB, envTitle, desDayInput.MaxDryBulb);
6446 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDrange, envTitle, desDayInput.DailyDBRange);
6447 182 : if (desDayInput.HumIndType != DesDayHumIndType::RelHumSch) {
6448 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, desDayInput.HumIndValue);
6449 : } else {
6450 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDhumid, envTitle, "N/A");
6451 : }
6452 364 : OutputReportPredefined::PreDefTableEntry(
6453 364 : state, state.dataOutRptPredefined->pdchDDhumTyp, envTitle, DesDayHumIndTypeStringRep[(int)desDayInput.HumIndType]);
6454 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindSp, envTitle, desDayInput.WindSpeed);
6455 182 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchDDwindDr, envTitle, desDayInput.WindDir);
6456 : }
6457 106 : }
6458 :
6459 115 : void GetLocationInfo(EnergyPlusData &state, bool &ErrorsFound)
6460 : {
6461 :
6462 : // SUBROUTINE INFORMATION:
6463 : // AUTHOR Richard Liesen
6464 : // DATE WRITTEN October 1997
6465 :
6466 : // PURPOSE OF THIS SUBROUTINE:
6467 : // This subroutine gets the location info from the IDF file; latitude,
6468 : // longitude and time zone number.
6469 :
6470 115 : auto const &ipsc = state.dataIPShortCut;
6471 115 : ipsc->cCurrentModuleObject = "Site:Location";
6472 115 : int const NumLocations = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6473 :
6474 115 : if (NumLocations > 1) {
6475 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6476 0 : ErrorsFound = true;
6477 : }
6478 :
6479 115 : if (NumLocations == 1) {
6480 : int LocNumAlpha; // Number of alpha names being passed
6481 : int LocNumProp; // Number of properties being passed
6482 : int IOStat; // IO Status when calling get input subroutine
6483 112 : Array1D_string LocAlphas(2); // Temporary array to transfer location info (non-numerics)
6484 112 : Array1D<Real64> LocProps(4); // Temporary array to transfer location info (numerics)
6485 : // Call Input Get routine to retrieve Location information
6486 224 : state.dataInputProcessing->inputProcessor->getObjectItem(
6487 112 : state, ipsc->cCurrentModuleObject, 1, LocAlphas, LocNumAlpha, LocProps, LocNumProp, IOStat);
6488 :
6489 : // set latitude, longitude, and time zone number variables
6490 112 : state.dataWeather->LocationTitle = LocAlphas(1);
6491 112 : state.dataEnvrn->Latitude = LocProps(1);
6492 112 : state.dataEnvrn->Longitude = LocProps(2);
6493 112 : state.dataEnvrn->TimeZoneNumber = LocProps(3);
6494 112 : state.dataEnvrn->Elevation = LocProps(4);
6495 112 : if (Util::SameString(LocAlphas(2), "Yes")) state.dataWeather->keepUserSiteLocationDefinition = true;
6496 112 : state.dataWeather->LocationGathered = true;
6497 112 : }
6498 115 : }
6499 :
6500 111 : void GetWeatherProperties(EnergyPlusData &state, bool &ErrorsFound)
6501 : {
6502 :
6503 : // SUBROUTINE INFORMATION:
6504 : // AUTHOR Linda Lawrie
6505 : // DATE WRITTEN July 2009
6506 :
6507 : // PURPOSE OF THIS SUBROUTINE:
6508 : // Weather properties are an advanced concept for simulation. Primarily, these properties are
6509 : // used in the test suite runs that have specific requirements for certain properties (such as
6510 : // sky temperature).
6511 :
6512 : // REFERENCES:
6513 : // WeatherProperty:SkyTemperature,
6514 : // \memo This object is used to override internal sky temperature calculations.
6515 : // A1, \field Name
6516 : // \reference DesignDays
6517 : // \note leave blank for RunPeriods (until we name them)
6518 : // \note This field references the applicable design day or runperiod(s) if left blank.
6519 : // A2, \field Calculation Type
6520 : // \type choice
6521 : // \key ScheduleValue
6522 : // \key DifferenceScheduleDryBulbValue
6523 : // \key DifferenceScheduleDewPointValue
6524 : // \key AlgorithmA
6525 : // A3; \field Schedule Name
6526 : // \type object-list
6527 : // \object-list DayScheduleNames
6528 : // \object-list ScheduleNames
6529 :
6530 : static constexpr std::string_view routineName = "GetWeatherProperties";
6531 :
6532 : int Found;
6533 : int envFound;
6534 :
6535 111 : auto const &ipsc = state.dataIPShortCut;
6536 111 : ipsc->cCurrentModuleObject = "WeatherProperty:SkyTemperature";
6537 111 : state.dataWeather->NumWPSkyTemperatures = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6538 :
6539 111 : state.dataWeather->WPSkyTemperature.allocate(state.dataWeather->NumWPSkyTemperatures); // by default, not used.
6540 :
6541 111 : for (int i = 1; i <= state.dataWeather->NumWPSkyTemperatures; ++i) {
6542 : int IOStat;
6543 : int NumAlpha;
6544 : int NumNumerics;
6545 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6546 0 : ipsc->cCurrentModuleObject,
6547 : i,
6548 0 : ipsc->cAlphaArgs,
6549 : NumAlpha,
6550 0 : ipsc->rNumericArgs,
6551 : NumNumerics,
6552 : IOStat,
6553 0 : ipsc->lNumericFieldBlanks,
6554 0 : ipsc->lAlphaFieldBlanks,
6555 0 : ipsc->cAlphaFieldNames,
6556 0 : ipsc->cNumericFieldNames);
6557 :
6558 0 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
6559 0 : auto &wpSkyTemp = state.dataWeather->WPSkyTemperature(i);
6560 0 : if (ipsc->cAlphaArgs(1).empty()) {
6561 0 : Found = 0;
6562 0 : for (int j = 1; j <= state.dataWeather->NumOfEnvrn; ++j) {
6563 0 : auto &environJ = state.dataWeather->Environment(j);
6564 0 : if (environJ.KindOfEnvrn != Constant::KindOfSim::RunPeriodWeather) continue;
6565 0 : if (environJ.WP_Type1 != 0) {
6566 0 : ShowSevereError(state,
6567 0 : format("{}: {}=\"{}\", indicated Environment Name already assigned.",
6568 : routineName,
6569 0 : ipsc->cCurrentModuleObject,
6570 0 : ipsc->cAlphaArgs(1)));
6571 0 : if (!environJ.Title.empty()) {
6572 0 : ShowContinueError(state,
6573 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6574 0 : environJ.Title,
6575 0 : ipsc->cCurrentModuleObject,
6576 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6577 : } else {
6578 0 : ShowContinueError(state,
6579 0 : format("... Runperiod Environment, already using {}=\"{}\".",
6580 0 : ipsc->cCurrentModuleObject,
6581 0 : state.dataWeather->WPSkyTemperature(environJ.WP_Type1).Name));
6582 : }
6583 0 : ErrorsFound = true;
6584 : } else {
6585 0 : environJ.WP_Type1 = i;
6586 0 : Found = j;
6587 : }
6588 : }
6589 0 : if (Found == 0) {
6590 0 : ShowWarningError(state, "GetWeatherProperties: WeatherProperty:SkyTemperature=blank, no run periods found.");
6591 0 : ShowContinueError(state, "...SkyTemperature will not be applied.");
6592 0 : continue;
6593 : }
6594 : } else { // really a name
6595 0 : Found = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataWeather->Environment, &EnvironmentData::Title);
6596 0 : envFound = Found;
6597 0 : if (Found == 0) {
6598 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
6599 0 : ErrorsFound = true;
6600 0 : continue;
6601 : }
6602 :
6603 0 : auto &envrnFound = state.dataWeather->Environment(Found);
6604 0 : if (envrnFound.WP_Type1 != 0) {
6605 0 : ShowSevereError(state,
6606 0 : format("{}:{}=\"{}\", indicated Environment Name already assigned.",
6607 : routineName,
6608 0 : ipsc->cCurrentModuleObject,
6609 0 : ipsc->cAlphaArgs(1)));
6610 0 : ShowContinueError(state,
6611 0 : format("...Environment=\"{}\", already using {}=\"{}\".",
6612 0 : envrnFound.Title,
6613 0 : ipsc->cCurrentModuleObject,
6614 0 : state.dataWeather->WPSkyTemperature(envrnFound.WP_Type1).Name));
6615 0 : ErrorsFound = true;
6616 : } else {
6617 0 : state.dataWeather->Environment(Found).WP_Type1 = i;
6618 : }
6619 : }
6620 :
6621 0 : wpSkyTemp.Name = !ipsc->lAlphaFieldBlanks(1) ? ipsc->cAlphaArgs(1) : "All RunPeriods";
6622 :
6623 : // Validate Calculation Type.
6624 : // std::string units;
6625 : Constant::Units unitType;
6626 0 : wpSkyTemp.skyTempModel = static_cast<SkyTempModel>(getEnumValue(Weather::SkyTempModelNamesUC, ipsc->cAlphaArgs(2)));
6627 :
6628 0 : switch (wpSkyTemp.skyTempModel) {
6629 0 : case SkyTempModel::ScheduleValue: {
6630 0 : wpSkyTemp.IsSchedule = true;
6631 : // units = "[C]";
6632 0 : unitType = Constant::Units::C;
6633 0 : } break;
6634 0 : case SkyTempModel::DryBulbDelta:
6635 : case SkyTempModel::DewPointDelta: {
6636 0 : wpSkyTemp.IsSchedule = true;
6637 : // units = "[deltaC]";
6638 0 : unitType = Constant::Units::deltaC;
6639 0 : } break;
6640 0 : case SkyTempModel::Brunt:
6641 : case SkyTempModel::Idso:
6642 : case SkyTempModel::BerdahlMartin:
6643 : case SkyTempModel::ClarkAllen: {
6644 0 : wpSkyTemp.IsSchedule = false;
6645 0 : } break;
6646 0 : default: {
6647 : // Bad inputs are trapped by input processor
6648 0 : assert(false);
6649 : } break;
6650 : } // switch (skyTempModel)
6651 :
6652 0 : if (wpSkyTemp.IsSchedule) {
6653 0 : if (state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodWeather ||
6654 0 : state.dataWeather->Environment(Found).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
6655 : // See if it's a schedule.
6656 0 : if ((wpSkyTemp.sched = Sched::GetSchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
6657 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6658 0 : ErrorsFound = true;
6659 : } else {
6660 0 : wpSkyTemp.IsSchedule = true;
6661 : }
6662 : } else { // See if it's a valid schedule.
6663 : // How can a schedule be either a yearly schedule or a day schedule?
6664 0 : if ((wpSkyTemp.sched = Sched::GetDaySchedule(state, ipsc->cAlphaArgs(3))) == nullptr) {
6665 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), ipsc->cAlphaArgs(3));
6666 0 : ErrorsFound = true;
6667 : } else {
6668 0 : if (envFound != 0) {
6669 0 : if (std::find(state.dataWeather->spSiteSchedNums.begin(),
6670 0 : state.dataWeather->spSiteSchedNums.end(),
6671 0 : wpSkyTemp.sched->Num) == state.dataWeather->spSiteSchedNums.end()) {
6672 0 : state.dataWeather->spSiteSchedNums.emplace_back(wpSkyTemp.sched->Num);
6673 0 : SetupOutputVariable(state,
6674 : "Sizing Period Site Sky Temperature Schedule Value",
6675 : unitType,
6676 0 : state.dataWeather->spSiteSchedules(envFound).SkyTemp,
6677 : OutputProcessor::TimeStepType::Zone,
6678 : OutputProcessor::StoreType::Average,
6679 0 : ipsc->cAlphaArgs(3));
6680 : }
6681 0 : wpSkyTemp.IsSchedule = true;
6682 : }
6683 : }
6684 : }
6685 : }
6686 :
6687 0 : if (!wpSkyTemp.IsSchedule && !ipsc->lAlphaFieldBlanks(4)) {
6688 0 : if (BooleanSwitch b = getYesNoValue(ipsc->cAlphaArgs(4)); b != BooleanSwitch::Invalid) {
6689 0 : wpSkyTemp.UseWeatherFileHorizontalIR = static_cast<bool>(b);
6690 : } else {
6691 0 : ShowSevereInvalidBool(state, eoh, ipsc->cAlphaFieldNames(4), ipsc->cAlphaArgs(4));
6692 0 : ErrorsFound = true;
6693 : }
6694 : } else {
6695 0 : wpSkyTemp.UseWeatherFileHorizontalIR = true;
6696 : }
6697 : }
6698 341 : for (auto &envCurr : state.dataWeather->Environment) {
6699 230 : if (envCurr.WP_Type1 != 0 && state.dataWeather->NumWPSkyTemperatures >= envCurr.WP_Type1) {
6700 0 : envCurr.skyTempModel = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).skyTempModel;
6701 0 : envCurr.UseWeatherFileHorizontalIR = state.dataWeather->WPSkyTemperature(envCurr.WP_Type1).UseWeatherFileHorizontalIR;
6702 : }
6703 : }
6704 111 : }
6705 :
6706 111 : void GetGroundTemps(EnergyPlusData &state)
6707 : {
6708 :
6709 : // SUBROUTINE INFORMATION:
6710 : // AUTHOR Richard Liesen
6711 : // DATE WRITTEN October 1997
6712 :
6713 : // PURPOSE OF THIS SUBROUTINE:
6714 : // This file reads the Ground Temps from the input file and puts them
6715 : // in a new variable.
6716 :
6717 : // Initialize Site:GroundTemperature:BuildingSurface object
6718 222 : state.dataWeather->siteBuildingSurfaceGroundTempsPtr =
6719 222 : GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteBuildingSurface, "");
6720 :
6721 : // Initialize Site:GroundTemperature:FCFactorMethod object
6722 222 : state.dataWeather->siteFCFactorMethodGroundTempsPtr =
6723 222 : GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteFCFactorMethod, "");
6724 :
6725 : // Initialize Site:GroundTemperature:Shallow object
6726 222 : state.dataWeather->siteShallowGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteShallow, "");
6727 :
6728 : // Initialize Site:GroundTemperature:Deep object
6729 111 : state.dataWeather->siteDeepGroundTempsPtr = GroundTemp::GetGroundTempModelAndInit(state, GroundTemp::ModelType::SiteDeep, "");
6730 111 : }
6731 :
6732 111 : void GetGroundReflectances(EnergyPlusData &state, bool &ErrorsFound)
6733 : {
6734 :
6735 : // SUBROUTINE INFORMATION:
6736 : // AUTHOR Linda Lawrie
6737 : // DATE WRITTEN March 2002
6738 :
6739 : // PURPOSE OF THIS SUBROUTINE:
6740 : // This file reads the Ground Reflectances from the input file (optional) and
6741 : // places them in the monthly array.
6742 :
6743 111 : auto const &ipsc = state.dataIPShortCut;
6744 111 : ipsc->cCurrentModuleObject = "Site:GroundReflectance";
6745 111 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6746 111 : if (nObjs != 0) {
6747 0 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6748 0 : Array1D<Real64> GndProps(12); // Temporary array to transfer ground reflectances
6749 0 : if (nObjs == 1) {
6750 : int GndNumAlpha; // Number of construction alpha names being passed
6751 : int GndNumProp; // dummy variable for properties being passed
6752 : int IOStat; // IO Status when calling get input subroutine
6753 : // Get the object names for each construction from the input processor
6754 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
6755 0 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6756 :
6757 0 : if (GndNumProp < 12) {
6758 0 : ShowSevereError(state, format("{}: Less than 12 values entered.", ipsc->cCurrentModuleObject));
6759 0 : ErrorsFound = true;
6760 : }
6761 :
6762 : // Assign the ground reflectances to the variable
6763 0 : state.dataWeather->GroundReflectances({1, 12}) = GndProps({1, 12});
6764 :
6765 : } else {
6766 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6767 0 : ErrorsFound = true;
6768 : }
6769 0 : }
6770 :
6771 : // Write Final Ground Reflectance Information to the initialization output file
6772 111 : print(state.files.eio,
6773 : "{}\n",
6774 : "! "
6775 : "<Site:GroundReflectance>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{dimensionless},"
6776 : "May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{dimensionless},Oct{"
6777 : "dimensionless},Nov{dimensionless},Dec{dimensionless}");
6778 :
6779 111 : print(state.files.eio, " Site:GroundReflectance");
6780 1443 : for (int i = 1; i <= 12; ++i) {
6781 1332 : print(state.files.eio, ", {:5.2F}", state.dataWeather->GroundReflectances(i));
6782 : }
6783 111 : print(state.files.eio, "\n");
6784 111 : }
6785 :
6786 111 : void GetSnowGroundRefModifiers(EnergyPlusData &state, bool &ErrorsFound)
6787 : {
6788 :
6789 : // SUBROUTINE INFORMATION:
6790 : // AUTHOR Linda Lawrie
6791 : // DATE WRITTEN March 2002
6792 :
6793 : // PURPOSE OF THIS SUBROUTINE:
6794 : // This file reads the Snow Ground Reflectance Modifiers from the input file (optional) and
6795 : // places them in the variables.
6796 :
6797 111 : auto const &ipsc = state.dataIPShortCut;
6798 111 : ipsc->cCurrentModuleObject = "Site:GroundReflectance:SnowModifier";
6799 111 : int nObjs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6800 111 : if (nObjs != 0) {
6801 0 : Array1D_string GndAlphas(1); // Construction Alpha names defined
6802 0 : Array1D<Real64> GndProps(2); // Temporary array to transfer ground reflectances
6803 0 : if (nObjs == 1) {
6804 : int GndNumAlpha; // Number of construction alpha names being passed
6805 : int GndNumProp; // dummy variable for properties being passed
6806 : int IOStat; // IO Status when calling get input subroutine
6807 : // Get the object names for each construction from the input processor
6808 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
6809 0 : state, ipsc->cCurrentModuleObject, 1, GndAlphas, GndNumAlpha, GndProps, GndNumProp, IOStat);
6810 :
6811 : // Assign the ground reflectances to the variable
6812 0 : state.dataWeather->SnowGndRefModifier = GndProps(1);
6813 0 : state.dataWeather->SnowGndRefModifierForDayltg = GndProps(2);
6814 :
6815 : } else {
6816 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6817 0 : ErrorsFound = true;
6818 : }
6819 0 : }
6820 :
6821 : // Write Final Ground Reflectance Modifier Information to the initialization output file
6822 111 : print(state.files.eio, "{}\n", "! <Site:GroundReflectance:SnowModifier>, Normal, Daylighting {dimensionless}");
6823 : static constexpr std::string_view Format_720(" Site:GroundReflectance:SnowModifier, {:7.3F}, {:7.3F}\n");
6824 111 : print(state.files.eio, Format_720, state.dataWeather->SnowGndRefModifier, state.dataWeather->SnowGndRefModifierForDayltg);
6825 :
6826 111 : print(state.files.eio,
6827 : "{}\n",
6828 : "! "
6829 : "<Site:GroundReflectance:Snow>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
6830 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
6831 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
6832 111 : print(state.files.eio, "{}", " Site:GroundReflectance:Snow");
6833 1443 : for (int i = 1; i <= 12; ++i) {
6834 1332 : print(state.files.eio, ", {:5.2F}", max(min(state.dataWeather->GroundReflectances(i) * state.dataWeather->SnowGndRefModifier, 1.0), 0.0));
6835 : }
6836 111 : print(state.files.eio, "\n");
6837 111 : print(state.files.eio,
6838 : "{}\n",
6839 : "! "
6840 : "<Site:GroundReflectance:Snow:Daylighting>,Jan{dimensionless},Feb{dimensionless},Mar{dimensionless},Apr{"
6841 : "dimensionless},May{dimensionless},Jun{dimensionless},Jul{dimensionless},Aug{dimensionless},Sep{"
6842 : "dimensionless},Oct{dimensionless},Nov{dimensionless},Dec{dimensionless}");
6843 111 : print(state.files.eio, " Site:GroundReflectance:Snow:Daylighting");
6844 1443 : for (nObjs = 1; nObjs <= 12; ++nObjs) {
6845 1332 : print(state.files.eio,
6846 : ", {:5.2F}",
6847 2664 : max(min(state.dataWeather->GroundReflectances(nObjs) * state.dataWeather->SnowGndRefModifierForDayltg, 1.0), 0.0));
6848 : }
6849 111 : print(state.files.eio, "\n");
6850 111 : }
6851 :
6852 117 : void GetWaterMainsTemperatures(EnergyPlusData &state, bool &ErrorsFound)
6853 : {
6854 :
6855 : // SUBROUTINE INFORMATION:
6856 : // AUTHOR Peter Graham Ellis
6857 : // DATE WRITTEN January 2005
6858 :
6859 : // PURPOSE OF THIS SUBROUTINE:
6860 : // Reads the input data for the WATER MAINS TEMPERATURES object.
6861 :
6862 117 : constexpr std::string_view routineName = "GetWaterMainsTemperatures";
6863 :
6864 117 : auto const &ipsc = state.dataIPShortCut;
6865 117 : ipsc->cCurrentModuleObject = "Site:WaterMainsTemperature";
6866 117 : int NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
6867 :
6868 117 : if (NumObjects == 1) {
6869 : int NumAlphas; // Number of elements in the alpha array
6870 : int NumNums; // Number of elements in the numeric array
6871 : int IOStat; // IO Status when calling get input subroutine
6872 12 : Array1D_string AlphArray(2); // Character string data
6873 12 : Array1D<Real64> NumArray(2); // Numeric data
6874 36 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
6875 12 : ipsc->cCurrentModuleObject,
6876 : 1,
6877 : AlphArray,
6878 : NumAlphas,
6879 : NumArray,
6880 : NumNums,
6881 : IOStat,
6882 12 : ipsc->lNumericFieldBlanks,
6883 12 : ipsc->lAlphaFieldBlanks,
6884 12 : ipsc->cAlphaFieldNames,
6885 12 : ipsc->cNumericFieldNames);
6886 :
6887 12 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ""};
6888 :
6889 24 : state.dataWeather->WaterMainsTempsMethod =
6890 12 : static_cast<Weather::WaterMainsTempCalcMethod>(getEnumValue(waterMainsCalcMethodNamesUC, AlphArray(1)));
6891 :
6892 12 : switch (state.dataWeather->WaterMainsTempsMethod) {
6893 0 : case WaterMainsTempCalcMethod::Schedule: {
6894 0 : if (ipsc->lAlphaFieldBlanks(2)) {
6895 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(2));
6896 0 : ErrorsFound = true;
6897 0 : } else if ((state.dataWeather->waterMainsTempSched = Sched::GetSchedule(state, AlphArray(2))) == nullptr) {
6898 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), AlphArray(2));
6899 0 : ErrorsFound = true;
6900 : }
6901 0 : } break;
6902 :
6903 6 : case WaterMainsTempCalcMethod::Correlation: {
6904 6 : if (NumNums == 0) {
6905 0 : ShowSevereError(state, format("{}: Missing Annual Average and Maximum Difference fields.", ipsc->cCurrentModuleObject));
6906 0 : ErrorsFound = true;
6907 6 : } else if (NumNums == 1) {
6908 0 : ShowSevereError(state, format("{}: Missing Maximum Difference field.", ipsc->cCurrentModuleObject));
6909 0 : ErrorsFound = true;
6910 : } else {
6911 6 : state.dataWeather->WaterMainsTempsAnnualAvgAirTemp = NumArray(1);
6912 6 : state.dataWeather->WaterMainsTempsMaxDiffAirTemp = NumArray(2);
6913 : }
6914 6 : } break;
6915 6 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile: {
6916 : // No action
6917 6 : } break;
6918 0 : default: {
6919 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), AlphArray(1));
6920 0 : ErrorsFound = true;
6921 0 : } break;
6922 : } // switch
6923 :
6924 117 : } else if (NumObjects > 1) {
6925 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
6926 0 : ErrorsFound = true;
6927 : }
6928 117 : }
6929 :
6930 326128 : void CalcWaterMainsTemp(EnergyPlusData &state)
6931 : {
6932 :
6933 : // SUBROUTINE INFORMATION:
6934 : // AUTHOR Peter Graham Ellis
6935 : // DATE WRITTEN January 2005
6936 : // MODIFIED June 2018, B. Nigusse
6937 :
6938 : // PURPOSE OF THIS SUBROUTINE:
6939 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
6940 :
6941 : // METHODOLOGY EMPLOYED:
6942 : // Water mains temperature is either taken from a schedule or calculated by a correlation. The correlation
6943 : // is fit to Fahrenheit units, so the air temperature values are first convert to F, then mains temperature
6944 : // is calculated and converted back to C.
6945 :
6946 326128 : switch (state.dataWeather->WaterMainsTempsMethod) {
6947 0 : case WaterMainsTempCalcMethod::Schedule:
6948 0 : state.dataEnvrn->WaterMainsTemp = state.dataWeather->waterMainsTempSched->getCurrentVal();
6949 0 : break;
6950 19946 : case WaterMainsTempCalcMethod::Correlation:
6951 19946 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(
6952 19946 : state, state.dataWeather->WaterMainsTempsAnnualAvgAirTemp, state.dataWeather->WaterMainsTempsMaxDiffAirTemp);
6953 19946 : break;
6954 6 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
6955 6 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
6956 6 : state.dataEnvrn->WaterMainsTemp = WaterMainsTempFromCorrelation(state,
6957 6 : state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp,
6958 6 : state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff);
6959 : } else {
6960 0 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
6961 : }
6962 6 : break;
6963 306176 : default:
6964 306176 : state.dataEnvrn->WaterMainsTemp = 10.0; // 50 F
6965 306176 : break;
6966 : }
6967 326128 : }
6968 :
6969 : Real64
6970 19952 : WaterMainsTempFromCorrelation(EnergyPlusData const &state, Real64 const AnnualOAAvgDryBulbTemp, Real64 const MonthlyOAAvgDryBulbTempMaxDiff)
6971 : {
6972 :
6973 : // SUBROUTINE INFORMATION:
6974 : // AUTHOR Peter Graham Ellis
6975 : // DATE WRITTEN January 2005
6976 : // MODIFIED B Nigusse June 2018 (Refactored)
6977 :
6978 : // PURPOSE OF THIS SUBROUTINE:
6979 : // Calculates the daily water mains temperature based on input data from the WATER MAINS TEMPERATURES object.
6980 :
6981 : // METHODOLOGY EMPLOYED:
6982 : // Water mains temperature calculated by a correlation. The correlation is fit to Fahrenheit units, so the
6983 : // air temperature values are first convert to F, then mains temperature is calculated and converted back to C.
6984 : // used for Calculated Method: 'Correlation' and 'CorrelationFromWeatherFile'.
6985 :
6986 : // REFERENCES:
6987 : // Correlation developed by Jay Burch and Craig Christensen at NREL, described in:
6988 : // Hendron, R., Anderson, R., Christensen, C., Eastment, M., and Reeves, P. 2004. "Development of an Energy
6989 : // Savings Benchmark for All Residential End-Uses", Proceedings of SimBuild 2004, IBPSA-USA National Conference,
6990 : // Boulder, CO, August 4 - 6, 2004.
6991 :
6992 : // Annual Average Outdoor Air Temperature (F)
6993 19952 : Real64 const Tavg = AnnualOAAvgDryBulbTemp * (9.0 / 5.0) + 32.0;
6994 : // Maximum difference in monthly average outdoor air temperatures (deltaF)
6995 19952 : Real64 const Tdiff = MonthlyOAAvgDryBulbTempMaxDiff * (9.0 / 5.0);
6996 :
6997 19952 : Real64 const Ratio = 0.4 + 0.01 * (Tavg - 44.0);
6998 19952 : Real64 const Lag = 35.0 - 1.0 * (Tavg - 44.0);
6999 19952 : Real64 constexpr Offset = 6.0;
7000 19952 : int const latitude_sign = (state.dataEnvrn->Latitude >= 0) ? 1 : -1;
7001 :
7002 : // calculated water main temp (F)
7003 : Real64 CurrentWaterMainsTemp =
7004 19952 : Tavg + Offset +
7005 19952 : Ratio * (Tdiff / 2.0) * latitude_sign * std::sin((0.986 * (state.dataEnvrn->DayOfYear - 15.0 - Lag) - 90) * Constant::DegToRad);
7006 :
7007 19952 : if (CurrentWaterMainsTemp < 32.0) CurrentWaterMainsTemp = 32.0;
7008 :
7009 : // Convert F to C
7010 19952 : return (CurrentWaterMainsTemp - 32.0) * (5.0 / 9.0);
7011 : }
7012 111 : void GetWeatherStation(EnergyPlusData &state, bool &ErrorsFound)
7013 : {
7014 :
7015 : // SUBROUTINE INFORMATION:
7016 : // AUTHOR Peter Graham Ellis
7017 : // DATE WRITTEN January 2006
7018 :
7019 : // PURPOSE OF THIS SUBROUTINE:
7020 : // Reads the input data for the WEATHER STATION object.
7021 :
7022 111 : auto const &ipsc = state.dataIPShortCut;
7023 111 : ipsc->cCurrentModuleObject = "Site:WeatherStation";
7024 111 : int const NumObjects = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
7025 :
7026 : // Default conditions for a weather station in an open field at a height of 10 m. (These should match the IDD defaults.)
7027 111 : Real64 WeatherFileWindSensorHeight = 10.0; // Height of the wind sensor at the weather station, i.e., weather file
7028 111 : Real64 WeatherFileWindExp = 0.14; // Exponent for the wind velocity profile at the weather station
7029 111 : Real64 WeatherFileWindBLHeight = 270.0; // Boundary layer height for the wind velocity profile at the weather station (m)
7030 111 : Real64 WeatherFileTempSensorHeight = 1.5; // Height of the air temperature sensor at the weather station (m)
7031 :
7032 111 : if (NumObjects == 1) {
7033 : int NumAlphas; // Number of elements in the alpha array
7034 : int NumNums; // Number of elements in the numeric array
7035 : int IOStat; // IO Status when calling get input subroutine
7036 0 : Array1D_string AlphArray(1); // Character string data
7037 0 : Array1D<Real64> NumArray(4); // Numeric data
7038 0 : state.dataInputProcessing->inputProcessor->getObjectItem(
7039 0 : state, ipsc->cCurrentModuleObject, 1, AlphArray, NumAlphas, NumArray, NumNums, IOStat);
7040 :
7041 0 : if (NumNums > 0) WeatherFileWindSensorHeight = NumArray(1);
7042 0 : if (NumNums > 1) WeatherFileWindExp = NumArray(2);
7043 0 : if (NumNums > 2) WeatherFileWindBLHeight = NumArray(3);
7044 0 : if (NumNums > 3) WeatherFileTempSensorHeight = NumArray(4);
7045 :
7046 111 : } else if (NumObjects > 1) {
7047 0 : ShowSevereError(state, format("{}: Too many objects entered. Only one allowed.", ipsc->cCurrentModuleObject));
7048 0 : ErrorsFound = true;
7049 : }
7050 :
7051 111 : state.dataEnvrn->WeatherFileWindModCoeff = std::pow(WeatherFileWindBLHeight / WeatherFileWindSensorHeight, WeatherFileWindExp);
7052 222 : state.dataEnvrn->WeatherFileTempModCoeff = DataEnvironment::AtmosphericTempGradient * DataEnvironment::EarthRadius *
7053 111 : WeatherFileTempSensorHeight / (DataEnvironment::EarthRadius + WeatherFileTempSensorHeight);
7054 :
7055 : // Write to the initialization output file
7056 111 : print(state.files.eio,
7057 : "{}\n",
7058 : "! <Environment:Weather Station>,Wind Sensor Height Above Ground {m},Wind Speed Profile Exponent "
7059 : "{},Wind Speed Profile Boundary Layer Thickness {m},Air Temperature Sensor Height Above Ground {m},Wind "
7060 : "Speed Modifier Coefficient-Internal,Temperature Modifier Coefficient-Internal");
7061 :
7062 : // Formats
7063 : static constexpr std::string_view Format_720("Environment:Weather Station,{:.3R},{:.3R},{:.3R},{:.3R},{:.3R},{:.3R}\n");
7064 111 : print(state.files.eio,
7065 : Format_720,
7066 : WeatherFileWindSensorHeight,
7067 : WeatherFileWindExp,
7068 : WeatherFileWindBLHeight,
7069 : WeatherFileTempSensorHeight,
7070 111 : state.dataEnvrn->WeatherFileWindModCoeff,
7071 111 : state.dataEnvrn->WeatherFileTempModCoeff);
7072 111 : }
7073 :
7074 326120 : void DayltgCurrentExtHorizIllum(EnergyPlusData &state)
7075 : {
7076 :
7077 : // SUBROUTINE INFORMATION:
7078 : // AUTHOR Fred Winkelmann
7079 : // DATE WRITTEN July 1997
7080 : // MODIFIED Nov98 (FW); Nov 2000 (FW)
7081 :
7082 : // PURPOSE OF THIS SUBROUTINE:
7083 : // CALCULATES EXTERIOR DAYLIGHT ILLUMINANCE AND LUMINOUS EFFICACY
7084 :
7085 : // METHODOLOGY EMPLOYED:
7086 : // CALLED by SetCurrentWeather.
7087 : // CALCULATES THE CURRENT-TIME-STEP
7088 : // ILLUMINANCE ON AN UNOBSTRUCTED HORIZONTAL SURFACE FROM THE
7089 : // THE SKY AND FROM DIRECT SUN.
7090 :
7091 : // REFERENCES:
7092 : // Based on DOE-2.1E subroutine DEXTIL.
7093 :
7094 : // SOLCOS(3), below, is the cosine of the solar zenith angle.
7095 326120 : if (state.dataEnvrn->SunIsUp) {
7096 : // Exterior horizontal beam irradiance (W/m2)
7097 159691 : Real64 SDIRH = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->SOLCOS.z;
7098 : // Exterior horizontal sky diffuse irradiance (W/m2)
7099 159691 : Real64 SDIFH = state.dataEnvrn->DifSolarRad;
7100 : // Fraction of sky covered by clouds
7101 159691 : state.dataEnvrn->CloudFraction = pow_2(SDIFH / (SDIRH + SDIFH + 0.0001));
7102 : // Luminous efficacy of sky diffuse solar and beam solar (lumens/W);
7103 : // Horizontal illuminance from sky and horizontal beam illuminance (lux)
7104 : // obtained from solar quantities on weather file and luminous efficacy.
7105 :
7106 159691 : DayltgLuminousEfficacy(state, state.dataEnvrn->PDIFLW, state.dataEnvrn->PDIRLW);
7107 159691 : state.dataEnvrn->HISKF = SDIFH * state.dataEnvrn->PDIFLW;
7108 159691 : state.dataEnvrn->HISUNF = SDIRH * state.dataEnvrn->PDIRLW;
7109 159691 : state.dataEnvrn->HISUNFnorm = state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW;
7110 : } else {
7111 166429 : state.dataEnvrn->CloudFraction = 0.0;
7112 166429 : state.dataEnvrn->PDIFLW = 0.0;
7113 166429 : state.dataEnvrn->PDIRLW = 0.0;
7114 166429 : state.dataEnvrn->HISKF = 0.0;
7115 166429 : state.dataEnvrn->HISUNF = 0.0;
7116 166429 : state.dataEnvrn->HISUNFnorm = 0.0;
7117 166429 : state.dataEnvrn->SkyClearness = 0.0;
7118 166429 : state.dataEnvrn->SkyBrightness = 0.0;
7119 : }
7120 326120 : }
7121 :
7122 159691 : void DayltgLuminousEfficacy(EnergyPlusData &state,
7123 : Real64 &DiffLumEff, // Luminous efficacy of sky diffuse solar radiation (lum/W)
7124 : Real64 &DirLumEff // Luminous efficacy of beam solar radiation (lum/W)
7125 : )
7126 : {
7127 : // SUBROUTINE INFORMATION:
7128 : // AUTHOR Fred Winkelmann
7129 : // DATE WRITTEN July 1997
7130 : // MODIFIED August 2009, BG fixed upper bound for sky clearness bin 7
7131 :
7132 : // PURPOSE OF THIS SUBROUTINE:
7133 : // Uses diffuse horizontal solar irradiance, direct normal solar
7134 : // irradiance, atmospheric moisture and sun position
7135 : // to determine the luminous efficacy in lumens/watt
7136 : // of sky diffuse solar radiation and direct normal solar radiation.
7137 : // Based on an empirical method described in
7138 : // R. Perez, P. Ineichen, R. Seals, J. Michalsky and R. Stewart,
7139 : // "Modeling daylight availability and irradiance components from direct
7140 : // global irradiance components from direct and global irradiance,"
7141 : // Solar Energy 44 (1990) 271-289.
7142 :
7143 : // Diffuse luminous efficacy coefficients
7144 : static constexpr std::array<Real64, 8> ADiffLumEff = {97.24, 107.22, 104.97, 102.39, 100.71, 106.42, 141.88, 152.23};
7145 : static constexpr std::array<Real64, 8> BDiffLumEff = {-0.46, 1.15, 2.96, 5.59, 5.94, 3.83, 1.90, 0.35};
7146 : static constexpr std::array<Real64, 8> CDiffLumEff = {12.00, 0.59, -5.53, -13.95, -22.75, -36.15, -53.24, -45.27};
7147 : static constexpr std::array<Real64, 8> DDiffLumEff = {-8.91, -3.95, -8.77, -13.90, -23.74, -28.83, -14.03, -7.98};
7148 : // Direct luminous efficacy coefficients
7149 : static constexpr std::array<Real64, 8> ADirLumEff = {57.20, 98.99, 109.83, 110.34, 106.36, 107.19, 105.75, 101.18};
7150 : static constexpr std::array<Real64, 8> BDirLumEff = {-4.55, -3.46, -4.90, -5.84, -3.97, -1.25, 0.77, 1.58};
7151 : static constexpr std::array<Real64, 8> CDirLumEff = {-2.98, -1.21, -1.71, -1.99, -1.75, -1.51, -1.26, -1.10};
7152 : static constexpr std::array<Real64, 8> DDirLumEff = {117.12, 12.38, -8.81, -4.56, -6.16, -26.73, -34.44, -8.29};
7153 : // Monthly exterrestrial direct normal illuminance (lum/m2)
7154 : static constexpr std::array<Real64, 12> ExtraDirNormIll = {
7155 : 131153.0, 130613.0, 128992.0, 126816.0, 124731.0, 123240.0, 122652.0, 123120.0, 124576.0, 126658.0, 128814.0, 130471.0};
7156 :
7157 159691 : Real64 const SunZenith = std::acos(state.dataEnvrn->SOLCOS.z); // Solar zenith angle (radians)
7158 159691 : Real64 const SunAltitude = Constant::PiOvr2 - SunZenith; // Solar altitude angle (radians)
7159 159691 : Real64 const SinSunAltitude = std::sin(SunAltitude);
7160 : // Clearness of sky. SkyClearness close to 1.0 corresponds to an overcast sky.
7161 : // SkyClearness > 6 is a clear sky.
7162 : // DifSolarRad is the diffuse horizontal irradiance.
7163 : // BeamSolarRad is the direct normal irradiance.
7164 159691 : Real64 const Zeta = 1.041 * pow_3(SunZenith);
7165 319382 : state.dataEnvrn->SkyClearness =
7166 159691 : ((state.dataEnvrn->DifSolarRad + state.dataEnvrn->BeamSolarRad) / (state.dataEnvrn->DifSolarRad + 0.0001) + Zeta) / (1.0 + Zeta);
7167 : // Relative optical air mass
7168 : Real64 const relAirMass =
7169 159691 : (1.0 - 0.1 * state.dataEnvrn->Elevation / 1000.0) / (SinSunAltitude + 0.15 / std::pow(SunAltitude / Constant::DegToRad + 3.885, 1.253));
7170 : // In the following, 93.73 is the extraterrestrial luminous efficacy
7171 159691 : state.dataEnvrn->SkyBrightness = (state.dataEnvrn->DifSolarRad * 93.73) * relAirMass / ExtraDirNormIll[state.dataEnvrn->Month - 1];
7172 : int ISkyClearness; // Sky clearness bin
7173 159691 : if (state.dataEnvrn->SkyClearness <= 1.065) {
7174 62364 : ISkyClearness = 0;
7175 97327 : } else if (state.dataEnvrn->SkyClearness <= 1.23) {
7176 2739 : ISkyClearness = 1;
7177 94588 : } else if (state.dataEnvrn->SkyClearness <= 1.50) {
7178 3360 : ISkyClearness = 2;
7179 91228 : } else if (state.dataEnvrn->SkyClearness <= 1.95) {
7180 4314 : ISkyClearness = 3;
7181 86914 : } else if (state.dataEnvrn->SkyClearness <= 2.80) {
7182 12286 : ISkyClearness = 4;
7183 74628 : } else if (state.dataEnvrn->SkyClearness <= 4.50) {
7184 28260 : ISkyClearness = 5;
7185 46368 : } else if (state.dataEnvrn->SkyClearness <= 6.20) {
7186 17161 : ISkyClearness = 6;
7187 : } else {
7188 29207 : ISkyClearness = 7;
7189 : }
7190 :
7191 : // Atmospheric moisture (cm of precipitable water)
7192 159691 : Real64 const AtmosMoisture = std::exp(0.07 * state.dataEnvrn->OutDewPointTemp - 0.075);
7193 : // Sky diffuse luminous efficacy
7194 159691 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7195 51559 : DiffLumEff = 0.0;
7196 : } else {
7197 108132 : DiffLumEff = ADiffLumEff[ISkyClearness] + BDiffLumEff[ISkyClearness] * AtmosMoisture +
7198 108132 : CDiffLumEff[ISkyClearness] * state.dataEnvrn->SOLCOS.z +
7199 108132 : DDiffLumEff[ISkyClearness] * std::log(state.dataEnvrn->SkyBrightness);
7200 : }
7201 : // Direct normal luminous efficacy
7202 159691 : if (state.dataEnvrn->SkyBrightness <= 0.0) {
7203 51559 : DirLumEff = 0.0;
7204 : } else {
7205 108132 : DirLumEff =
7206 108132 : max(0.0,
7207 108132 : ADirLumEff[ISkyClearness] + BDirLumEff[ISkyClearness] * AtmosMoisture +
7208 108132 : CDirLumEff[ISkyClearness] * std::exp(5.73 * SunZenith - 5.0) + DDirLumEff[ISkyClearness] * state.dataEnvrn->SkyBrightness);
7209 : }
7210 159691 : }
7211 :
7212 113 : Real64 GetSTM(Real64 const Longitude) // Longitude from user input
7213 : {
7214 : // FUNCTION INFORMATION:
7215 : // AUTHOR Linda K. Lawrie
7216 : // DATE WRITTEN August 2003
7217 :
7218 : // PURPOSE OF THIS FUNCTION:
7219 : // This function determines the "standard time meridian" from the input
7220 : // longitude. Calculates the proper Meridian from Longitude. This
7221 : // value is needed for weather calculations so that the sun comes
7222 : // up and goes down at the right times.
7223 :
7224 : Real64 GetSTM;
7225 :
7226 113 : Array1D<Real64> longl({-12, 12}); // Lower Longitude value for a Time Zone
7227 113 : Array1D<Real64> longh({-12, 12}); // Upper Longitude value for a Time Zone
7228 :
7229 113 : GetSTM = 0.0;
7230 :
7231 113 : longl(0) = -7.5;
7232 113 : longh(0) = 7.5;
7233 1469 : for (int i = 1; i <= 12; ++i) {
7234 1356 : longl(i) = longl(i - 1) + 15.0;
7235 1356 : longh(i) = longh(i - 1) + 15.0;
7236 : }
7237 1469 : for (int i = 1; i <= 12; ++i) {
7238 1356 : longl(-i) = longl(-i + 1) - 15.0;
7239 1356 : longh(-i) = longh(-i + 1) - 15.0;
7240 : }
7241 113 : Real64 temp = mod(Longitude, 360.0);
7242 113 : if (temp > 180.0) temp -= 180.0;
7243 : Real64 tz; // resultant tz meridian
7244 808 : for (int i = -12; i <= 12; ++i) {
7245 808 : if (temp > longl(i) && temp <= longh(i)) {
7246 113 : tz = mod(i, 24.0);
7247 113 : GetSTM = tz;
7248 113 : break;
7249 : }
7250 : }
7251 :
7252 113 : return GetSTM;
7253 113 : }
7254 :
7255 241 : void ProcessEPWHeader(EnergyPlusData &state, EpwHeaderType const headerType, std::string &Line, bool &ErrorsFound)
7256 : {
7257 :
7258 : // SUBROUTINE INFORMATION:
7259 : // AUTHOR Linda Lawrie
7260 : // DATE WRITTEN December 1999
7261 :
7262 : // PURPOSE OF THIS SUBROUTINE:
7263 : // This subroutine processes each header line in the EPW weather file.
7264 :
7265 : // METHODOLOGY EMPLOYED:
7266 : // File is positioned to the correct line, then backspaced. This routine
7267 : // reads in the line and processes as appropriate.
7268 :
7269 : Weather::DateType dateType;
7270 : int NumHdArgs;
7271 :
7272 : // Strip off Header value from Line
7273 241 : std::string::size_type Pos = index(Line, ',');
7274 241 : if ((Pos == std::string::npos) && !((headerType == EpwHeaderType::Comments1) || (headerType == EpwHeaderType::Comments2))) {
7275 0 : ShowSevereError(state, "Invalid Header line in in.epw -- no commas");
7276 0 : ShowContinueError(state, format("Line={}", Line));
7277 0 : ShowFatalError(state, "Previous conditions cause termination.");
7278 : }
7279 241 : if (Pos != std::string::npos) Line.erase(0, Pos + 1);
7280 :
7281 241 : switch (headerType) {
7282 30 : case Weather::EpwHeaderType::Location: {
7283 :
7284 : // LOCATION, A1 [City], A2 [State/Province/Region], A3 [Country],
7285 : // A4 [Source], N1 [WMO], N2 [Latitude],
7286 : // N3 [Longitude], N4 [Time Zone], N5 [Elevation {m}]
7287 :
7288 30 : NumHdArgs = 9;
7289 300 : for (int i = 1; i <= NumHdArgs; ++i) {
7290 270 : strip(Line);
7291 270 : Pos = index(Line, ',');
7292 270 : if (Pos == std::string::npos) {
7293 29 : if (len(Line) == 0) {
7294 0 : while (Pos == std::string::npos) {
7295 0 : Line = state.files.inputWeatherFile.readLine().data;
7296 0 : strip(Line);
7297 0 : uppercase(Line);
7298 0 : Pos = index(Line, ',');
7299 : }
7300 : } else {
7301 29 : Pos = len(Line);
7302 : }
7303 : }
7304 :
7305 270 : switch (i) {
7306 30 : case 1:
7307 30 : state.dataWeather->EPWHeaderTitle = stripped(Line.substr(0, Pos));
7308 30 : break;
7309 90 : case 2:
7310 : case 3:
7311 : case 4:
7312 90 : state.dataWeather->EPWHeaderTitle = strip(state.dataWeather->EPWHeaderTitle) + ' ' + stripped(Line.substr(0, Pos));
7313 90 : break;
7314 30 : case 5:
7315 30 : state.dataWeather->EPWHeaderTitle += " WMO#=" + stripped(Line.substr(0, Pos));
7316 30 : break;
7317 120 : case 6:
7318 : case 7:
7319 : case 8:
7320 : case 9: {
7321 : bool errFlag;
7322 120 : Real64 const Number = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7323 120 : if (!errFlag) {
7324 120 : switch (i) {
7325 30 : case 6:
7326 30 : state.dataWeather->WeatherFileLatitude = Number;
7327 30 : break;
7328 30 : case 7:
7329 30 : state.dataWeather->WeatherFileLongitude = Number;
7330 30 : break;
7331 30 : case 8:
7332 30 : state.dataWeather->WeatherFileTimeZone = Number;
7333 30 : break;
7334 30 : case 9:
7335 30 : state.dataWeather->WeatherFileElevation = Number;
7336 30 : break;
7337 0 : default:
7338 0 : break;
7339 : }
7340 : }
7341 120 : } break;
7342 0 : default:
7343 0 : ShowSevereError(state, format("GetEPWHeader:LOCATION, invalid numeric={}", Line.substr(0, Pos)));
7344 0 : ErrorsFound = true;
7345 0 : break;
7346 : }
7347 270 : Line.erase(0, Pos + 1);
7348 : }
7349 30 : state.dataEnvrn->WeatherFileLocationTitle = stripped(state.dataWeather->EPWHeaderTitle);
7350 30 : } break;
7351 30 : case Weather::EpwHeaderType::TypicalExtremePeriods: {
7352 30 : strip(Line);
7353 30 : Pos = index(Line, ',');
7354 30 : if (Pos == std::string::npos) {
7355 0 : if (len(Line) == 0) {
7356 0 : while (Pos == std::string::npos && len(Line) == 0) {
7357 0 : Line = state.files.inputWeatherFile.readLine().data;
7358 0 : strip(Line);
7359 0 : Pos = index(Line, ',');
7360 : }
7361 : } else {
7362 0 : Pos = len(Line);
7363 : }
7364 : }
7365 : bool IOStatus;
7366 30 : state.dataWeather->NumEPWTypExtSets = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7367 30 : Line.erase(0, Pos + 1);
7368 30 : state.dataWeather->TypicalExtremePeriods.allocate(state.dataWeather->NumEPWTypExtSets);
7369 30 : int TropExtremeCount = 0;
7370 207 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7371 177 : strip(Line);
7372 177 : Pos = index(Line, ',');
7373 177 : if (Pos != std::string::npos) {
7374 177 : state.dataWeather->TypicalExtremePeriods(i).Title = Line.substr(0, Pos);
7375 177 : Line.erase(0, Pos + 1);
7376 : } else {
7377 0 : ShowWarningError(state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={}", Line.substr(0, Pos)));
7378 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7379 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7380 0 : break;
7381 : }
7382 177 : Pos = index(Line, ',');
7383 177 : if (Pos != std::string::npos) {
7384 177 : state.dataWeather->TypicalExtremePeriods(i).TEType = Line.substr(0, Pos);
7385 177 : Line.erase(0, Pos + 1);
7386 177 : if (Util::SameString(state.dataWeather->TypicalExtremePeriods(i).TEType, "EXTREME")) {
7387 60 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MAX")) {
7388 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7389 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY SEASON - WEEK NEAR ANNUAL MIN")) {
7390 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7391 58 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MAX")) {
7392 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMax";
7393 58 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET SEASON - WEEK NEAR ANNUAL MIN")) {
7394 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeasonMin";
7395 : // to account for problems earlier in weather files:
7396 58 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7397 0 : if (TropExtremeCount == 0) {
7398 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Max";
7399 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMax";
7400 0 : ++TropExtremeCount;
7401 0 : } else if (TropExtremeCount == 1) {
7402 0 : state.dataWeather->TypicalExtremePeriods(i).Title = "No Dry Season - Week Near Annual Min";
7403 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeasonMin";
7404 0 : ++TropExtremeCount;
7405 : }
7406 : } else { // make new short titles
7407 58 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7408 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7409 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7410 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7411 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7412 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7413 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7414 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7415 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7416 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7417 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7418 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7419 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7420 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7421 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7422 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7423 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7424 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7425 0 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7426 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7427 : }
7428 : }
7429 : } else { // not extreme
7430 117 : if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SUMMER")) {
7431 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Summer";
7432 88 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WINTER")) {
7433 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Winter";
7434 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL HOT")) {
7435 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalHot";
7436 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "TROPICAL COLD")) {
7437 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "TropicalCold";
7438 59 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "AUTUMN")) {
7439 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Autumn";
7440 30 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO DRY")) {
7441 1 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoDrySeason";
7442 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "NO WET")) {
7443 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "NoWetSeason";
7444 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "WET ")) {
7445 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "WetSeason";
7446 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "DRY ")) {
7447 0 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "DrySeason";
7448 29 : } else if (has_prefixi(state.dataWeather->TypicalExtremePeriods(i).Title, "SPRING")) {
7449 29 : state.dataWeather->TypicalExtremePeriods(i).ShortTitle = "Spring";
7450 : }
7451 : }
7452 : } else {
7453 0 : ShowWarningError(state,
7454 0 : format("ProcessEPWHeader: Invalid Typical/Extreme Periods Header(WeatherFile)={} {}",
7455 0 : state.dataWeather->TypicalExtremePeriods(i).Title,
7456 0 : Line.substr(0, Pos)));
7457 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7458 0 : state.dataWeather->NumEPWTypExtSets = i - 1;
7459 0 : break;
7460 : }
7461 : int PMonth;
7462 : int PDay;
7463 : int PWeekDay;
7464 177 : Pos = index(Line, ',');
7465 177 : if (Pos != std::string::npos) {
7466 177 : std::string dateStringUC = Line.substr(0, Pos);
7467 177 : dateStringUC = uppercase(dateStringUC);
7468 177 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7469 177 : if (dateType != DateType::Invalid) {
7470 177 : if (PMonth != 0 && PDay != 0) {
7471 177 : state.dataWeather->TypicalExtremePeriods(i).StartMonth = PMonth;
7472 177 : state.dataWeather->TypicalExtremePeriods(i).StartDay = PDay;
7473 : }
7474 : } else {
7475 0 : ShowSevereError(
7476 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7477 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7478 0 : ErrorsFound = true;
7479 : }
7480 177 : Line.erase(0, Pos + 1);
7481 177 : }
7482 177 : Pos = index(Line, ',');
7483 177 : if (Pos != std::string::npos) {
7484 148 : std::string dateStringUC = Line.substr(0, Pos);
7485 148 : dateStringUC = uppercase(dateStringUC);
7486 148 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7487 148 : if (dateType != DateType::Invalid) {
7488 148 : if (PMonth != 0 && PDay != 0) {
7489 148 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7490 148 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7491 : }
7492 : } else {
7493 0 : ShowSevereError(
7494 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7495 0 : ShowContinueError(state, format("...on processing Typical/Extreme period #{}", i));
7496 0 : ErrorsFound = true;
7497 : }
7498 148 : Line.erase(0, Pos + 1);
7499 148 : } else { // Pos=0, probably last one
7500 29 : std::string const dateStringUC = uppercase(Line);
7501 29 : General::ProcessDateString(state, dateStringUC, PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7502 29 : if (dateType != DateType::Invalid) {
7503 29 : if (PMonth != 0 && PDay != 0) {
7504 29 : state.dataWeather->TypicalExtremePeriods(i).EndMonth = PMonth;
7505 29 : state.dataWeather->TypicalExtremePeriods(i).EndDay = PDay;
7506 : }
7507 : } else {
7508 0 : ShowSevereError(
7509 0 : state, format("ProcessEPWHeader: Invalid Typical/Extreme Periods End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7510 0 : ErrorsFound = true;
7511 : }
7512 29 : }
7513 : }
7514 : // Process periods to set up other values.
7515 207 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7516 177 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7517 : // JulianDay (Month,Day,LeapYearValue)
7518 177 : std::string const ExtremePeriodTitle = Util::makeUPPER(typicalExtPer.ShortTitle);
7519 177 : if (ExtremePeriodTitle == "SUMMER") {
7520 58 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7521 29 : typicalExtPer.MatchValue = "SummerExtreme";
7522 29 : typicalExtPer.MatchValue1 = "TropicalHot";
7523 29 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7524 : } else {
7525 29 : typicalExtPer.MatchValue = "SummerTypical";
7526 : }
7527 :
7528 119 : } else if (ExtremePeriodTitle == "WINTER") {
7529 58 : if (Util::SameString(typicalExtPer.TEType, "EXTREME")) {
7530 29 : typicalExtPer.MatchValue = "WinterExtreme";
7531 29 : typicalExtPer.MatchValue1 = "TropicalCold";
7532 29 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7533 : } else {
7534 29 : typicalExtPer.MatchValue = "WinterTypical";
7535 : }
7536 :
7537 61 : } else if (ExtremePeriodTitle == "AUTUMN") {
7538 29 : typicalExtPer.MatchValue = "AutumnTypical";
7539 :
7540 32 : } else if (ExtremePeriodTitle == "SPRING") {
7541 29 : typicalExtPer.MatchValue = "SpringTypical";
7542 :
7543 3 : } else if (ExtremePeriodTitle == "WETSEASON") {
7544 0 : typicalExtPer.MatchValue = "WetSeason";
7545 :
7546 3 : } else if (ExtremePeriodTitle == "DRYSEASON") {
7547 0 : typicalExtPer.MatchValue = "DrySeason";
7548 :
7549 3 : } else if (ExtremePeriodTitle == "NOWETSEASON") {
7550 0 : typicalExtPer.MatchValue = "NoWetSeason";
7551 :
7552 3 : } else if (ExtremePeriodTitle == "NODRYSEASON") {
7553 1 : typicalExtPer.MatchValue = "NoDrySeason";
7554 :
7555 2 : } else if ((ExtremePeriodTitle == "NODRYSEASONMAX") || (ExtremePeriodTitle == "NOWETSEASONMAX")) {
7556 1 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7557 1 : typicalExtPer.MatchValue1 = "TropicalHot";
7558 1 : typicalExtPer.MatchValue2 = "SummerExtreme";
7559 :
7560 1 : } else if ((ExtremePeriodTitle == "NODRYSEASONMIN") || (ExtremePeriodTitle == "NOWETSEASONMIN")) {
7561 1 : typicalExtPer.MatchValue = typicalExtPer.ShortTitle;
7562 1 : typicalExtPer.MatchValue1 = "TropicalCold";
7563 1 : typicalExtPer.MatchValue2 = "WinterExtreme";
7564 :
7565 0 : } else if (ExtremePeriodTitle == "TROPICALHOT") {
7566 0 : typicalExtPer.MatchValue = "TropicalHot";
7567 0 : typicalExtPer.MatchValue1 = "SummerExtreme";
7568 0 : typicalExtPer.MatchValue2 = "NoDrySeasonMax";
7569 :
7570 0 : } else if (ExtremePeriodTitle == "TROPICALCOLD") {
7571 0 : typicalExtPer.MatchValue = "TropicalCold";
7572 0 : typicalExtPer.MatchValue1 = "WinterExtreme";
7573 0 : typicalExtPer.MatchValue2 = "NoDrySeasonMin";
7574 :
7575 : } else {
7576 0 : typicalExtPer.MatchValue = "Invalid - no match";
7577 : }
7578 177 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, 0);
7579 177 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, 0);
7580 177 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7581 176 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7582 : } else {
7583 1 : typicalExtPer.TotalDays =
7584 1 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7585 : }
7586 177 : }
7587 30 : } break;
7588 30 : case Weather::EpwHeaderType::GroundTemperatures: {
7589 : // Added for ground surfaces defined with F or c factor method. TH 7/2009
7590 : // Assume the 0.5 m set of ground temperatures
7591 : // or first set on a weather file, if any.
7592 30 : Pos = index(Line, ',');
7593 30 : if (Pos != std::string::npos) {
7594 : bool errFlag;
7595 30 : int NumGrndTemps = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7596 30 : if (!errFlag && NumGrndTemps >= 1) {
7597 30 : Line.erase(0, Pos + 1);
7598 : // skip depth, soil conductivity, soil density, soil specific heat
7599 150 : for (int i = 1; i <= 4; ++i) {
7600 120 : Pos = index(Line, ',');
7601 120 : if (Pos == std::string::npos) {
7602 0 : Line.clear();
7603 0 : break;
7604 : }
7605 120 : Line.erase(0, Pos + 1);
7606 : }
7607 30 : state.dataWeather->GroundTempsFCFromEPWHeader = 0.0;
7608 30 : int actcount = 0;
7609 390 : for (int i = 1; i <= 12; ++i) { // take the first set of ground temperatures.
7610 360 : Pos = index(Line, ',');
7611 360 : if (Pos != std::string::npos) {
7612 360 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7613 360 : ++actcount;
7614 : } else {
7615 0 : if (len(Line) > 0) {
7616 0 : state.dataWeather->GroundTempsFCFromEPWHeader(i) = Util::ProcessNumber(Line.substr(0, Pos), errFlag);
7617 0 : ++actcount;
7618 : }
7619 0 : break;
7620 : }
7621 360 : Line.erase(0, Pos + 1);
7622 : }
7623 30 : if (actcount == 12) state.dataWeather->wthFCGroundTemps = true;
7624 : }
7625 : }
7626 30 : } break;
7627 31 : case Weather::EpwHeaderType::HolidaysDST: {
7628 : // A1, \field LeapYear Observed
7629 : // \type choice
7630 : // \key Yes
7631 : // \key No
7632 : // \note Yes if Leap Year will be observed for this file
7633 : // \note No if Leap Year days (29 Feb) should be ignored in this file
7634 : // A2, \field Daylight Saving Start Day
7635 : // A3, \field Daylight Saving End Day
7636 : // N1, \field Number of Holidays
7637 : // A4, \field Holiday 1 Name
7638 : // A5, \field Holiday 1 Day
7639 : // etc.
7640 : // Start with Minimum number of NumHdArgs
7641 31 : uppercase(Line);
7642 31 : NumHdArgs = 4;
7643 31 : int CurCount = 0;
7644 155 : for (int i = 1; i <= NumHdArgs; ++i) {
7645 124 : strip(Line);
7646 124 : Pos = index(Line, ',');
7647 124 : if (Pos == std::string::npos) {
7648 30 : if (len(Line) == 0) {
7649 0 : while (Pos == std::string::npos) {
7650 0 : Line = state.files.inputWeatherFile.readLine().data;
7651 0 : strip(Line);
7652 0 : uppercase(Line);
7653 0 : Pos = index(Line, ',');
7654 : }
7655 : } else {
7656 30 : Pos = len(Line);
7657 : }
7658 : }
7659 :
7660 : int PMonth;
7661 : int PDay;
7662 : int PWeekDay;
7663 : bool IOStatus;
7664 124 : if (i == 1) {
7665 31 : state.dataWeather->WFAllowsLeapYears = (Line[0] == 'Y');
7666 93 : } else if (i == 2) {
7667 : // In this section, we call ProcessDateString, and if that fails, we can recover from it
7668 : // by setting DST to false, so we don't affect ErrorsFound
7669 :
7670 : // call ProcessDateString with local bool (unused)
7671 : bool errflag1;
7672 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, errflag1);
7673 31 : if (dateType != DateType::Invalid) {
7674 : // ErrorsFound is still false after ProcessDateString
7675 31 : if (PMonth == 0 && PDay == 0) {
7676 30 : state.dataWeather->EPWDaylightSaving = false;
7677 : } else {
7678 1 : state.dataWeather->EPWDaylightSaving = true;
7679 1 : state.dataWeather->EPWDST.StDateType = dateType;
7680 1 : state.dataWeather->EPWDST.StMon = PMonth;
7681 1 : state.dataWeather->EPWDST.StDay = PDay;
7682 1 : state.dataWeather->EPWDST.StWeekDay = PWeekDay;
7683 : }
7684 : } else {
7685 : // ErrorsFound is untouched
7686 0 : ShowContinueError(
7687 0 : state, format("ProcessEPWHeader: Invalid Daylight Saving Period Start Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7688 0 : ShowContinueError(state, format("...invalid header={}", epwHeaders[static_cast<int>(headerType)]));
7689 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7690 0 : state.dataWeather->EPWDaylightSaving = false;
7691 : }
7692 :
7693 62 : } else if (i == 3) {
7694 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7695 31 : if (state.dataWeather->EPWDaylightSaving) {
7696 1 : if (dateType != DateType::Invalid) {
7697 1 : state.dataWeather->EPWDST.EnDateType = dateType;
7698 1 : state.dataWeather->EPWDST.EnMon = PMonth;
7699 1 : state.dataWeather->EPWDST.EnDay = PDay;
7700 1 : state.dataWeather->EPWDST.EnWeekDay = PWeekDay;
7701 : } else {
7702 0 : ShowWarningError(
7703 : state,
7704 0 : format("ProcessEPWHeader: Invalid Daylight Saving Period End Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7705 0 : ShowContinueError(state, "...Setting Weather File DST to false.");
7706 0 : state.dataWeather->EPWDaylightSaving = false;
7707 : }
7708 1 : state.dataWeather->DST = state.dataWeather->EPWDST;
7709 : }
7710 :
7711 31 : } else if (i == 4) {
7712 31 : int NumEPWHolidays = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7713 62 : state.dataWeather->NumSpecialDays =
7714 31 : NumEPWHolidays + state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "RunPeriodControl:SpecialDays");
7715 31 : state.dataWeather->SpecialDays.allocate(state.dataWeather->NumSpecialDays);
7716 31 : NumHdArgs = 4 + NumEPWHolidays * 2;
7717 :
7718 0 : } else if ((i >= 5)) {
7719 0 : if (mod(i, 2) != 0) {
7720 0 : ++CurCount;
7721 0 : if (CurCount > state.dataWeather->NumSpecialDays) {
7722 0 : ShowSevereError(state, "Too many SpecialDays");
7723 0 : ErrorsFound = true;
7724 : } else {
7725 0 : state.dataWeather->SpecialDays(CurCount).Name = Line.substr(0, Pos);
7726 : }
7727 : // Process name
7728 : } else {
7729 0 : if (CurCount <= state.dataWeather->NumSpecialDays) {
7730 0 : auto &specialDay = state.dataWeather->SpecialDays(CurCount);
7731 : // Process date
7732 0 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound);
7733 0 : if (dateType == DateType::MonthDay) {
7734 0 : specialDay.dateType = dateType;
7735 0 : specialDay.Month = PMonth;
7736 0 : specialDay.Day = PDay;
7737 0 : specialDay.WeekDay = 0;
7738 0 : specialDay.CompDate = PMonth * 32 + PDay;
7739 0 : specialDay.Duration = 1;
7740 0 : specialDay.DayType = 1;
7741 0 : specialDay.WthrFile = true;
7742 0 : } else if (dateType != DateType::Invalid) {
7743 0 : specialDay.dateType = dateType;
7744 0 : specialDay.Month = PMonth;
7745 0 : specialDay.Day = PDay;
7746 0 : specialDay.WeekDay = PWeekDay;
7747 0 : specialDay.CompDate = 0;
7748 0 : specialDay.Duration = 1;
7749 0 : specialDay.DayType = 1;
7750 0 : specialDay.WthrFile = true;
7751 : } else {
7752 0 : ShowSevereError(state, format("Invalid SpecialDay Date Field(WeatherFile)={}", Line.substr(0, Pos)));
7753 0 : ErrorsFound = true;
7754 : }
7755 : }
7756 : }
7757 : }
7758 124 : Line.erase(0, Pos + 1);
7759 : }
7760 211 : for (int i = 1; i <= state.dataWeather->NumEPWTypExtSets; ++i) {
7761 : // General::OrdinalDay (Month,Day,LeapYearValue)
7762 180 : auto &typicalExtPer = state.dataWeather->TypicalExtremePeriods(i);
7763 180 : typicalExtPer.StartJDay = General::OrdinalDay(typicalExtPer.StartMonth, typicalExtPer.StartDay, state.dataWeather->LeapYearAdd);
7764 180 : typicalExtPer.EndJDay = General::OrdinalDay(typicalExtPer.EndMonth, typicalExtPer.EndDay, state.dataWeather->LeapYearAdd);
7765 180 : if (typicalExtPer.StartJDay <= typicalExtPer.EndJDay) {
7766 179 : typicalExtPer.TotalDays = typicalExtPer.EndJDay - typicalExtPer.StartJDay + 1;
7767 : } else {
7768 1 : typicalExtPer.TotalDays =
7769 1 : General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - typicalExtPer.StartJDay + 1 + typicalExtPer.EndJDay;
7770 : }
7771 : }
7772 31 : } break;
7773 90 : case Weather::EpwHeaderType::Comments1:
7774 : case Weather::EpwHeaderType::Comments2:
7775 : case Weather::EpwHeaderType::DesignConditions: {
7776 : // no action
7777 90 : } break;
7778 30 : case Weather::EpwHeaderType::DataPeriods: {
7779 : // N1, \field Number of Data Periods
7780 : // N2, \field Number of Records per hour
7781 : // A1, \field Data Period 1 Name/Description
7782 : // A2, \field Data Period 1 Start Day of Week
7783 : // \type choice
7784 : // \key Sunday
7785 : // \key Monday
7786 : // \key Tuesday
7787 : // \key Wednesday
7788 : // \key Thursday
7789 : // \key Friday
7790 : // \key Saturday
7791 : // A3, \field Data Period 1 Start Day
7792 : // A4, \field Data Period 1 End Day
7793 30 : uppercase(Line);
7794 30 : NumHdArgs = 2;
7795 30 : int CurCount = 0;
7796 214 : for (int i = 1; i <= NumHdArgs; ++i) {
7797 184 : strip(Line);
7798 184 : Pos = index(Line, ',');
7799 184 : if (Pos == std::string::npos) {
7800 29 : if (len(Line) == 0) {
7801 0 : while (Pos == std::string::npos) {
7802 0 : Line = state.files.inputWeatherFile.readLine().data;
7803 0 : strip(Line);
7804 0 : uppercase(Line);
7805 0 : Pos = index(Line, ',');
7806 : }
7807 : } else {
7808 29 : Pos = len(Line);
7809 : }
7810 : }
7811 :
7812 : bool IOStatus;
7813 184 : if (i == 1) {
7814 30 : state.dataWeather->NumDataPeriods = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7815 30 : state.dataWeather->DataPeriods.allocate(state.dataWeather->NumDataPeriods);
7816 30 : NumHdArgs += 4 * state.dataWeather->NumDataPeriods;
7817 30 : if (state.dataWeather->NumDataPeriods > 0) {
7818 61 : for (auto &e : state.dataWeather->DataPeriods)
7819 31 : e.NumDays = 0;
7820 : }
7821 :
7822 154 : } else if (i == 2) {
7823 30 : state.dataWeather->NumIntervalsPerHour = Util::ProcessNumber(Line.substr(0, Pos), IOStatus);
7824 124 : } else if (i >= 3) {
7825 124 : int const CurOne = mod(i - 3, 4);
7826 : int PMonth;
7827 : int PDay;
7828 : int PWeekDay;
7829 : int PYear;
7830 124 : if (CurOne == 0) {
7831 : // Description of Data Period
7832 31 : ++CurCount;
7833 31 : if (CurCount > state.dataWeather->NumDataPeriods) {
7834 0 : ShowSevereError(state, "Too many data periods");
7835 0 : ErrorsFound = true;
7836 : } else {
7837 31 : state.dataWeather->DataPeriods(CurCount).Name = Line.substr(0, Pos);
7838 : }
7839 :
7840 93 : } else if (CurOne == 1) {
7841 : // Start Day of Week
7842 31 : if (CurCount <= state.dataWeather->NumDataPeriods) {
7843 31 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
7844 31 : dataPeriod.DayOfWeek = Line.substr(0, Pos);
7845 31 : dataPeriod.WeekDay = getEnumValue(Sched::dayTypeNamesUC, dataPeriod.DayOfWeek);
7846 31 : if (dataPeriod.WeekDay < 1 || dataPeriod.WeekDay > 7) {
7847 0 : ShowSevereError(state,
7848 0 : fmt::format("Weather File -- Invalid Start Day of Week for Data Period #{}, Invalid day={}",
7849 : CurCount,
7850 0 : dataPeriod.DayOfWeek));
7851 0 : ErrorsFound = true;
7852 : }
7853 : }
7854 :
7855 62 : } else if (CurOne == 2) {
7856 : // DataPeriod Start Day
7857 31 : if (CurCount <= state.dataWeather->NumDataPeriods) {
7858 31 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
7859 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
7860 31 : if (dateType == DateType::MonthDay) {
7861 31 : dataPeriod.StMon = PMonth;
7862 31 : dataPeriod.StDay = PDay;
7863 31 : dataPeriod.StYear = PYear;
7864 31 : if (PYear != 0) dataPeriod.HasYearData = true;
7865 : } else {
7866 0 : ShowSevereError(state,
7867 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day> (WeatherFile), found={}",
7868 0 : Line.substr(0, Pos)));
7869 0 : ErrorsFound = true;
7870 : }
7871 : }
7872 :
7873 31 : } else if (CurOne == 3) {
7874 31 : auto &dataPeriod = state.dataWeather->DataPeriods(CurCount);
7875 31 : if (CurCount <= state.dataWeather->NumDataPeriods) {
7876 31 : General::ProcessDateString(state, Line.substr(0, Pos), PMonth, PDay, PWeekDay, dateType, ErrorsFound, PYear);
7877 31 : if (dateType == DateType::MonthDay) {
7878 31 : dataPeriod.EnMon = PMonth;
7879 31 : dataPeriod.EnDay = PDay;
7880 31 : dataPeriod.EnYear = PYear;
7881 31 : if (PYear == 0 && dataPeriod.HasYearData) {
7882 0 : ShowWarningError(state, "Data Period (WeatherFile) - Start Date contains year. End Date does not.");
7883 0 : ShowContinueError(state, "...Assuming same year as Start Date for this data.");
7884 0 : dataPeriod.EnYear = dataPeriod.StYear;
7885 : }
7886 : } else {
7887 0 : ShowSevereError(state,
7888 0 : format("Data Periods must be of the form <DayOfYear> or <Month Day>, (WeatherFile) found={}",
7889 0 : Line.substr(0, Pos)));
7890 0 : ErrorsFound = true;
7891 : }
7892 : }
7893 31 : if (dataPeriod.StYear == 0 || dataPeriod.EnYear == 0) {
7894 30 : dataPeriod.DataStJDay = General::OrdinalDay(dataPeriod.StMon, dataPeriod.StDay, state.dataWeather->LeapYearAdd);
7895 30 : dataPeriod.DataEnJDay = General::OrdinalDay(dataPeriod.EnMon, dataPeriod.EnDay, state.dataWeather->LeapYearAdd);
7896 30 : if (dataPeriod.DataStJDay <= dataPeriod.DataEnJDay) {
7897 30 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
7898 : } else {
7899 0 : dataPeriod.NumDays = (365 - dataPeriod.DataStJDay + 1) + (dataPeriod.DataEnJDay - 1 + 1);
7900 : }
7901 : } else { // weather file has actual year(s)
7902 1 : dataPeriod.DataStJDay = computeJulianDate(dataPeriod.StYear, dataPeriod.StMon, dataPeriod.StDay);
7903 1 : dataPeriod.DataEnJDay = computeJulianDate(dataPeriod.EnYear, dataPeriod.EnMon, dataPeriod.EnDay);
7904 1 : dataPeriod.NumDays = dataPeriod.DataEnJDay - dataPeriod.DataStJDay + 1;
7905 : }
7906 : // Have processed the last item for this, can set up Weekdays for months
7907 31 : dataPeriod.MonWeekDay = 0;
7908 31 : if (!ErrorsFound) {
7909 93 : SetupWeekDaysByMonth(state,
7910 31 : state.dataWeather->DataPeriods(CurCount).StMon,
7911 31 : state.dataWeather->DataPeriods(CurCount).StDay,
7912 31 : state.dataWeather->DataPeriods(CurCount).WeekDay,
7913 31 : state.dataWeather->DataPeriods(CurCount).MonWeekDay);
7914 : }
7915 : }
7916 : }
7917 184 : Line.erase(0, Pos + 1);
7918 : }
7919 30 : } break;
7920 0 : default: {
7921 : // Invalid header type
7922 0 : assert(false);
7923 : } break;
7924 : }
7925 241 : }
7926 :
7927 67 : void SkipEPlusWFHeader(EnergyPlusData &state)
7928 : {
7929 :
7930 : // SUBROUTINE INFORMATION:
7931 : // AUTHOR Linda K. Lawrie
7932 : // DATE WRITTEN August 2000
7933 :
7934 : // PURPOSE OF THIS SUBROUTINE:
7935 : // This subroutine skips the initial header records on the EnergyPlus Weather File (in.epw).
7936 :
7937 : static constexpr std::string_view Header("DATA PERIODS");
7938 :
7939 : // Read in Header Information
7940 134 : InputFile::ReadResult<std::string> Line{"", true, false};
7941 :
7942 : // Headers should come in order
7943 : while (true) {
7944 536 : Line = state.files.inputWeatherFile.readLine();
7945 536 : if (Line.eof) {
7946 0 : ShowFatalError(state,
7947 0 : format("Unexpected End-of-File on EPW Weather file, while reading header information, looking for header={}", Header),
7948 0 : OptionalOutputFileRef{state.files.eso});
7949 : }
7950 536 : uppercase(Line.data);
7951 536 : if (has(Line.data, Header)) break;
7952 : }
7953 :
7954 : // Dummy process Data Periods line
7955 : // 'DATA PERIODS'
7956 : // N1, \field Number of Data Periods
7957 : // N2, \field Number of Records per hour
7958 : // A1, \field Data Period 1 Name/Description
7959 : // A2, \field Data Period 1 Start Day of Week
7960 : // \type choice
7961 : // \key Sunday
7962 : // \key Monday
7963 : // \key Tuesday
7964 : // \key Wednesday
7965 : // \key Thursday
7966 : // \key Friday
7967 : // \key Saturday
7968 : // A3, \field Data Period 1 Start Day
7969 : // A4, \field Data Period 1 End Day
7970 :
7971 67 : int NumHdArgs = 2;
7972 67 : int CurCount = 0;
7973 201 : for (int i = 1; i <= NumHdArgs; ++i) {
7974 134 : strip(Line.data);
7975 134 : std::string::size_type Pos = index(Line.data, ',');
7976 134 : if (Pos == std::string::npos) {
7977 0 : if (len(Line.data) == 0) {
7978 0 : while (Pos == std::string::npos) {
7979 0 : Line = state.files.inputWeatherFile.readLine();
7980 0 : strip(Line.data);
7981 0 : uppercase(Line.data);
7982 0 : Pos = index(Line.data, ',');
7983 : }
7984 : } else {
7985 0 : Pos = len(Line.data);
7986 : }
7987 : }
7988 :
7989 134 : if (i == 1) {
7990 : bool IOStatus;
7991 67 : int const NumPeriods = Util::ProcessNumber(Line.data.substr(0, Pos), IOStatus);
7992 67 : NumHdArgs += 4 * NumPeriods;
7993 67 : } else if ((i >= 3)) {
7994 0 : if (mod(i - 3, 4) == 0) ++CurCount;
7995 : }
7996 134 : Line.data.erase(0, Pos + 1);
7997 : }
7998 67 : }
7999 :
8000 22 : void ReportMissing_RangeData(EnergyPlusData &state)
8001 : {
8002 :
8003 : // SUBROUTINE INFORMATION:
8004 : // AUTHOR Linda Lawrie
8005 : // DATE WRITTEN January 2002
8006 :
8007 : // PURPOSE OF THIS SUBROUTINE:
8008 : // This subroutine reports the counts of missing/out of range data
8009 : // for weather file environments.
8010 :
8011 : static constexpr std::string_view MissString("Missing Data Found on Weather Data File");
8012 : static constexpr std::string_view msFmt("Missing {}, Number of items={:5}");
8013 : static constexpr std::string_view InvString("Invalid Data Found on Weather Data File");
8014 : static constexpr std::string_view ivFmt("Invalid {}, Number of items={:5}");
8015 : static constexpr std::string_view RangeString("Out of Range Data Found on Weather Data File");
8016 : static constexpr std::string_view rgFmt("Out of Range {} [{},{}], Number of items={:5}");
8017 :
8018 22 : if (!state.dataEnvrn->DisplayWeatherMissingDataWarnings) return;
8019 :
8020 0 : bool MissedHeader = false;
8021 0 : auto missedHeaderCheck = [&](Real64 const value, std::string const &description) {
8022 0 : if (value > 0) {
8023 0 : if (!MissedHeader) {
8024 0 : ShowWarningError(state, std::string{MissString});
8025 0 : MissedHeader = true;
8026 : }
8027 0 : ShowMessage(state, format(msFmt, "\"" + description + "\"", value));
8028 : }
8029 0 : };
8030 :
8031 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDryBulbTemp, "Dry Bulb Temperature");
8032 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutBaroPress, "Atmospheric Pressure");
8033 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutRelHum, "Relative Humidity");
8034 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OutDewPointTemp, "Dew Point Temperatures");
8035 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindSpeed, "Wind Speed");
8036 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.WindDir, "Wind Direction");
8037 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.BeamSolarRad, "Direct Radiation");
8038 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.DifSolarRad, "Diffuse Radiation");
8039 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.TotalSkyCover, "Total Sky Cover");
8040 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.OpaqueSkyCover, "Opaque Sky Cover");
8041 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.SnowDepth, "Snow Depth");
8042 0 : if (state.dataWeather->wvarsMissedCounts.WeathCodes > 0) {
8043 0 : ShowWarningError(state, std::string{InvString});
8044 0 : ShowMessage(state, format(ivFmt, "\"Weather Codes\" (not equal 9 digits)", state.dataWeather->wvarsMissedCounts.WeathCodes));
8045 : }
8046 0 : missedHeaderCheck(state.dataWeather->wvarsMissedCounts.LiquidPrecip, "Liquid Precipitation Depth");
8047 :
8048 0 : bool OutOfRangeHeader = false;
8049 : auto outOfRangeHeaderCheck = // (AUTO_OK_LAMBDA)
8050 0 : [&](Real64 const value, std::string_view description, std::string_view rangeLow, std::string_view rangeHigh, std::string_view extraMsg) {
8051 0 : if (value > 0) {
8052 0 : if (!OutOfRangeHeader) {
8053 0 : ShowWarningError(state, std::string{RangeString});
8054 0 : OutOfRangeHeader = true;
8055 : }
8056 0 : ShowMessage(state, EnergyPlus::format(rgFmt, description, rangeLow, rangeHigh, value));
8057 0 : if (!extraMsg.empty()) ShowMessage(state, std::string{extraMsg});
8058 : }
8059 0 : };
8060 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDryBulbTemp, "Dry Bulb Temperatures", ">=-90", "<=70", "");
8061 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutBaroPress,
8062 : "Atmospheric Pressure",
8063 : ">31000",
8064 : "<=120000",
8065 : "Out of Range values set to last good value");
8066 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutRelHum, "Relative Humidity", ">=0", "<=110", "");
8067 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.OutDewPointTemp, "Dew Point Temperatures", ">=-90", "<=70", "");
8068 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindSpeed, "Wind Speed", ">=0", "<=40", "");
8069 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.WindDir, "Wind Direction", ">=0", "<=360", "");
8070 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.BeamSolarRad, "Direct Radiation", ">=0", "NoLimit", "");
8071 0 : outOfRangeHeaderCheck(state.dataWeather->wvarsOutOfRangeCounts.DifSolarRad, "Diffuse Radiation", ">=0", "NoLimit", "");
8072 : }
8073 :
8074 112 : void SetupInterpolationValues(EnergyPlusData &state)
8075 : {
8076 :
8077 : // SUBROUTINE INFORMATION:
8078 : // AUTHOR Linda Lawrie
8079 : // DATE WRITTEN November 2002
8080 :
8081 : // PURPOSE OF THIS SUBROUTINE:
8082 : // This subroutine creates the "interpolation" values / weights that are used for
8083 : // interpolating weather data from hourly down to the time step level.
8084 :
8085 : // METHODOLOGY EMPLOYED:
8086 : // Create arrays (InterpolationValues, SolarInterpolationValues) dependent on
8087 : // Number of Time Steps in Hour. This will be used in the "SetCurrentWeather" procedure.
8088 :
8089 112 : state.dataWeather->Interpolation.allocate(state.dataGlobal->TimeStepsInHour);
8090 112 : state.dataWeather->SolarInterpolation.allocate(state.dataGlobal->TimeStepsInHour);
8091 112 : state.dataWeather->Interpolation = 0.0;
8092 112 : state.dataWeather->SolarInterpolation = 0.0;
8093 :
8094 716 : for (int tloop = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop) {
8095 604 : state.dataWeather->Interpolation(tloop) =
8096 604 : (state.dataGlobal->TimeStepsInHour == 1) ? 1.0 : min(1.0, (double(tloop) / double(state.dataGlobal->TimeStepsInHour)));
8097 : }
8098 :
8099 112 : if (mod(state.dataGlobal->TimeStepsInHour, 2) == 0) {
8100 : // even number of time steps.
8101 102 : int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
8102 :
8103 102 : state.dataWeather->SolarInterpolation(halfpoint) = 1.0;
8104 102 : Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
8105 399 : for (int tloop = halfpoint + 1, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
8106 297 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8107 : }
8108 297 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8109 195 : state.dataWeather->SolarInterpolation(tloop) = 1.0 - hpoint * tweight;
8110 : }
8111 : } else { // odd number of time steps
8112 10 : if (state.dataGlobal->TimeStepsInHour == 1) {
8113 10 : state.dataWeather->SolarInterpolation(1) = 0.5;
8114 0 : } else if (state.dataGlobal->TimeStepsInHour == 3) {
8115 0 : state.dataWeather->SolarInterpolation(1) = 5.0 / 6.0;
8116 0 : state.dataWeather->SolarInterpolation(2) = 5.0 / 6.0;
8117 0 : state.dataWeather->SolarInterpolation(3) = 0.5;
8118 : } else {
8119 0 : Real64 tweight = 1.0 / double(state.dataGlobal->TimeStepsInHour);
8120 0 : int halfpoint = state.dataGlobal->TimeStepsInHour / 2;
8121 0 : Real64 tweight1 = 1.0 - tweight / 2.0;
8122 0 : state.dataWeather->SolarInterpolation(halfpoint) = tweight1;
8123 0 : state.dataWeather->SolarInterpolation(halfpoint + 1) = tweight1;
8124 0 : for (int tloop = halfpoint + 2, hpoint = 1; tloop <= state.dataGlobal->TimeStepsInHour; ++tloop, ++hpoint) {
8125 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8126 : }
8127 0 : for (int tloop = halfpoint - 1, hpoint = 1; tloop >= 1; --tloop, ++hpoint) {
8128 0 : state.dataWeather->SolarInterpolation(tloop) = tweight1 - hpoint * tweight;
8129 : }
8130 : }
8131 : }
8132 112 : }
8133 :
8134 114 : void SetupEnvironmentTypes(EnergyPlusData &state)
8135 : {
8136 :
8137 : // SUBROUTINE INFORMATION:
8138 : // AUTHOR Linda Lawrie
8139 : // DATE WRITTEN October 2010
8140 :
8141 : // PURPOSE OF THIS SUBROUTINE:
8142 : // Make sure Environment derived type is set prior to getting
8143 : // Weather Properties
8144 :
8145 : // Transfer weather file information to the Environment derived type
8146 114 : state.dataWeather->Envrn = state.dataEnvrn->TotDesDays + 1;
8147 :
8148 : // Sizing Periods from Weather File
8149 114 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunDesPers; ++iRunPer, ++state.dataWeather->Envrn) {
8150 0 : auto const &runPer = state.dataWeather->RunPeriodDesignInput(iRunPer);
8151 0 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8152 :
8153 0 : envCurr.StartMonth = runPer.startMonth;
8154 0 : envCurr.StartDay = runPer.startDay;
8155 0 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, state.dataWeather->LeapYearAdd);
8156 0 : envCurr.TotalDays = runPer.totalDays;
8157 0 : envCurr.EndMonth = runPer.endMonth;
8158 0 : envCurr.EndDay = runPer.endDay;
8159 0 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, state.dataWeather->LeapYearAdd);
8160 0 : envCurr.NumSimYears = runPer.numSimYears;
8161 0 : if (envCurr.StartJDay <= envCurr.EndJDay) {
8162 0 : envCurr.TotalDays = (envCurr.EndJDay - envCurr.StartJDay + 1) * envCurr.NumSimYears;
8163 : } else {
8164 0 : envCurr.TotalDays =
8165 0 : (General::OrdinalDay(12, 31, state.dataWeather->LeapYearAdd) - envCurr.StartJDay + 1 + envCurr.EndJDay) * envCurr.NumSimYears;
8166 : }
8167 0 : state.dataEnvrn->TotRunDesPersDays += envCurr.TotalDays;
8168 0 : envCurr.UseDST = runPer.useDST;
8169 0 : envCurr.UseHolidays = runPer.useHolidays;
8170 0 : envCurr.Title = runPer.title;
8171 0 : envCurr.cKindOfEnvrn = runPer.periodType;
8172 0 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodDesign;
8173 0 : envCurr.DesignDayNum = 0;
8174 0 : envCurr.RunPeriodDesignNum = iRunPer;
8175 0 : envCurr.DayOfWeek = runPer.dayOfWeek;
8176 0 : envCurr.MonWeekDay = runPer.monWeekDay;
8177 0 : envCurr.SetWeekDays = false;
8178 0 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8179 0 : envCurr.UseRain = runPer.useRain;
8180 0 : envCurr.UseSnow = runPer.useSnow;
8181 0 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // this will just the default
8182 : }
8183 :
8184 : // RunPeriods from weather file
8185 172 : for (int iRunPer = 1; iRunPer <= state.dataWeather->TotRunPers; ++iRunPer, ++state.dataWeather->Envrn) {
8186 58 : auto const &runPer = state.dataWeather->RunPeriodInput(iRunPer);
8187 58 : auto &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8188 :
8189 58 : envCurr.StartMonth = runPer.startMonth;
8190 58 : envCurr.StartDay = runPer.startDay;
8191 58 : envCurr.StartYear = runPer.startYear;
8192 58 : envCurr.EndMonth = runPer.endMonth;
8193 58 : envCurr.EndDay = runPer.endDay;
8194 58 : envCurr.EndYear = runPer.endYear;
8195 58 : envCurr.NumSimYears = runPer.numSimYears;
8196 58 : envCurr.CurrentYear = runPer.startYear;
8197 58 : envCurr.IsLeapYear = runPer.isLeapYear;
8198 58 : envCurr.TreatYearsAsConsecutive = true;
8199 58 : if (runPer.actualWeather) {
8200 : // This will require leap years to be present, thus Julian days can be used for all the calculations
8201 0 : envCurr.StartJDay = envCurr.StartDate = runPer.startJulianDate;
8202 0 : envCurr.EndJDay = envCurr.EndDate = runPer.endJulianDate;
8203 0 : envCurr.TotalDays = envCurr.EndDate - envCurr.StartDate + 1;
8204 0 : envCurr.RawSimDays = envCurr.EndDate - envCurr.StartDate + 1;
8205 0 : envCurr.MatchYear = true;
8206 0 : envCurr.ActualWeather = true;
8207 : } else { // std RunPeriod
8208 58 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8209 58 : if (envCurr.StartYear == envCurr.EndYear) {
8210 : // Short-circuit all the calculations, we're in a single year
8211 :
8212 58 : envCurr.IsLeapYear = isLeapYear(envCurr.StartYear) && state.dataWeather->WFAllowsLeapYears;
8213 58 : int LocalLeapYearAdd = (int)envCurr.IsLeapYear;
8214 :
8215 58 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, LocalLeapYearAdd);
8216 58 : envCurr.EndJDay = General::OrdinalDay(runPer.endMonth, runPer.endDay, LocalLeapYearAdd);
8217 58 : envCurr.RawSimDays = (envCurr.EndJDay - envCurr.StartJDay + 1);
8218 58 : envCurr.TotalDays = envCurr.RawSimDays;
8219 : } else {
8220 : // Environment crosses year boundaries
8221 0 : envCurr.RollDayTypeOnRepeat = runPer.RollDayTypeOnRepeat;
8222 0 : envCurr.StartJDay = General::OrdinalDay(runPer.startMonth, runPer.startDay, (int)runPer.isLeapYear);
8223 0 : envCurr.EndJDay = General::OrdinalDay(
8224 0 : runPer.endMonth, runPer.endDay, (int)(isLeapYear(runPer.endYear) && state.dataWeather->WFAllowsLeapYears));
8225 0 : envCurr.TotalDays = 366 - envCurr.StartJDay + envCurr.EndJDay + 365 * std::max(envCurr.NumSimYears - 2, 0);
8226 0 : if (state.dataWeather->WFAllowsLeapYears) {
8227 : // First year
8228 0 : if (envCurr.StartJDay < 59) {
8229 0 : if (isLeapYear(envCurr.StartYear)) {
8230 0 : ++envCurr.TotalDays;
8231 : }
8232 : }
8233 : // Middle years
8234 0 : for (int yr = envCurr.StartYear + 1; yr < envCurr.EndYear; ++yr) {
8235 0 : if (isLeapYear(yr)) {
8236 0 : ++envCurr.TotalDays;
8237 : }
8238 : }
8239 : // Last year not needed, the end ordinal date will take this into account
8240 : }
8241 0 : envCurr.RawSimDays = envCurr.TotalDays;
8242 : }
8243 : }
8244 58 : envCurr.UseDST = runPer.useDST;
8245 58 : envCurr.UseHolidays = runPer.useHolidays;
8246 58 : if (runPer.title.empty()) {
8247 2 : envCurr.Title = state.dataEnvrn->WeatherFileLocationTitle;
8248 : } else {
8249 56 : envCurr.Title = runPer.title;
8250 : }
8251 58 : if (envCurr.KindOfEnvrn == Constant::KindOfSim::ReadAllWeatherData) {
8252 2 : envCurr.cKindOfEnvrn = "ReadAllWeatherDataRunPeriod";
8253 : } else {
8254 56 : envCurr.cKindOfEnvrn = "WeatherFileRunPeriod";
8255 56 : envCurr.KindOfEnvrn = Constant::KindOfSim::RunPeriodWeather;
8256 : }
8257 58 : envCurr.DayOfWeek = runPer.dayOfWeek;
8258 58 : envCurr.MonWeekDay = runPer.monWeekDay;
8259 58 : envCurr.SetWeekDays = false;
8260 58 : envCurr.ApplyWeekendRule = runPer.applyWeekendRule;
8261 58 : envCurr.UseRain = runPer.useRain;
8262 58 : envCurr.UseSnow = runPer.useSnow;
8263 58 : envCurr.firstHrInterpUseHr1 = runPer.firstHrInterpUsingHr1; // first hour interpolation choice
8264 : } // for (i)
8265 114 : }
8266 :
8267 124 : bool isLeapYear(int const Year)
8268 : {
8269 : // true if it's a leap year, false if not.
8270 :
8271 124 : if (mod(Year, 4) == 0) { // Potential Leap Year
8272 5 : if (!(mod(Year, 100) == 0 && mod(Year, 400) != 0)) {
8273 5 : return true;
8274 : }
8275 : }
8276 119 : return false;
8277 : }
8278 :
8279 320 : int computeJulianDate(int const gyyyy, // input/output gregorian year, should be specified as 4 digits
8280 : int const gmm, // input/output gregorian month
8281 : int const gdd // input/output gregorian day
8282 : )
8283 : {
8284 : // SUBROUTINE INFORMATION:
8285 : // AUTHOR Jason DeGraw
8286 : // DATE WRITTEN 10/25/2017
8287 :
8288 : // PURPOSE OF THIS SUBROUTINE:
8289 : // Split the former JGDate function in two. Convert a gregorian
8290 : // date to actual julian date. the advantage of storing a julian date
8291 : // in the jdate format rather than a 5 digit format is that any
8292 : // number of days can be add or subtracted to jdate and
8293 : // that result is a proper julian date.
8294 :
8295 : // REFERENCES:
8296 : // for discussion of this algorithm,
8297 : // see cacm, vol 11, no 10, oct 1968, page 657
8298 :
8299 320 : int tyyyy = gyyyy;
8300 320 : int tmm = gmm;
8301 320 : int tdd = gdd;
8302 320 : int l = (tmm - 14) / 12;
8303 320 : return tdd - 32075 + 1461 * (tyyyy + 4800 + l) / 4 + 367 * (tmm - 2 - l * 12) / 12 - 3 * ((tyyyy + 4900 + l) / 100) / 4;
8304 : }
8305 :
8306 4 : int computeJulianDate(GregorianDate const &gdate)
8307 : {
8308 4 : return computeJulianDate(gdate.year, gdate.month, gdate.day);
8309 : }
8310 :
8311 4 : GregorianDate computeGregorianDate(int const jdate)
8312 : {
8313 4 : int tdate = jdate;
8314 4 : int l = tdate + 68569;
8315 4 : int n = 4 * l / 146097;
8316 4 : l -= (146097 * n + 3) / 4;
8317 4 : int tyyyy = 4000 * (l + 1) / 1461001;
8318 4 : l = l - 1461 * tyyyy / 4 + 31;
8319 4 : int tmm = 80 * l / 2447;
8320 4 : int tdd = l - 2447 * tmm / 80;
8321 4 : l = tmm / 11;
8322 4 : tmm += 2 - 12 * l;
8323 4 : tyyyy += 100 * (n - 49) + l;
8324 4 : return {tyyyy, tmm, tdd};
8325 : }
8326 :
8327 6 : Sched::DayType calculateDayOfWeek(EnergyPlusData &state, int const year, int const month, int const day)
8328 : {
8329 :
8330 : // FUNCTION INFORMATION:
8331 : // AUTHOR Linda Lawrie
8332 : // DATE WRITTEN March 2012
8333 : // MODIFIED October 2017, Jason DeGraw
8334 :
8335 : // PURPOSE OF THIS FUNCTION:
8336 : // Calculate the correct day of week.
8337 :
8338 : // METHODOLOGY EMPLOYED:
8339 : // Zeller's algorithm.
8340 :
8341 : // REFERENCES:
8342 : // http://en.wikipedia.org/wiki/Zeller%27s_congruence
8343 : // and other references around the web.
8344 :
8345 6 : int Gyyyy(year); // Gregorian yyyy
8346 6 : int Gmm(month); // Gregorian mm
8347 :
8348 : // Jan, Feb are 13, 14 months of previous year
8349 6 : if (Gmm < 3) {
8350 6 : Gmm += 12;
8351 6 : --Gyyyy;
8352 : }
8353 :
8354 6 : state.dataEnvrn->DayOfWeek = mod(day + (13 * (Gmm + 1) / 5) + Gyyyy + (Gyyyy / 4) + 6 * (Gyyyy / 100) + (Gyyyy / 400), 7);
8355 6 : if (state.dataEnvrn->DayOfWeek == 0) state.dataEnvrn->DayOfWeek = 7;
8356 :
8357 6 : return static_cast<Sched::DayType>(state.dataEnvrn->DayOfWeek);
8358 : }
8359 :
8360 57 : int calculateDayOfYear(int const Month, int const Day, bool const leapYear)
8361 : {
8362 :
8363 : // FUNCTION INFORMATION:
8364 : // AUTHOR Jason DeGraw
8365 : // DATE WRITTEN October 10, 2017
8366 :
8367 : // PURPOSE OF THIS FUNCTION:
8368 : // Compute the day of the year for leap and non-leap years.
8369 :
8370 : static std::array<int, 12> const daysbefore{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}};
8371 : static std::array<int, 12> const daysbeforeleap{{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
8372 :
8373 : // Could probably do some bounds checking here, but for now assume the month is in [1, 12]
8374 57 : if (leapYear) {
8375 2 : return daysbeforeleap[Month - 1] + Day;
8376 : } else {
8377 55 : return daysbefore[Month - 1] + Day;
8378 : }
8379 : }
8380 :
8381 124 : bool validMonthDay(int const month, int const day, int const leapYearAdd)
8382 : {
8383 :
8384 : // FUNCTION INFORMATION:
8385 : // AUTHOR Jason DeGraw
8386 : // DATE WRITTEN October 31, 2017
8387 :
8388 : // PURPOSE OF THIS FUNCTION:
8389 : // Determine if a month/day+leapyear combination is valid.
8390 :
8391 124 : switch (month) {
8392 123 : case 1:
8393 : case 3:
8394 : case 5:
8395 : case 7:
8396 : case 8:
8397 : case 10:
8398 : case 12:
8399 123 : if (day > 31) {
8400 0 : return false;
8401 : }
8402 123 : break;
8403 0 : case 4:
8404 : case 6:
8405 : case 9:
8406 : case 11:
8407 0 : if (day > 30) {
8408 0 : return false;
8409 : }
8410 0 : break;
8411 1 : case 2:
8412 1 : if (day > 28 + leapYearAdd) {
8413 0 : return false;
8414 : }
8415 1 : break;
8416 0 : default:
8417 0 : return false;
8418 : }
8419 124 : return true;
8420 : }
8421 :
8422 3 : void AnnualMonthlyDryBulbWeatherData::CalcAnnualAndMonthlyDryBulbTemp(EnergyPlusData &state)
8423 : {
8424 :
8425 : // PURPOSE OF THIS SUBROUTINE:
8426 : // Calculates monthly daily average outdoor air drybulb temperature from
8427 : // either weather (*.EPW) file or reads monthly daily average outdoor air
8428 : // drybulb temperature from STAT (*.stat) for use to autosize main water
8429 : // temperature.
8430 :
8431 3 : Real64 MonthlyDailyDryBulbMin(200.0); // monthly-daily minimum outside air dry-bulb temperature
8432 3 : Real64 MonthlyDailyDryBulbMax(-200.0); // monthly-daily maximum outside air dry-bulb temperature
8433 3 : Real64 AnnualDailyAverageDryBulbTempSum(0.0); // annual sum of daily average outside air dry-bulb temperature
8434 3 : Array1D<Real64> MonthlyAverageDryBulbTemp(12, 0.0); // monthly-daily average outside air temperature
8435 :
8436 3 : if (!this->OADryBulbWeatherDataProcessed) {
8437 3 : const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
8438 3 : const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
8439 3 : if (statFileExists) {
8440 2 : auto statFile = state.files.inStatFilePath.try_open();
8441 2 : if (!statFile.good()) {
8442 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", statFile.filePath));
8443 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8444 0 : return;
8445 : }
8446 :
8447 2 : std::string lineAvg;
8448 136 : while (statFile.good()) {
8449 135 : auto lineIn = statFile.readLine();
8450 135 : if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
8451 8 : for (int i = 1; i <= 7; ++i) {
8452 7 : lineIn = statFile.readLine();
8453 : }
8454 1 : lineIn = statFile.readLine();
8455 1 : lineAvg = lineIn.data;
8456 1 : break;
8457 : }
8458 135 : }
8459 2 : if (lineAvg.empty()) {
8460 2 : ShowSevereError(
8461 : state,
8462 2 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have Monthly Statistics for Dry Bulb temperatures.",
8463 : statFile.filePath));
8464 2 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8465 1 : return;
8466 1 : } else if (lineAvg.find("Daily Avg") == std::string::npos) {
8467 0 : ShowSevereError(state,
8468 0 : format("CalcAnnualAndMonthlyDryBulbTemp: Stat file '{}' does not have the 'Daily Avg' line in the Monthly "
8469 : "Statistics for Dry Bulb temperatures.",
8470 : statFile.filePath));
8471 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8472 0 : return;
8473 : } else {
8474 1 : int AnnualNumberOfDays = 0;
8475 13 : for (int i = 1; i <= 12; ++i) {
8476 12 : MonthlyAverageDryBulbTemp(i) = OutputReportTabular::StrToReal(OutputReportTabular::GetColumnUsingTabs(lineAvg, i + 2));
8477 12 : AnnualDailyAverageDryBulbTempSum += MonthlyAverageDryBulbTemp(i) * state.dataWeather->EndDayOfMonth(i);
8478 12 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyAverageDryBulbTemp(i));
8479 12 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyAverageDryBulbTemp(i));
8480 12 : AnnualNumberOfDays += state.dataWeather->EndDayOfMonth(i);
8481 : }
8482 1 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8483 1 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8484 1 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8485 1 : this->OADryBulbWeatherDataProcessed = true;
8486 : }
8487 4 : } else if (epwFileExists) {
8488 1 : auto epwFile = state.files.inputWeatherFilePath.try_open();
8489 1 : bool epwHasLeapYear(false);
8490 1 : if (!epwFile.good()) {
8491 0 : ShowSevereError(state, format("CalcAnnualAndMonthlyDryBulbTemp: Could not open file {} for input (read).", epwFile.filePath));
8492 0 : ShowContinueError(state, "Water Mains Temperature will be set to a fixed default value of 10.0 C.");
8493 0 : return;
8494 : }
8495 9 : for (int i = 1; i <= 8; ++i) { // Headers
8496 8 : auto epwLine = epwFile.readLine();
8497 :
8498 8 : if (i == 5) {
8499 : // HOLIDAYS/DAYLIGHT SAVINGS,Yes,0,0,0
8500 1 : std::string::size_type pos = index(epwLine.data, ',');
8501 1 : epwLine.data.erase(0, pos + 1);
8502 1 : pos = index(epwLine.data, ',');
8503 1 : std::string LeapYear = Util::makeUPPER(epwLine.data.substr(0, pos));
8504 1 : if (LeapYear[0] == 'Y') {
8505 0 : epwHasLeapYear = true;
8506 : }
8507 1 : }
8508 8 : }
8509 1 : Array1D<int> EndDayOfMonthLocal;
8510 1 : EndDayOfMonthLocal = state.dataWeather->EndDayOfMonth;
8511 1 : if (epwHasLeapYear) {
8512 : // increase number of days for february by one day if weather data has leap year
8513 0 : EndDayOfMonthLocal(2) = EndDayOfMonthLocal(2) + 1;
8514 : }
8515 13 : for (int i = 1; i <= 12; ++i) {
8516 12 : Real64 MonthlyDailyDryBulbAvg = 0.0;
8517 12 : int DaysCountOfMonth = EndDayOfMonthLocal(i);
8518 377 : for (int DayNum = 1; DayNum <= DaysCountOfMonth; ++DayNum) {
8519 365 : Real64 DailyAverageDryBulbTemp = 0.0;
8520 : std::string::size_type pos;
8521 9125 : for (int j = 1; j <= 24; ++j) {
8522 8760 : auto epwLine = epwFile.readLine();
8523 61320 : for (int ind = 1; ind <= 6; ++ind) {
8524 52560 : pos = index(epwLine.data, ',');
8525 52560 : epwLine.data.erase(0, pos + 1);
8526 : }
8527 8760 : pos = index(epwLine.data, ',');
8528 8760 : Real64 HourlyDryBulbTemp = OutputReportTabular::StrToReal(epwLine.data.substr(0, pos));
8529 8760 : DailyAverageDryBulbTemp += (HourlyDryBulbTemp / 24.0);
8530 8760 : }
8531 365 : AnnualDailyAverageDryBulbTempSum += DailyAverageDryBulbTemp;
8532 365 : MonthlyDailyDryBulbAvg += (DailyAverageDryBulbTemp / DaysCountOfMonth);
8533 : }
8534 12 : MonthlyAverageDryBulbTemp(i) = MonthlyDailyDryBulbAvg;
8535 12 : MonthlyDailyDryBulbMin = min(MonthlyDailyDryBulbMin, MonthlyDailyDryBulbAvg);
8536 12 : MonthlyDailyDryBulbMax = max(MonthlyDailyDryBulbMax, MonthlyDailyDryBulbAvg);
8537 : }
8538 : // calculate annual average outdoor air dry-bulb temperature and monthly daily average
8539 : // outdoor air temperature maximum difference
8540 1 : int AnnualNumberOfDays = 365;
8541 1 : if (epwHasLeapYear) AnnualNumberOfDays++;
8542 1 : this->AnnualAvgOADryBulbTemp = AnnualDailyAverageDryBulbTempSum / AnnualNumberOfDays;
8543 1 : this->MonthlyAvgOADryBulbTempMaxDiff = MonthlyDailyDryBulbMax - MonthlyDailyDryBulbMin;
8544 1 : this->MonthlyDailyAverageDryBulbTemp = MonthlyAverageDryBulbTemp;
8545 1 : this->OADryBulbWeatherDataProcessed = true;
8546 1 : } else {
8547 0 : ShowSevereError(state, "CalcAnnualAndMonthlyDryBulbTemp: weather file or stat file does not exist.");
8548 0 : ShowContinueError(state, format("Weather file: {}.", state.files.inputWeatherFilePath.filePath));
8549 0 : ShowContinueError(state, format("Stat file: {}.", state.files.inStatFilePath.filePath));
8550 0 : ShowContinueError(state, "Water Mains Monthly Temperature cannot be calculated using CorrelationFromWeatherFile method.");
8551 0 : ShowContinueError(state, "Instead a fixed default value of 10.0 C will be used.");
8552 : }
8553 : }
8554 3 : }
8555 :
8556 104 : void ReportWaterMainsTempParameters(EnergyPlusData &state)
8557 : {
8558 : // PURPOSE OF THIS SUBROUTINE:
8559 : // report site water mains temperature object user inputs and/or parameters calculated
8560 : // from weather or stat file
8561 :
8562 104 : if (!state.files.eio.good()) {
8563 0 : return;
8564 : }
8565 :
8566 104 : std::stringstream ss;
8567 104 : auto *eiostream = &ss;
8568 :
8569 : // Write annual average OA temperature and maximum difference in monthly-daily average outdoor air temperature
8570 : *eiostream << "! <Site Water Mains Temperature Information>"
8571 : ",Calculation Method{}"
8572 : ",Water Mains Temperature Schedule Name{}"
8573 : ",Annual Average Outdoor Air Temperature{C}"
8574 : ",Maximum Difference In Monthly Average Outdoor Air Temperatures{deltaC}"
8575 104 : ",Fixed Default Water Mains Temperature{C}\n";
8576 :
8577 104 : switch (state.dataWeather->WaterMainsTempsMethod) {
8578 0 : case WaterMainsTempCalcMethod::Schedule:
8579 0 : *eiostream << "Site Water Mains Temperature Information,";
8580 0 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8581 0 : << state.dataWeather->waterMainsTempSched->Name << ",";
8582 0 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8583 0 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8584 0 : *eiostream << "NA\n";
8585 0 : break;
8586 6 : case WaterMainsTempCalcMethod::Correlation:
8587 6 : *eiostream << "Site Water Mains Temperature Information,";
8588 6 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8589 : << "NA"
8590 12 : << ",";
8591 6 : *eiostream << format("{:.2R}", state.dataWeather->WaterMainsTempsAnnualAvgAirTemp) << ","
8592 12 : << format("{:.2R}", state.dataWeather->WaterMainsTempsMaxDiffAirTemp) << ",";
8593 6 : *eiostream << "NA\n";
8594 6 : break;
8595 1 : case WaterMainsTempCalcMethod::CorrelationFromWeatherFile:
8596 1 : if (state.dataWeather->OADryBulbAverage.OADryBulbWeatherDataProcessed) {
8597 1 : *eiostream << "Site Water Mains Temperature Information,";
8598 1 : *eiostream << waterMainsCalcMethodNames[static_cast<int>(state.dataWeather->WaterMainsTempsMethod)] << ","
8599 : << "NA"
8600 2 : << ",";
8601 2 : *eiostream << format("{:.2R}", state.dataWeather->OADryBulbAverage.AnnualAvgOADryBulbTemp) << ","
8602 2 : << format("{:.2R}", state.dataWeather->OADryBulbAverage.MonthlyAvgOADryBulbTempMaxDiff) << ","
8603 3 : << "NA\n";
8604 : } else {
8605 0 : *eiostream << "Site Water Mains Temperature Information,";
8606 : *eiostream << "FixedDefault"
8607 : << ","
8608 : << "NA"
8609 : << ","
8610 : << "NA"
8611 : << ","
8612 : << "NA"
8613 0 : << "," << format("{:.1R}", 10.0) << '\n';
8614 : }
8615 1 : break;
8616 97 : default:
8617 97 : *eiostream << "Site Water Mains Temperature Information,";
8618 : *eiostream << "FixedDefault"
8619 : << ","
8620 : << "NA"
8621 : << ","
8622 : << "NA"
8623 : << ","
8624 : << "NA"
8625 97 : << "," << format("{:.1R}", 10.0) << '\n';
8626 97 : break;
8627 : }
8628 :
8629 104 : print(state.files.eio, "{}", ss.str());
8630 104 : }
8631 :
8632 90504 : void calcSky(EnergyPlusData &state,
8633 : Real64 &HorizIRSky,
8634 : Real64 &SkyTemp,
8635 : Real64 OpaqueSkyCover,
8636 : Real64 DryBulb,
8637 : Real64 DewPoint,
8638 : Real64 RelHum,
8639 : Real64 IRHoriz)
8640 : {
8641 90504 : if (IRHoriz <= 0.0) IRHoriz = 9999.0;
8642 :
8643 90504 : auto const &envCurr = state.dataWeather->Environment(state.dataWeather->Envrn);
8644 90504 : if (!envCurr.UseWeatherFileHorizontalIR || IRHoriz >= 9999.0) {
8645 : // Missing or user defined to not use IRHoriz from weather, using sky cover and clear sky emissivity
8646 384 : Real64 ESky = CalcSkyEmissivity(state, envCurr.skyTempModel, OpaqueSkyCover, DryBulb, DewPoint, RelHum);
8647 384 : HorizIRSky = ESky * Constant::StefanBoltzmann * pow_4(DryBulb + Constant::Kelvin);
8648 384 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8649 384 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8650 384 : SkyTemp = (DryBulb + Constant::Kelvin) * root_4(ESky) - Constant::Kelvin;
8651 : } else {
8652 0 : SkyTemp = 0.0; // dealt with later
8653 : }
8654 384 : } else {
8655 : // Valid IR from weather files
8656 90120 : HorizIRSky = IRHoriz;
8657 90120 : if (envCurr.skyTempModel == SkyTempModel::Brunt || envCurr.skyTempModel == SkyTempModel::Idso ||
8658 90120 : envCurr.skyTempModel == SkyTempModel::BerdahlMartin || envCurr.skyTempModel == SkyTempModel::ClarkAllen) {
8659 90120 : SkyTemp = root_4(IRHoriz / Constant::StefanBoltzmann) - Constant::Kelvin;
8660 : } else {
8661 0 : SkyTemp = 0.0; // dealt with later
8662 : }
8663 : }
8664 90504 : }
8665 :
8666 : } // namespace Weather
8667 :
8668 : } // namespace EnergyPlus
|