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 <map>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/string.functions.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <EnergyPlus/CommandLineInterface.hh>
56 : #include <EnergyPlus/Data/EnergyPlusData.hh>
57 : #include <EnergyPlus/DataEnvironment.hh>
58 : #include <EnergyPlus/DataStringGlobals.hh>
59 : #include <EnergyPlus/DataSystemVariables.hh>
60 : #include <EnergyPlus/EMSManager.hh>
61 : #include <EnergyPlus/FileSystem.hh>
62 : #include <EnergyPlus/General.hh>
63 : // #include <EnergyPlus/GlobalNames.hh>
64 : #include <EnergyPlus/InputProcessing/CsvParser.hh>
65 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
66 : #include <EnergyPlus/OutputProcessor.hh>
67 : #include <EnergyPlus/ScheduleManager.hh>
68 : #include <EnergyPlus/StringUtilities.hh>
69 : #include <EnergyPlus/UtilityRoutines.hh>
70 : #include <EnergyPlus/WeatherManager.hh>
71 :
72 : namespace EnergyPlus {
73 :
74 : namespace Sched {
75 : // Module containing the Schedule Manager routines
76 :
77 : // MODULE INFORMATION:
78 : // AUTHOR Linda K. Lawrie
79 : // DATE WRITTEN September 1997
80 : // MODIFIED January 2003 -- added sub-hourly schedule possibility (and interval scheduling)
81 : // J. Glazer January 2005 -- added Schedule:File
82 : // Michael Wetter February 2010 -- added Schedule for external Interface
83 : // L Lawrie - October 2012 - added sub-hourly option for Schedule:File
84 :
85 : // PURPOSE OF THIS MODULE:
86 : // To provide the capabilities of getting the schedule data from the input,
87 : // validating it, and storing it in such a manner that the schedule manager
88 : // can provide the scheduling value needs for the simulation.
89 :
90 : // REFERENCES:
91 : // Proposal for Schedule Manager in EnergyPlus (Rick Strand)
92 :
93 : // MODULE PARAMETER DEFINITIONS
94 17675 : int GetScheduleTypeNum(EnergyPlusData &state, std::string const &name)
95 : {
96 17675 : auto const &s_sched = state.dataSched;
97 45584 : for (int i = 0; i < (int)s_sched->scheduleTypes.size(); ++i) {
98 45572 : if (s_sched->scheduleTypes[i]->Name == name) {
99 17663 : return i;
100 : }
101 : }
102 12 : return SchedNum_Invalid;
103 : }
104 :
105 26302 : Real64 ScheduleBase::getMinVal(EnergyPlusData &state)
106 : {
107 26302 : if (!isMinMaxSet) {
108 2 : setMinMaxVals(state);
109 : }
110 26302 : return minVal;
111 : }
112 :
113 26092 : Real64 ScheduleBase::getMaxVal(EnergyPlusData &state)
114 : {
115 26092 : if (!isMinMaxSet) {
116 414 : setMinMaxVals(state);
117 : }
118 26092 : return maxVal;
119 : }
120 :
121 : // Day types are 1-based for EMS and output and other uses, so add a dummy
122 : constexpr std::array<std::string_view, (int)DayType::Num> dayTypeNames = {"Unused",
123 : "Sunday",
124 : "Monday",
125 : "Tuesday",
126 : "Wednesday",
127 : "Thursday",
128 : "Friday",
129 : "Saturday",
130 : "Holiday",
131 : "SummerDesignDay",
132 : "WinterDesignDay",
133 : "CustomDay1",
134 : "CustomDay2"};
135 :
136 : constexpr std::array<std::string_view, (int)DayType::Num> dayTypeNamesUC = {"UNUSED",
137 : "SUNDAY",
138 : "MONDAY",
139 : "TUESDAY",
140 : "WEDNESDAY",
141 : "THURSDAY",
142 : "FRIDAY",
143 : "SATURDAY",
144 : "HOLIDAY",
145 : "SUMMERDESIGNDAY",
146 : "WINTERDESIGNDAY",
147 : "CUSTOMDAY1",
148 : "CUSTOMDAY2"};
149 :
150 : static constexpr std::array<std::string_view, (int)LimitUnits::Num> limitUnitNamesUC = {"DIMENSIONLESS",
151 : "TEMPERATURE",
152 : "DELTATEMPERATURE",
153 : "PRECIPITATIONRATE",
154 : "ANGLE",
155 : "CONVECTIONCOEFFICIENT",
156 : "ACTIVITYLEVEL",
157 : "VELOCITY",
158 : "CAPACITY",
159 : "POWER",
160 : "AVAILABILITY",
161 : "PERCENT",
162 : "CONTROL",
163 : "MODE"};
164 :
165 : constexpr std::array<std::string_view, (int)ReportLevel::Num> reportLevelNames = {"Hourly", "Timestep"};
166 : constexpr std::array<std::string_view, (int)ReportLevel::Num> reportLevelNamesUC = {"HOURLY", "TIMESTEP"};
167 : constexpr std::array<std::string_view, (int)Interpolation::Num> interpolationNames = {"No", "Average", "Linear"};
168 : constexpr std::array<std::string_view, (int)Interpolation::Num> interpolationNamesUC = {"NO", "AVERAGE", "LINEAR"};
169 :
170 532 : bool DaySchedule::checkValsForLimitViolations(EnergyPlusData &state) const
171 : {
172 532 : auto &s_sched = state.dataSched;
173 :
174 532 : if (this->schedTypeNum == SchedNum_Invalid) {
175 0 : return false;
176 : }
177 532 : auto *schedType = s_sched->scheduleTypes[this->schedTypeNum];
178 532 : if (!schedType->isLimited) {
179 58 : return false;
180 : }
181 :
182 45642 : for (int i = 0; i < Constant::iHoursInDay * state.dataGlobal->TimeStepsInHour; ++i) {
183 45168 : if (this->tsVals[i] < schedType->minVal || this->tsVals[i] > schedType->maxVal) {
184 0 : return true;
185 : }
186 : }
187 :
188 474 : return false;
189 : } // ScheduleDay::checkValsForLimitViolations()
190 :
191 532 : bool DaySchedule::checkValsForBadIntegers(EnergyPlusData &state) const
192 : {
193 532 : auto &s_sched = state.dataSched;
194 532 : if (this->schedTypeNum == SchedNum_Invalid) {
195 0 : return false;
196 : }
197 532 : auto *schedType = s_sched->scheduleTypes[this->schedTypeNum];
198 532 : if (schedType->isReal) {
199 505 : return false;
200 : }
201 : // Make sure each is integer
202 3195 : for (int i = 0; i < Constant::iHoursInDay * state.dataGlobal->TimeStepsInHour; ++i) {
203 3168 : if (this->tsVals[i] != int(this->tsVals[i])) {
204 0 : return true;
205 : }
206 : }
207 27 : return false;
208 : } // ScheduleDay::checkValsForBadIntegers()
209 :
210 79758 : void DaySchedule::populateFromMinuteVals(EnergyPlusData &state, std::array<Real64, Constant::iMinutesInDay> const &minuteVals)
211 : {
212 79758 : auto &s_glob = state.dataGlobal;
213 79758 : if (this->interpolation == Interpolation::Average) {
214 6200 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
215 5952 : int begMin = 0;
216 5952 : int endMin = s_glob->MinutesInTimeStep - 1;
217 37200 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
218 31248 : Real64 accum = 0.0;
219 388368 : for (int iMin = begMin; iMin <= endMin; ++iMin) {
220 357120 : accum += minuteVals[hr * Constant::iMinutesInHour + iMin];
221 : }
222 31248 : this->tsVals[hr * s_glob->TimeStepsInHour + ts] = accum / double(s_glob->MinutesInTimeStep);
223 31248 : this->sumTsVals += this->tsVals[hr * s_glob->TimeStepsInHour + ts];
224 31248 : begMin = endMin + 1;
225 31248 : endMin += s_glob->MinutesInTimeStep;
226 : }
227 : }
228 : } else {
229 1987750 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
230 1908240 : int endMinute = s_glob->MinutesInTimeStep - 1;
231 11274816 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
232 9366576 : this->tsVals[hr * s_glob->TimeStepsInHour + ts] = minuteVals[hr * Constant::iMinutesInHour + endMinute];
233 9366576 : this->sumTsVals += this->tsVals[hr * s_glob->TimeStepsInHour + ts];
234 9366576 : endMinute += s_glob->MinutesInTimeStep;
235 : }
236 : }
237 : }
238 79758 : } // ScheduleDay::populateFromHrMinVals()
239 :
240 1871 : ScheduleConstant *AddScheduleConstant(EnergyPlusData &state, std::string const &name, Real64 value)
241 : {
242 1871 : auto const &s_sched = state.dataSched;
243 1871 : auto const &s_glob = state.dataGlobal;
244 :
245 1871 : auto *sched = new ScheduleConstant;
246 1871 : sched->Name = name;
247 1871 : sched->type = SchedType::Constant;
248 1871 : sched->Num = (int)s_sched->schedules.size();
249 1871 : sched->currentVal = value;
250 : // When InitConstantScheduleData is called, TimeStepsInHour is 0, so we ensure 24
251 1871 : sched->tsVals.assign(Constant::iHoursInDay * max(1, s_glob->TimeStepsInHour), value);
252 :
253 1871 : s_sched->schedules.push_back(sched);
254 1871 : s_sched->scheduleMap.insert_or_assign(std::move(Util::makeUPPER(sched->Name)), sched->Num);
255 :
256 1871 : return sched;
257 : } // AddScheduleConstant()
258 :
259 17025 : ScheduleDetailed *AddScheduleDetailed(EnergyPlusData &state, std::string const &name)
260 : {
261 17025 : auto const &s_sched = state.dataSched;
262 :
263 17025 : auto *sched = new ScheduleDetailed;
264 17025 : sched->Name = name;
265 :
266 17025 : sched->Num = (int)s_sched->schedules.size();
267 17025 : s_sched->schedules.push_back(sched);
268 17025 : s_sched->scheduleMap.insert_or_assign(std::move(Util::makeUPPER(sched->Name)), sched->Num);
269 :
270 17025 : sched->type = SchedType::Year;
271 17025 : return sched;
272 : } // AddScheduleDetailed()
273 :
274 125193 : DaySchedule *AddDaySchedule(EnergyPlusData &state, std::string const &name)
275 : {
276 125193 : auto &s_glob = state.dataGlobal;
277 125193 : auto &s_sched = state.dataSched;
278 :
279 125193 : auto *daySched = new DaySchedule;
280 125193 : daySched->Name = name;
281 :
282 125193 : daySched->Num = (int)s_sched->daySchedules.size();
283 125193 : s_sched->daySchedules.push_back(daySched);
284 125193 : s_sched->dayScheduleMap.insert_or_assign(std::move(Util::makeUPPER(daySched->Name)), daySched->Num);
285 :
286 125193 : daySched->tsVals.resize(Constant::iHoursInDay * s_glob->TimeStepsInHour);
287 :
288 125193 : return daySched;
289 : } // AddDaySchedule()
290 :
291 113346 : WeekSchedule *AddWeekSchedule(EnergyPlusData &state, std::string const &name)
292 : {
293 113346 : auto const &s_sched = state.dataSched;
294 :
295 113346 : auto *weekSched = new WeekSchedule;
296 113346 : weekSched->Name = name;
297 :
298 113346 : weekSched->Num = (int)s_sched->weekSchedules.size();
299 113346 : s_sched->weekSchedules.push_back(weekSched);
300 113346 : s_sched->weekScheduleMap.insert_or_assign(std::move(Util::makeUPPER(weekSched->Name)), weekSched->Num);
301 :
302 113346 : return weekSched;
303 : } // AddWeekSchedule()
304 :
305 803 : void InitConstantScheduleData(EnergyPlusData &state)
306 : {
307 : // Create ScheduleAlwaysOn and ScheduleAlwaysOff
308 : // Create constant schedules
309 803 : auto *schedOff = AddScheduleConstant(state, "Constant-0.0", 0.0);
310 803 : assert(schedOff->Num == SchedNum_AlwaysOff);
311 803 : schedOff->isUsed = true; // Suppress unused warnings
312 :
313 803 : auto *schedOn = AddScheduleConstant(state, "Constant-1.0", 1.0);
314 803 : assert(schedOn->Num == SchedNum_AlwaysOn);
315 803 : schedOn->isUsed = true; // Suppress unused warnings
316 803 : }
317 :
318 803 : void ProcessScheduleInput(EnergyPlusData &state)
319 : {
320 : // SUBROUTINE INFORMATION:
321 : // AUTHOR Linda K. Lawrie
322 : // DATE WRITTEN September 1997
323 : // MODIFIED Rui Zhang February 2010
324 :
325 : // PURPOSE OF THIS SUBROUTINE:
326 : // This subroutine processes the schedules input for EnergyPlus.
327 :
328 : // METHODOLOGY EMPLOYED:
329 : // Uses the standard get routines in the InputProcessor.
330 :
331 : // Using/Aliasing
332 : using DataStringGlobals::CharComma;
333 : using DataStringGlobals::CharSemicolon;
334 : using DataStringGlobals::CharSpace;
335 : using DataStringGlobals::CharTab;
336 : using DataSystemVariables::CheckForActualFilePath;
337 : using General::ProcessDateString;
338 :
339 : // Locals
340 : // SUBROUTINE PARAMETER DEFINITIONS:
341 803 : constexpr std::string_view routineName = "ProcessScheduleInput";
342 :
343 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
344 :
345 803 : Array1D_string Alphas;
346 803 : Array1D_string cAlphaFields;
347 803 : Array1D_string cNumericFields;
348 803 : Array1D<Real64> Numbers;
349 803 : Array1D_bool lAlphaBlanks;
350 803 : Array1D_bool lNumericBlanks;
351 : int NumAlphas;
352 : int NumNumbers;
353 : int Status;
354 :
355 : int EndMonth;
356 : int EndDay;
357 : int StartPointer;
358 : int EndPointer;
359 : int NumPointer;
360 803 : bool ErrorsFound(false);
361 : bool NumErrorFlag;
362 :
363 803 : std::string CFld; // Character field for error message
364 : // CHARACTER(len=20) CFld1 ! Character field for error message
365 :
366 : std::array<Real64, Constant::iMinutesInDay> minuteVals;
367 : std::array<bool, Constant::iMinutesInDay> setMinuteVals;
368 :
369 : int NumFields;
370 : // LOGICAL RptSchedule
371 :
372 : int MinutesPerItem;
373 : int NumExpectedItems;
374 : std::array<bool, (int)DayType::Num> allDays;
375 : std::array<bool, (int)DayType::Num> theseDays;
376 : bool ErrorHere;
377 : int SchNum;
378 : int WkCount;
379 : int DyCount;
380 : int NumField;
381 : int Count;
382 : Weather::DateType PDateType;
383 : int PWeekDay;
384 : int ThruField;
385 : int UntilFld;
386 : int xxcount;
387 : // REAL(r64) tempval
388 803 : std::string CurrentThrough;
389 803 : std::string LastFor;
390 803 : std::string errmsg;
391 : // for SCHEDULE:FILE
392 : int rowCnt;
393 :
394 803 : std::string subString;
395 : int MaxNums1;
396 : char ColumnSep;
397 : bool FileIntervalInterpolated;
398 : int rowLimitCount;
399 : int skiprowCount;
400 : int curcolCount;
401 803 : int numerrors = 0;
402 :
403 803 : auto const &s_glob = state.dataGlobal;
404 803 : auto const &s_ip = state.dataInputProcessing->inputProcessor;
405 803 : auto const &s_sched = state.dataSched;
406 :
407 803 : if (s_sched->ScheduleInputProcessed) {
408 0 : return;
409 : }
410 :
411 803 : s_sched->ScheduleInputProcessed = true;
412 :
413 803 : int MaxNums = 1; // Need at least 1 number because it's used as a local variable in the Schedule Types loop
414 803 : int MaxAlps = 0;
415 :
416 803 : std::string CurrentModuleObject = "ScheduleTypeLimits";
417 803 : int NumScheduleTypes = s_ip->getNumObjectsFound(state, CurrentModuleObject);
418 803 : if (NumScheduleTypes > 0) {
419 801 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
420 801 : MaxNums = max(MaxNums, NumNumbers);
421 801 : MaxAlps = max(MaxAlps, NumAlphas);
422 : }
423 803 : CurrentModuleObject = "Schedule:Day:Hourly";
424 803 : int NumHrDaySchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
425 803 : if (NumHrDaySchedules > 0) {
426 17 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
427 17 : MaxNums = max(MaxNums, NumNumbers);
428 17 : MaxAlps = max(MaxAlps, NumAlphas);
429 : }
430 803 : CurrentModuleObject = "Schedule:Day:Interval";
431 803 : int NumIntDaySchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
432 803 : if (NumIntDaySchedules > 0) {
433 9 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
434 9 : MaxNums = max(MaxNums, NumNumbers);
435 9 : MaxAlps = max(MaxAlps, NumAlphas);
436 : }
437 803 : CurrentModuleObject = "Schedule:Day:List";
438 803 : int NumLstDaySchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
439 803 : if (NumLstDaySchedules > 0) {
440 1 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
441 1 : MaxNums = max(MaxNums, NumNumbers);
442 1 : MaxAlps = max(MaxAlps, NumAlphas);
443 : }
444 803 : CurrentModuleObject = "Schedule:Week:Daily";
445 803 : int NumRegWeekSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
446 803 : if (NumRegWeekSchedules > 0) {
447 19 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
448 19 : MaxNums = max(MaxNums, NumNumbers);
449 19 : MaxAlps = max(MaxAlps, NumAlphas);
450 : }
451 803 : CurrentModuleObject = "Schedule:Week:Compact";
452 803 : int NumCptWeekSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
453 803 : if (NumCptWeekSchedules > 0) {
454 4 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
455 4 : MaxNums = max(MaxNums, NumNumbers);
456 4 : MaxAlps = max(MaxAlps, NumAlphas);
457 : }
458 803 : CurrentModuleObject = "Schedule:Year";
459 803 : int NumRegSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
460 803 : if (NumRegSchedules > 0) {
461 23 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
462 23 : MaxNums = max(MaxNums, NumNumbers);
463 23 : MaxAlps = max(MaxAlps, NumAlphas);
464 : }
465 803 : CurrentModuleObject = "Schedule:Compact";
466 803 : int NumCptSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
467 803 : if (NumCptSchedules > 0) {
468 773 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
469 773 : MaxNums = max(MaxNums, NumNumbers);
470 773 : MaxAlps = max(MaxAlps, NumAlphas + 1);
471 : }
472 803 : CurrentModuleObject = "Schedule:File";
473 803 : int NumCommaFileSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
474 803 : if (NumCommaFileSchedules > 0) {
475 8 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
476 8 : MaxNums = max(MaxNums, NumNumbers);
477 8 : MaxAlps = max(MaxAlps, NumAlphas);
478 : }
479 :
480 803 : CurrentModuleObject = "Schedule:Constant";
481 803 : int NumConstantSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
482 803 : if (NumConstantSchedules > 0) {
483 95 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
484 95 : MaxNums = max(MaxNums, NumNumbers);
485 95 : MaxAlps = max(MaxAlps, NumAlphas);
486 : }
487 803 : CurrentModuleObject = "ExternalInterface:Schedule";
488 803 : int NumExternalInterfaceSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
489 : // added for FMI
490 803 : if (NumExternalInterfaceSchedules > 0) {
491 0 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
492 0 : MaxNums = max(MaxNums, NumNumbers);
493 0 : MaxAlps = max(MaxAlps, NumAlphas + 1);
494 : }
495 : // added for FMU Import
496 803 : CurrentModuleObject = "ExternalInterface:FunctionalMockupUnitImport:To:Schedule";
497 803 : int NumExternalInterfaceFunctionalMockupUnitImportSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
498 803 : if (NumExternalInterfaceFunctionalMockupUnitImportSchedules > 0) {
499 1 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
500 1 : MaxNums = max(MaxNums, NumNumbers);
501 1 : MaxAlps = max(MaxAlps, NumAlphas + 1);
502 : }
503 : // added for FMU Export
504 803 : CurrentModuleObject = "ExternalInterface:FunctionalMockupUnitExport:To:Schedule";
505 803 : int NumExternalInterfaceFunctionalMockupUnitExportSchedules = s_ip->getNumObjectsFound(state, CurrentModuleObject);
506 803 : if (NumExternalInterfaceFunctionalMockupUnitExportSchedules > 0) {
507 0 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
508 0 : MaxNums = max(MaxNums, NumNumbers);
509 0 : MaxAlps = max(MaxAlps, NumAlphas + 1);
510 : }
511 803 : CurrentModuleObject = "Output:Schedules";
512 803 : s_ip->getObjectDefMaxArgs(state, CurrentModuleObject, Count, NumAlphas, NumNumbers);
513 803 : MaxNums = max(MaxNums, NumNumbers);
514 803 : MaxAlps = max(MaxAlps, NumAlphas);
515 :
516 803 : Alphas.allocate(MaxAlps); // Maximum Alphas possible
517 803 : cAlphaFields.allocate(MaxAlps);
518 803 : cNumericFields.allocate(MaxNums);
519 803 : Numbers.dimension(MaxNums, 0.0); // Maximum Numbers possible
520 803 : lAlphaBlanks.dimension(MaxAlps, true);
521 803 : lNumericBlanks.dimension(MaxNums, true);
522 :
523 : // Prescan to determine extra day and week schedules due to compact schedule input
524 803 : CurrentModuleObject = "Schedule:Compact";
525 803 : MaxNums1 = 0;
526 :
527 17403 : for (int LoopIndex = 1; LoopIndex <= NumCptSchedules; ++LoopIndex) {
528 16600 : s_ip->getObjectItem(state, CurrentModuleObject, LoopIndex, Alphas, NumAlphas, Numbers, NumNumbers, Status);
529 : // # 'THROUGH" => Number of additional week schedules
530 : // # 'FOR' => Number of additional day schedules
531 235266 : for (Count = 3; Count <= NumAlphas; ++Count) {
532 218666 : if (has_prefix(Alphas(Count), "UNTIL")) {
533 80156 : ++MaxNums1;
534 : }
535 : }
536 : }
537 803 : if (MaxNums1 > MaxNums) {
538 752 : MaxNums = MaxNums1;
539 752 : cNumericFields.deallocate();
540 752 : Numbers.deallocate();
541 752 : lNumericBlanks.deallocate();
542 752 : cNumericFields.allocate(MaxNums);
543 752 : Numbers.dimension(MaxNums, 0.0); // Maximum Numbers possible
544 752 : lNumericBlanks.dimension(MaxNums, true);
545 : }
546 :
547 : // add week and day schedules for each FILE:COMMA schedule
548 :
549 803 : CurrentModuleObject = "Schedule:File:Shading";
550 803 : int NumCommaFileShading = s_ip->getNumObjectsFound(state, CurrentModuleObject);
551 803 : NumAlphas = 0;
552 803 : NumNumbers = 0;
553 803 : if (NumCommaFileShading > 1) {
554 0 : ShowWarningError(state, format("{}: More than 1 occurrence of this object found, only first will be used.", CurrentModuleObject));
555 : }
556 :
557 803 : std::map<fs::path, nlohmann::json>::iterator schedule_file_shading_result;
558 803 : if (NumCommaFileShading != 0) {
559 1 : s_ip->getObjectItem(state,
560 : CurrentModuleObject,
561 : 1,
562 : Alphas,
563 : NumAlphas,
564 : Numbers,
565 : NumNumbers,
566 : Status,
567 : lNumericBlanks,
568 : lAlphaBlanks,
569 : cAlphaFields,
570 : cNumericFields);
571 1 : std::string ShadingSunlitFracFileName = Alphas(1);
572 :
573 1 : std::string contextString = CurrentModuleObject + ", " + cAlphaFields(1) + ": ";
574 1 : state.files.TempFullFilePath.filePath = CheckForActualFilePath(state, ShadingSunlitFracFileName, contextString);
575 :
576 1 : if (state.files.TempFullFilePath.filePath.empty()) {
577 0 : ShowFatalError(state, "Program terminates due to previous condition.");
578 : }
579 :
580 1 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
581 0 : rowLimitCount = 366 * Constant::iHoursInDay * s_glob->TimeStepsInHour;
582 : } else {
583 1 : rowLimitCount = 365 * Constant::iHoursInDay * s_glob->TimeStepsInHour;
584 : }
585 1 : ColumnSep = CharComma;
586 :
587 1 : schedule_file_shading_result = s_sched->UniqueProcessedExternalFiles.find(state.files.TempFullFilePath.filePath);
588 1 : if (schedule_file_shading_result == s_sched->UniqueProcessedExternalFiles.end()) {
589 :
590 1 : FileSystem::FileTypes const ext = FileSystem::getFileType(state.files.TempFullFilePath.filePath);
591 1 : if (FileSystem::is_flat_file_type(ext)) {
592 1 : auto const schedule_data = FileSystem::readFile(state.files.TempFullFilePath.filePath);
593 1 : CsvParser csvParser;
594 1 : skiprowCount = 1; // make sure to parse header row only for Schedule:File:Shading
595 1 : auto it = s_sched->UniqueProcessedExternalFiles.emplace(state.files.TempFullFilePath.filePath,
596 1 : csvParser.decode(schedule_data, ColumnSep, skiprowCount));
597 1 : if (csvParser.hasErrors()) {
598 0 : for (const auto &[error, isContinued] : csvParser.errors()) {
599 0 : if (isContinued) {
600 0 : ShowContinueError(state, error);
601 : } else {
602 0 : ShowSevereError(state, error);
603 : }
604 0 : }
605 0 : ShowContinueError(state, fmt::format("Error Occurred in {}", state.files.TempFullFilePath.filePath));
606 0 : ShowFatalError(state, "Program terminates due to previous condition.");
607 : }
608 1 : schedule_file_shading_result = it.first;
609 1 : } else if (FileSystem::is_all_json_type(ext)) {
610 0 : auto schedule_data = FileSystem::readJSON(state.files.TempFullFilePath.filePath);
611 : auto it = // (AUTO_OK_ITER)
612 0 : s_sched->UniqueProcessedExternalFiles.emplace(state.files.TempFullFilePath.filePath, std::move(schedule_data));
613 0 : schedule_file_shading_result = it.first;
614 0 : } else {
615 0 : ShowSevereError(state,
616 0 : fmt::format(R"({}: {}="{}", {}="{}" has an unknown file extension and cannot be read by this program.)",
617 : routineName,
618 : CurrentModuleObject,
619 : Alphas(1),
620 : cAlphaFields(3),
621 : Alphas(3)));
622 0 : ShowFatalError(state, "Program terminates due to previous condition.");
623 : }
624 : }
625 :
626 1 : auto const &column_json = schedule_file_shading_result->second["values"].at(0); // assume there is at least 1 column
627 1 : rowCnt = column_json.size();
628 : int NumCSVAllColumnsSchedules =
629 1 : schedule_file_shading_result->second["header"].get<std::set<std::string>>().size() - 1; // -1 to account for timestamp column
630 :
631 1 : if (schedule_file_shading_result->second["header"].back().get<std::string>() == "()") {
632 0 : ShowWarningError(state,
633 0 : format("{}: {}=\"{}\" Removing last column of the CSV since it has '()' for the surface name.",
634 : routineName,
635 : CurrentModuleObject,
636 : Alphas(1)));
637 0 : ShowContinueError(state, "This was a problem in E+ 22.2.0 and below, consider removing it from the file to suppress this warning.");
638 0 : schedule_file_shading_result->second["header"].erase(NumCSVAllColumnsSchedules);
639 0 : assert(schedule_file_shading_result->second["header"].size() == schedule_file_shading_result->second["values"].size());
640 0 : --NumCSVAllColumnsSchedules;
641 : }
642 :
643 1 : if (rowCnt != rowLimitCount) {
644 0 : if (rowCnt < rowLimitCount) {
645 0 : ShowSevereError(state, format("{}: {}=\"{}\" {} data values read.", routineName, CurrentModuleObject, Alphas(1), rowCnt));
646 0 : } else if (rowCnt > rowLimitCount) {
647 0 : ShowSevereError(state, format("{}: {}=\"{}\" too many data values read.", routineName, CurrentModuleObject, Alphas(1)));
648 : }
649 0 : ShowContinueError(
650 : state,
651 0 : format("Number of rows in the shading file must be a full year multiplied by the simulation TimeStep: {}.", rowLimitCount));
652 0 : ShowFatalError(state, "Program terminates due to previous condition.");
653 : }
654 :
655 : // schedule values have been filled into the CSVAllColumnNameAndValues map.
656 1 : s_sched->ScheduleFileShadingProcessed = true;
657 :
658 1 : if (numerrors > 0) {
659 0 : ShowWarningError(
660 : state,
661 0 : format(
662 : "{}:{}=\"{}\" {} records had errors - these values are set to 0.", routineName, CurrentModuleObject, Alphas(1), numerrors));
663 : }
664 1 : }
665 :
666 : //! Most initializations in the schedule data structures are taken care of in
667 : //! the definitions (see above)
668 :
669 1606 : print(state.files.audit.ensure_open(state, "ProcessScheduleInput", state.files.outputControl.audit),
670 : "{}\n",
671 : " Processing Schedule Input -- Start");
672 :
673 : //!! Get Schedule Types
674 :
675 803 : CurrentModuleObject = "ScheduleTypeLimits";
676 5092 : for (int Loop = 1; Loop <= NumScheduleTypes; ++Loop) {
677 4289 : s_ip->getObjectItem(state,
678 : CurrentModuleObject,
679 : Loop,
680 : Alphas,
681 : NumAlphas,
682 : Numbers,
683 : NumNumbers,
684 : Status,
685 : lNumericBlanks,
686 : lAlphaBlanks,
687 : cAlphaFields,
688 : cNumericFields);
689 :
690 4289 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
691 :
692 4289 : if (s_sched->scheduleTypeMap.find(Alphas(1)) != s_sched->scheduleTypeMap.end()) {
693 0 : ShowSevereDuplicateName(state, eoh);
694 0 : ErrorsFound = true;
695 0 : continue;
696 : }
697 :
698 4289 : auto *schedType = new ScheduleType;
699 4289 : schedType->Name = Alphas(1);
700 :
701 4289 : schedType->Num = (int)s_sched->scheduleTypes.size();
702 4289 : s_sched->scheduleTypes.push_back(schedType);
703 4289 : s_sched->scheduleTypeMap.insert_or_assign(schedType->Name, schedType->Num);
704 :
705 4289 : schedType->isLimited = !lNumericBlanks(1) && !lNumericBlanks(2);
706 :
707 4289 : if (!lNumericBlanks(1)) {
708 3301 : schedType->minVal = Numbers(1);
709 : }
710 4289 : if (!lNumericBlanks(2)) {
711 3292 : schedType->maxVal = Numbers(2);
712 : }
713 :
714 4289 : if (schedType->isLimited) {
715 3292 : if (Alphas(2) == "DISCRETE" || Alphas(2) == "INTEGER") {
716 1440 : schedType->isReal = false;
717 1852 : } else if (Alphas(2) == "CONTINUOUS" || Alphas(2) == "REAL") {
718 1852 : schedType->isReal = true;
719 : } else {
720 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(2), Alphas(2));
721 0 : ErrorsFound = true;
722 : }
723 : }
724 :
725 4289 : if (NumAlphas >= 3 && !lAlphaBlanks(3)) {
726 593 : schedType->limitUnits = static_cast<LimitUnits>(getEnumValue(limitUnitNamesUC, Alphas(3)));
727 593 : if (schedType->limitUnits == LimitUnits::Invalid) {
728 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(3), Alphas(3));
729 0 : ErrorsFound = true;
730 : }
731 : }
732 :
733 4289 : if (schedType->isLimited && schedType->minVal > schedType->maxVal) {
734 0 : if (schedType->isReal) {
735 0 : ShowSevereCustom(
736 0 : state, eoh, format("{} [{:.2R}] > {} [{:.2R}].", cNumericFields(1), schedType->minVal, cNumericFields(2), schedType->maxVal));
737 : } else {
738 0 : ShowSevereCustom(
739 0 : state, eoh, format("{} [{:.0R}] > {} [{:.0R}].", cNumericFields(1), schedType->minVal, cNumericFields(2), schedType->maxVal));
740 : }
741 0 : ShowContinueError(state, " Other warning/severes about schedule values may appear.");
742 : }
743 : } // for (Loop)
744 :
745 : //!! Get Day Schedules (all types)
746 :
747 : //!!=> Get "DAYSCHEDULE" (Hourly)
748 :
749 803 : Count = 0;
750 803 : CurrentModuleObject = "Schedule:Day:Hourly";
751 976 : for (int Loop = 1; Loop <= NumHrDaySchedules; ++Loop) {
752 173 : s_ip->getObjectItem(state,
753 : CurrentModuleObject,
754 : Loop,
755 : Alphas,
756 : NumAlphas,
757 : Numbers,
758 : NumNumbers,
759 : Status,
760 : lNumericBlanks,
761 : lAlphaBlanks,
762 : cAlphaFields,
763 : cNumericFields);
764 :
765 173 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
766 :
767 173 : if (s_sched->dayScheduleMap.find(Alphas(1)) != s_sched->dayScheduleMap.end()) {
768 0 : ShowSevereDuplicateName(state, eoh);
769 0 : ErrorsFound = true;
770 0 : continue;
771 : }
772 :
773 173 : auto *daySched = AddDaySchedule(state, Alphas(1));
774 :
775 : // Validate ScheduleType
776 173 : if (lAlphaBlanks(2)) {
777 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
778 0 : ShowContinueError(state, "Schedule will not be validated.");
779 173 : } else if ((daySched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
780 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
781 0 : ShowContinueError(state, "Schedule will not be validated.");
782 : }
783 :
784 173 : daySched->interpolation = Interpolation::No;
785 :
786 4325 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
787 28104 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
788 23952 : daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = Numbers(hr + 1);
789 23952 : daySched->sumTsVals += daySched->tsVals[hr * s_glob->TimeStepsInHour + ts];
790 : }
791 : }
792 :
793 173 : if (daySched->checkValsForLimitViolations(state)) {
794 0 : ShowWarningCustom(state, eoh, format("Values are outside of range for {}={}", cAlphaFields(2), Alphas(2)));
795 : }
796 :
797 173 : if (daySched->checkValsForBadIntegers(state)) {
798 0 : ShowWarningCustom(state, eoh, format("One or more values are not integer in {}={}", cAlphaFields(2), Alphas(2)));
799 : }
800 :
801 : } // for (Loop)
802 :
803 : //!! Get "DaySchedule:Interval"
804 :
805 803 : CurrentModuleObject = "Schedule:Day:Interval";
806 1156 : for (int Loop = 1; Loop <= NumIntDaySchedules; ++Loop) {
807 353 : s_ip->getObjectItem(state,
808 : CurrentModuleObject,
809 : Loop,
810 : Alphas,
811 : NumAlphas,
812 : Numbers,
813 : NumNumbers,
814 : Status,
815 : lNumericBlanks,
816 : lAlphaBlanks,
817 : cAlphaFields,
818 : cNumericFields);
819 :
820 353 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
821 :
822 353 : if (s_sched->dayScheduleMap.find(Alphas(1)) != s_sched->dayScheduleMap.end()) {
823 0 : ShowSevereDuplicateName(state, eoh);
824 0 : ErrorsFound = true;
825 0 : continue;
826 : }
827 :
828 353 : auto *daySched = AddDaySchedule(state, Alphas(1));
829 :
830 : // Validate ScheduleType
831 353 : if (lAlphaBlanks(2)) {
832 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
833 0 : ShowContinueError(state, "Schedule will not be validated.");
834 353 : } else if ((daySched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
835 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
836 0 : ShowContinueError(state, "Schedule will not be validated.");
837 : }
838 :
839 353 : NumFields = NumAlphas - 3;
840 : // check to see if numfield=0
841 353 : if (NumFields == 0) {
842 0 : ShowSevereCustom(state,
843 : eoh,
844 0 : format("Insufficient data entered for a full schedule day."
845 : "Number of interval fields == [{}].",
846 : NumFields));
847 0 : ErrorsFound = true;
848 : }
849 :
850 : // Depending on value of "Interpolate" field, the value for each time step in each hour gets processed:
851 353 : daySched->interpolation = static_cast<Interpolation>(getEnumValue(interpolationNamesUC, Alphas(3)));
852 353 : if (daySched->interpolation == Interpolation::Invalid) {
853 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(3), Alphas(3));
854 0 : ErrorsFound = true;
855 : }
856 :
857 353 : ProcessIntervalFields(state,
858 1412 : Alphas({4, _}),
859 : Numbers,
860 : NumFields,
861 : NumNumbers,
862 : minuteVals,
863 : setMinuteVals,
864 : ErrorsFound,
865 353 : Alphas(1),
866 : CurrentModuleObject,
867 : daySched->interpolation);
868 :
869 : // Now parcel into TS Value.... tsVals.resize() was called in AddDaySchedule()
870 353 : daySched->populateFromMinuteVals(state, minuteVals);
871 :
872 353 : if (daySched->checkValsForLimitViolations(state)) {
873 0 : ShowWarningCustom(state, eoh, format("Values are outside of range for {}={}", cAlphaFields(2), Alphas(2)));
874 : }
875 :
876 353 : if (daySched->checkValsForBadIntegers(state)) {
877 0 : ShowWarningCustom(state, eoh, format("One or more values are not integer in {}={}", cAlphaFields(2), Alphas(2)));
878 : }
879 : }
880 :
881 : //!! Get "DaySchedule:List"
882 :
883 803 : CurrentModuleObject = "Schedule:Day:List";
884 809 : for (int Loop = 1; Loop <= NumLstDaySchedules; ++Loop) {
885 6 : s_ip->getObjectItem(state,
886 : CurrentModuleObject,
887 : Loop,
888 : Alphas,
889 : NumAlphas,
890 : Numbers,
891 : NumNumbers,
892 : Status,
893 : lNumericBlanks,
894 : lAlphaBlanks,
895 : cAlphaFields,
896 : cNumericFields);
897 :
898 6 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
899 :
900 6 : if (s_sched->dayScheduleMap.find(Alphas(1)) != s_sched->dayScheduleMap.end()) {
901 0 : ShowSevereDuplicateName(state, eoh);
902 0 : ErrorsFound = true;
903 0 : continue;
904 : }
905 :
906 6 : auto *daySched = AddDaySchedule(state, Alphas(1));
907 :
908 : // Validate ScheduleType
909 6 : if (lAlphaBlanks(2)) {
910 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
911 0 : ShowContinueError(state, "Schedule will not be validated.");
912 6 : } else if ((daySched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
913 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
914 0 : ShowContinueError(state, "Schedule will not be validated.");
915 : }
916 :
917 : // Depending on value of "Interpolate" field, the value for each time step in each hour gets processed:
918 6 : daySched->interpolation = static_cast<Interpolation>(getEnumValue(interpolationNamesUC, Alphas(3)));
919 :
920 : // check to see if there are any fields
921 6 : if (Numbers(1) <= 0.0) {
922 0 : ShowSevereCustom(state,
923 : eoh,
924 0 : format("Insufficient data entered for a full schedule day."
925 : "...Minutes per Item field = [{}].",
926 : Numbers(1)));
927 0 : ErrorsFound = true;
928 0 : continue;
929 : }
930 6 : if (NumNumbers < 25) {
931 0 : ShowSevereCustom(state,
932 : eoh,
933 0 : format("Insufficient data entered for a full schedule day."
934 : "...Minutes per Item field = [{}] and only [{}] to apply to list fields.",
935 : Numbers(1),
936 0 : NumNumbers - 1));
937 0 : ErrorsFound = true;
938 0 : continue;
939 : }
940 :
941 6 : MinutesPerItem = int(Numbers(1));
942 6 : NumExpectedItems = 1440 / MinutesPerItem;
943 6 : if ((NumNumbers - 1) != NumExpectedItems) {
944 0 : ShowSevereCustom(state,
945 : eoh,
946 0 : format("Number of Entered Items={} not equal number of expected items={}"
947 : "based on {}={}",
948 0 : NumNumbers - 1,
949 : NumExpectedItems,
950 : cNumericFields(1),
951 : MinutesPerItem));
952 0 : ErrorsFound = true;
953 0 : continue;
954 : }
955 :
956 6 : if (mod(Constant::iMinutesInHour, MinutesPerItem) != 0) {
957 0 : ShowSevereCustom(state, eoh, format("{}={} not evenly divisible into 60", cNumericFields(1), MinutesPerItem));
958 0 : ErrorsFound = true;
959 0 : continue;
960 : }
961 :
962 : // Number of numbers in the Numbers list okay to process
963 6 : int hr = 0;
964 6 : int begMin = 0;
965 6 : int endMin = MinutesPerItem - 1;
966 438 : for (int NumFields = 2; NumFields <= NumNumbers; ++NumFields) {
967 9072 : for (int iMin = begMin; iMin <= endMin; ++iMin) {
968 8640 : minuteVals[hr * Constant::iMinutesInHour + iMin] = Numbers(NumFields);
969 : }
970 432 : begMin = endMin + 1;
971 432 : endMin += MinutesPerItem;
972 432 : if (endMin >= Constant::iMinutesInHour) {
973 144 : endMin = MinutesPerItem - 1;
974 144 : begMin = 0;
975 144 : ++hr;
976 : }
977 : }
978 :
979 : // Now parcel into TS Value.... tsVals.resize() was called in AddDaySchedule()
980 6 : daySched->populateFromMinuteVals(state, minuteVals);
981 :
982 6 : if (daySched->checkValsForLimitViolations(state)) {
983 0 : ShowWarningCustom(state, eoh, format("Values are outside of range for {}={}", cAlphaFields(2), Alphas(2)));
984 : }
985 :
986 6 : if (daySched->checkValsForBadIntegers(state)) {
987 0 : ShowWarningCustom(state, eoh, format("One or more values are not integer for {}={}", cAlphaFields(2), Alphas(2)));
988 : }
989 : }
990 :
991 : //!! Get Week Schedules - regular
992 :
993 803 : CurrentModuleObject = "Schedule:Week:Daily";
994 1150 : for (int Loop = 1; Loop <= NumRegWeekSchedules; ++Loop) {
995 347 : s_ip->getObjectItem(state,
996 : CurrentModuleObject,
997 : Loop,
998 : Alphas,
999 : NumAlphas,
1000 : Numbers,
1001 : NumNumbers,
1002 : Status,
1003 : lNumericBlanks,
1004 : lAlphaBlanks,
1005 : cAlphaFields,
1006 : cNumericFields);
1007 :
1008 347 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1009 :
1010 347 : if (s_sched->weekScheduleMap.find(Alphas(1)) != s_sched->weekScheduleMap.end()) {
1011 0 : ShowSevereDuplicateName(state, eoh);
1012 0 : ErrorsFound = true;
1013 0 : continue;
1014 : }
1015 :
1016 347 : auto *weekSched = AddWeekSchedule(state, Alphas(1));
1017 :
1018 : // Rest of Alphas are processed into schedule nums
1019 4511 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
1020 4164 : if ((weekSched->dayScheds[iDayType] = GetDaySchedule(state, Alphas(iDayType + 1))) == nullptr) {
1021 0 : ShowSevereItemNotFoundAudit(state, eoh, cAlphaFields(iDayType + 1), Alphas(iDayType + 1));
1022 0 : ErrorsFound = true;
1023 : }
1024 : } // for (iDayType)
1025 : }
1026 :
1027 : //!! Get Week Schedules - compact
1028 803 : Count = NumRegWeekSchedules;
1029 803 : CurrentModuleObject = "Schedule:Week:Compact";
1030 855 : for (int Loop = 1; Loop <= NumCptWeekSchedules; ++Loop) {
1031 52 : s_ip->getObjectItem(state,
1032 : CurrentModuleObject,
1033 : Loop,
1034 : Alphas,
1035 : NumAlphas,
1036 : Numbers,
1037 : NumNumbers,
1038 : Status,
1039 : lNumericBlanks,
1040 : lAlphaBlanks,
1041 : cAlphaFields,
1042 : cNumericFields);
1043 :
1044 52 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1045 :
1046 52 : if (s_sched->weekScheduleMap.find(Alphas(1)) != s_sched->weekScheduleMap.end()) {
1047 0 : ShowSevereDuplicateName(state, eoh);
1048 0 : ErrorsFound = true;
1049 0 : continue;
1050 : }
1051 :
1052 52 : auto *weekSched = AddWeekSchedule(state, Alphas(1));
1053 :
1054 52 : std::fill(allDays.begin(), allDays.end(), false);
1055 : // Rest of Alphas are processed into schedule indices
1056 140 : for (int idx = 2; idx <= NumAlphas; idx += 2) {
1057 88 : auto *daySched = GetDaySchedule(state, Alphas(idx + 1));
1058 88 : if (daySched == nullptr) {
1059 0 : ShowSevereItemNotFoundAudit(state, eoh, cAlphaFields(idx + 1), Alphas(idx + 1));
1060 0 : ShowContinueError(state, format("ref: {} \"{}\"", cAlphaFields(idx), Alphas(idx)));
1061 0 : ErrorsFound = true;
1062 : } else {
1063 88 : std::fill(theseDays.begin(), theseDays.end(), false);
1064 88 : ErrorHere = false;
1065 88 : ProcessForDayTypes(state, Alphas(idx), theseDays, allDays, ErrorHere);
1066 88 : if (ErrorHere) {
1067 0 : ShowContinueError(state, format("{}: {}=\"{}", routineName, CurrentModuleObject, Alphas(1)));
1068 0 : ErrorsFound = true;
1069 : } else {
1070 1144 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
1071 1056 : if (theseDays[iDayType]) {
1072 624 : weekSched->dayScheds[iDayType] = daySched;
1073 : }
1074 : }
1075 : }
1076 : }
1077 : }
1078 :
1079 : // Have processed all named days, check to make sure all given
1080 676 : for (int iDayType = iDayType_Sun; iDayType < (int)DayType::Num; ++iDayType) {
1081 624 : if (allDays[iDayType] == true) {
1082 624 : continue;
1083 : }
1084 0 : ShowSevereError(state, format("{}: {}=\"{}\", Missing some day assignments", routineName, CurrentModuleObject, Alphas(1)));
1085 0 : ErrorsFound = true;
1086 0 : break;
1087 : }
1088 : }
1089 803 : NumRegWeekSchedules = Count;
1090 :
1091 : //!! Get Schedules (all types)
1092 :
1093 : //!! Get Regular Schedules
1094 :
1095 803 : CurrentModuleObject = "Schedule:Year";
1096 980 : for (int Loop = 1; Loop <= NumRegSchedules; ++Loop) {
1097 177 : s_ip->getObjectItem(state,
1098 : CurrentModuleObject,
1099 : Loop,
1100 : Alphas,
1101 : NumAlphas,
1102 : Numbers,
1103 : NumNumbers,
1104 : Status,
1105 : lNumericBlanks,
1106 : lAlphaBlanks,
1107 : cAlphaFields,
1108 : cNumericFields);
1109 :
1110 177 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1111 :
1112 177 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
1113 0 : ShowSevereDuplicateName(state, eoh);
1114 0 : ErrorsFound = true;
1115 0 : continue;
1116 : }
1117 :
1118 177 : auto *sched = AddScheduleDetailed(state, Alphas(1));
1119 :
1120 : // Validate ScheduleType
1121 177 : if (lAlphaBlanks(2)) {
1122 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
1123 0 : ShowContinueError(state, "Schedule will not be validated.");
1124 177 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
1125 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
1126 0 : ShowContinueError(state, "Schedule will not be validated.");
1127 : }
1128 :
1129 177 : int NumPointer = 0;
1130 :
1131 : std::array<int, 367> daysInYear;
1132 177 : std::fill(daysInYear.begin(), daysInYear.end(), 0);
1133 :
1134 : // Rest of Alphas (Weekschedules) are processed into Pointers
1135 583 : for (int idx = 3; idx <= NumAlphas; ++idx) {
1136 406 : auto *weekSched = GetWeekSchedule(state, Alphas(idx));
1137 406 : if (weekSched == nullptr) {
1138 0 : ShowSevereItemNotFoundAudit(state, eoh, cAlphaFields(idx), Alphas(idx));
1139 0 : ErrorsFound = true;
1140 0 : continue;
1141 : }
1142 :
1143 : // Process for month, day
1144 406 : int StartMonth = int(Numbers(NumPointer + 1));
1145 406 : int StartDay = int(Numbers(NumPointer + 2));
1146 406 : int EndMonth = int(Numbers(NumPointer + 3));
1147 406 : int EndDay = int(Numbers(NumPointer + 4));
1148 406 : NumPointer += 4;
1149 406 : int StartPointer = General::OrdinalDay(StartMonth, StartDay, 1);
1150 406 : int EndPointer = General::OrdinalDay(EndMonth, EndDay, 1);
1151 406 : if (StartPointer <= EndPointer) {
1152 65188 : for (int Count = StartPointer; Count <= EndPointer; ++Count) {
1153 64782 : ++daysInYear[Count];
1154 64782 : sched->weekScheds[Count] = weekSched;
1155 : }
1156 : } else {
1157 0 : for (int Count = StartPointer; Count <= 366; ++Count) {
1158 0 : ++daysInYear[Count];
1159 0 : sched->weekScheds[Count] = weekSched;
1160 : }
1161 0 : for (int Count = 1; Count <= EndPointer; ++Count) {
1162 0 : ++daysInYear[Count];
1163 0 : sched->weekScheds[Count] = weekSched;
1164 : }
1165 : }
1166 : }
1167 :
1168 : // Perform Error checks on this item
1169 : // Do special test for Feb 29. Make equal to Feb 28.
1170 177 : if (daysInYear[60] == 0) {
1171 0 : daysInYear[60] = daysInYear[59];
1172 0 : sched->weekScheds[60] = sched->weekScheds[59];
1173 : }
1174 :
1175 64959 : for (int iDay = 1; iDay <= 366; ++iDay) {
1176 64782 : if (daysInYear[iDay] == 0) {
1177 0 : ShowSevereCustomAudit(state, eoh, "has missing days in its schedule pointers");
1178 0 : ErrorsFound = true;
1179 0 : break;
1180 64782 : } else if (daysInYear[iDay] > 1) {
1181 0 : ShowSevereCustomAudit(state, eoh, "has overlapping days in its schedule pointers");
1182 0 : ErrorsFound = true;
1183 0 : break;
1184 : }
1185 : }
1186 :
1187 : // What does it mean to actuate a schedule?
1188 177 : if (s_glob->AnyEnergyManagementSystemInModel) { // setup constant schedules as actuators
1189 70 : SetupEMSActuator(state, "Schedule:Year", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal);
1190 : }
1191 : }
1192 :
1193 : //!! Get Compact Schedules
1194 : // SCHEDULE:COMPACT,
1195 : // \memo Irregular object. Does not follow the usual definition for fields. Fields A3... are:
1196 : // \memo Through: Date
1197 : // \memo For: Applicable days (ref: Weekschedule:Compact)
1198 : // \memo Interpolate: Yes/No (ref: Dayschedule:interval) -- optional, if not used will be "No"
1199 : // \memo Until: <Time> (ref: Dayschedule:Interval)
1200 : // \memo <numeric value>
1201 : // \memo words "Through","For","Interpolate","Until" must be included.
1202 : // A1 , \field Name
1203 : // \required-field
1204 : // \type alpha
1205 : // \reference ScheduleNames
1206 : // A2 , \field ScheduleType
1207 : // \type object-list
1208 : // \object-list ScheduleTypeNames
1209 : // A3 , \field Complex Field #1
1210 : // A4 , \field Complex Field #2
1211 : // A5 , \field Complex Field #3
1212 :
1213 803 : SchNum = NumRegSchedules;
1214 803 : CurrentModuleObject = "Schedule:Compact";
1215 17403 : for (int Loop = 1; Loop <= NumCptSchedules; ++Loop) {
1216 16600 : s_ip->getObjectItem(state,
1217 : CurrentModuleObject,
1218 : Loop,
1219 : Alphas,
1220 : NumAlphas,
1221 : Numbers,
1222 : NumNumbers,
1223 : Status,
1224 : lNumericBlanks,
1225 : lAlphaBlanks,
1226 : cAlphaFields,
1227 : cNumericFields);
1228 :
1229 16600 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1230 :
1231 16600 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
1232 0 : ShowSevereDuplicateName(state, eoh);
1233 0 : ErrorsFound = true;
1234 0 : continue;
1235 : }
1236 :
1237 16600 : auto *sched = AddScheduleDetailed(state, Alphas(1));
1238 16600 : sched->type = SchedType::Compact;
1239 :
1240 : // Validate ScheduleType
1241 16600 : if (lAlphaBlanks(2)) {
1242 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
1243 0 : ShowContinueError(state, "Schedule will not be validated.");
1244 16600 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
1245 10 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
1246 30 : ShowContinueError(state, "Schedule will not be validated.");
1247 : }
1248 :
1249 16600 : NumPointer = 0;
1250 :
1251 : std::array<int, 367> daysInYear;
1252 16600 : std::fill(daysInYear.begin() + 1, daysInYear.end(), 0);
1253 : // Process the "complex" fields -- so named because they are not a 1:1 correspondence
1254 : // as other objects are
1255 16600 : NumField = 3;
1256 16600 : StartPointer = 1;
1257 16600 : WkCount = 0;
1258 16600 : DyCount = 0;
1259 16600 : bool FullYearSet = false;
1260 39755 : while (NumField < NumAlphas) {
1261 : // Process "Through"
1262 23155 : if (!has_prefix(Alphas(NumField), "THROUGH:") && !has_prefix(Alphas(NumField), "THROUGH")) {
1263 0 : ShowSevereCustom(state, eoh, format("Expecting \"Through:\" date, instead found entry={}", Alphas(NumField)));
1264 0 : ErrorsFound = true;
1265 0 : goto Through_exit;
1266 : }
1267 :
1268 23155 : int sPos = (Alphas(NumField)[7] == ':') ? 8 : 7;
1269 23155 : Alphas(NumField).erase(0, sPos);
1270 23155 : strip(Alphas(NumField));
1271 :
1272 23155 : CurrentThrough = Alphas(NumField);
1273 23155 : ErrorHere = false;
1274 23155 : ProcessDateString(state, Alphas(NumField), EndMonth, EndDay, PWeekDay, PDateType, ErrorHere);
1275 23155 : if (PDateType == Weather::DateType::NthDayInMonth || PDateType == Weather::DateType::LastDayInMonth) {
1276 0 : ShowSevereCustom(state, eoh, format("Invalid \"Through:\" date, found entry={}", Alphas(NumField)));
1277 0 : ErrorsFound = true;
1278 0 : goto Through_exit;
1279 : }
1280 :
1281 23155 : if (ErrorHere) {
1282 0 : ShowSevereCustom(state, eoh, "Invalid \"Through:\" date");
1283 0 : ErrorsFound = true;
1284 0 : goto Through_exit;
1285 : }
1286 :
1287 23155 : EndPointer = General::OrdinalDay(EndMonth, EndDay, 1);
1288 23155 : if (EndPointer == 366) {
1289 16600 : if (FullYearSet) {
1290 0 : ShowSevereCustom(
1291 0 : state, eoh, format("New \"Through\" entry when \"full year\" already set \"Through\" field={}", CurrentThrough));
1292 0 : ErrorsFound = true;
1293 : }
1294 16600 : FullYearSet = true;
1295 : }
1296 :
1297 23155 : ++WkCount;
1298 :
1299 23155 : auto *weekSched = AddWeekSchedule(state, format("{}_wk_{}", Alphas(1), WkCount));
1300 23155 : weekSched->isUsed = true;
1301 :
1302 6098755 : for (int iDay = StartPointer; iDay <= EndPointer; ++iDay) {
1303 6075600 : sched->weekScheds[iDay] = weekSched;
1304 6075600 : ++daysInYear[iDay];
1305 : }
1306 :
1307 23155 : StartPointer = EndPointer + 1;
1308 23155 : ThruField = NumField;
1309 23155 : std::fill(allDays.begin(), allDays.end(), false);
1310 23155 : ++NumField;
1311 :
1312 58024 : while (NumField < NumAlphas) { // Continues until next "Through"
1313 41424 : if (has_prefix(Alphas(NumField), "THROUGH")) {
1314 6555 : goto For_exit;
1315 : }
1316 : // "For" must be next, adds to "# Day Schedules"
1317 34869 : if (!has_prefix(Alphas(NumField), "FOR")) {
1318 0 : ShowSevereCustom(state, eoh, format("Looking for \"For\" field, found={}", Alphas(NumField)));
1319 0 : ErrorsFound = true;
1320 0 : goto Through_exit;
1321 : }
1322 :
1323 34869 : ++DyCount;
1324 :
1325 34869 : auto *daySched = AddDaySchedule(state, format("{}_dy_{}", Alphas(1), DyCount));
1326 :
1327 34869 : daySched->schedTypeNum = sched->schedTypeNum;
1328 34869 : daySched->isUsed = true;
1329 :
1330 34869 : std::fill(theseDays.begin(), theseDays.end(), false);
1331 34869 : ErrorHere = false;
1332 34869 : LastFor = Alphas(NumField);
1333 34869 : ProcessForDayTypes(state, Alphas(NumField), theseDays, allDays, ErrorHere);
1334 34869 : if (ErrorHere) {
1335 0 : ShowContinueError(state, format("ref {}=\"{}\"", CurrentModuleObject, Alphas(1)));
1336 0 : ShowContinueError(state, format("ref Through field={}", Alphas(ThruField)));
1337 0 : ErrorsFound = true;
1338 : } else {
1339 453297 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
1340 418428 : if (theseDays[iDayType]) {
1341 277860 : weekSched->dayScheds[iDayType] = daySched;
1342 : }
1343 : }
1344 : }
1345 :
1346 : // Check for "Interpolate"
1347 34869 : ++NumField;
1348 34869 : if (has_prefix(Alphas(NumField), "INTERPOLATE") || !has_prefix(Alphas(NumField), "UNTIL")) {
1349 330 : if (has(Alphas(NumField), "NO")) {
1350 88 : daySched->interpolation = Interpolation::No;
1351 242 : } else if (has(Alphas(NumField), "AVERAGE")) {
1352 242 : daySched->interpolation = Interpolation::Average;
1353 0 : } else if (has(Alphas(NumField), "LINEAR")) {
1354 0 : daySched->interpolation = Interpolation::Linear;
1355 : } else {
1356 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(NumField), Alphas(NumField));
1357 0 : ErrorsFound = true;
1358 : }
1359 330 : ++NumField;
1360 : }
1361 :
1362 34869 : NumNumbers = 0;
1363 34869 : xxcount = 0;
1364 34869 : UntilFld = NumField;
1365 : while (true) {
1366 98425 : if (has_prefix(Alphas(NumField), "FOR")) {
1367 11714 : break;
1368 : }
1369 86711 : if (has_prefix(Alphas(NumField), "THROUGH")) {
1370 6555 : break;
1371 : }
1372 80156 : if (has_prefix(Alphas(NumField), "UNTIL")) {
1373 : // Process Until/Value pairs for later processing by other routine.
1374 80156 : ++NumField;
1375 80156 : ++xxcount;
1376 80156 : ++NumNumbers;
1377 80156 : Numbers(NumNumbers) = Util::ProcessNumber(Alphas(NumField), ErrorHere);
1378 80156 : if (ErrorHere) {
1379 0 : ShowSevereCustom(
1380 0 : state, eoh, format("Until field=[{}] has illegal value field=[{}].", Alphas(NumField - 1), Alphas(NumField)));
1381 0 : ErrorsFound = true;
1382 : }
1383 80156 : ++NumField;
1384 80156 : Alphas(UntilFld + xxcount) = Alphas(NumField); // In case next is "until"
1385 : } else {
1386 0 : ShowSevereCustom(state, eoh, format("Looking for \"Until\" field, found={}", Alphas(NumField)));
1387 0 : ErrorsFound = true;
1388 0 : goto Through_exit;
1389 : }
1390 80156 : if (Alphas(NumField).empty()) {
1391 16600 : break;
1392 : }
1393 : }
1394 :
1395 : // Process Untils, Numbers
1396 34869 : if (NumNumbers > 0) {
1397 34869 : NumFields = NumNumbers;
1398 34869 : ErrorHere = false;
1399 34869 : ProcessIntervalFields(state,
1400 139476 : Alphas({UntilFld, _}),
1401 : Numbers,
1402 : NumFields,
1403 : NumNumbers,
1404 : minuteVals,
1405 : setMinuteVals,
1406 : ErrorHere,
1407 34869 : daySched->Name,
1408 69738 : CurrentModuleObject + " DaySchedule Fields",
1409 : daySched->interpolation);
1410 : // Depending on value of "Interpolate" field, the value for each time step in each hour gets processed:
1411 34869 : if (ErrorHere) {
1412 0 : ShowContinueError(state, format("ref {}=\"{}\"", CurrentModuleObject, Alphas(1)));
1413 0 : ErrorsFound = true;
1414 : }
1415 :
1416 34869 : daySched->populateFromMinuteVals(state, minuteVals);
1417 : }
1418 : }
1419 :
1420 16600 : For_exit:;
1421 301015 : for (int iDayType = iDayType_Sun; iDayType < (int)DayType::Num; ++iDayType) {
1422 277860 : if (allDays[iDayType] == true) {
1423 277860 : continue;
1424 : }
1425 :
1426 0 : ShowWarningCustom(state, eoh, format("has missing day types in Through={}", CurrentThrough));
1427 0 : ShowContinueError(state, format("Last \"For\" field={}", LastFor));
1428 0 : std::string errmsg = "Missing day types=,";
1429 0 : for (int kDayType = iDayType_Sun; kDayType < (int)DayType::Num; ++kDayType) {
1430 0 : if (allDays[kDayType]) {
1431 0 : continue;
1432 : }
1433 0 : errmsg.erase(errmsg.length() - 1);
1434 0 : errmsg = format("{} \"{}\",-", errmsg, dayTypeNames[kDayType]);
1435 : }
1436 0 : errmsg.erase(errmsg.length() - 2);
1437 0 : ShowContinueError(state, errmsg);
1438 0 : ShowContinueError(state, "Missing day types will have 0.0 as Schedule Values");
1439 0 : break;
1440 0 : }
1441 : }
1442 :
1443 16600 : Through_exit:;
1444 16600 : if (daysInYear[60] == 0) {
1445 0 : daysInYear[60] = daysInYear[59];
1446 0 : sched->weekScheds[60] = sched->weekScheds[59];
1447 : }
1448 :
1449 16600 : if (std::find(daysInYear.begin() + 1, daysInYear.end(), 0) != daysInYear.end()) {
1450 0 : ShowSevereCustomAudit(state, eoh, "has missing days in its schedule pointers");
1451 0 : ErrorsFound = true;
1452 : }
1453 6092200 : if (std::find_if(daysInYear.begin() + 1, daysInYear.end(), [](int i) { return i > 1; }) != daysInYear.end()) {
1454 0 : ShowSevereCustomAudit(state, eoh, "has overlapping days in its schedule pointers");
1455 0 : ErrorsFound = true;
1456 : }
1457 :
1458 16600 : if (s_glob->AnyEnergyManagementSystemInModel) { // setup constant schedules as actuators
1459 3860 : SetupEMSActuator(state, "Schedule:Compact", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal);
1460 : }
1461 : }
1462 :
1463 : // Schedule:File,
1464 : // \min-fields 5
1465 : // \memo A Schedule:File points to a text computer file that has 8760-8784 hours of data.
1466 : // A1 , \field Name
1467 : // \required-field
1468 : // \type alpha
1469 : // \reference ScheduleNames
1470 : // A2 , \field Schedule Type Limits Name
1471 : // \type object-list
1472 : // \object-list ScheduleTypeLimitsNames
1473 : // A3 , \field File Name
1474 : // \required-field
1475 : // \retaincase
1476 : // N1 , \field Column Number
1477 : // \required-field
1478 : // \type integer
1479 : // \minimum 1
1480 : // N2 , \field Rows to Skip at Top
1481 : // \required-field
1482 : // \type integer
1483 : // \minimum 0
1484 : // N3 , \field Number of Hours of Data
1485 : // \note 8760 hours does not account for leap years, 8784 does.
1486 : // \note should be either 8760 or 8784
1487 : // \default 8760
1488 : // \minimum 8760
1489 : // \maximum 8784
1490 : // A4 , \field Column Separator
1491 : // \type choice
1492 : // \key Comma
1493 : // \key Tab
1494 : // \key Space
1495 : // \key Semicolon
1496 : // \default Comma
1497 : // A5 , \field Interpolate to Timestep
1498 : // \note when the interval does not match the user specified timestep a "Yes" choice will average between the intervals request (to
1499 : // \note timestep resolution. a "No" choice will use the interval value at the simulation timestep without regard to if it matches
1500 : // \note the boundary or not.
1501 : // \type choice
1502 : // \key Yes
1503 : // \key No
1504 : // \default No
1505 : // N4 ; \field Minutes per Item
1506 : // \note Must be evenly divisible into 60
1507 : // \type integer
1508 : // \minimum 1
1509 : // \maximum 60
1510 :
1511 : // continue adding to SchNum,AddWeekSch,AddDaySch
1512 :
1513 803 : CurrentModuleObject = "Schedule:File";
1514 936 : for (int Loop = 1; Loop <= NumCommaFileSchedules; ++Loop) {
1515 133 : s_ip->getObjectItem(state,
1516 : CurrentModuleObject,
1517 : Loop,
1518 : Alphas,
1519 : NumAlphas,
1520 : Numbers,
1521 : NumNumbers,
1522 : Status,
1523 : lNumericBlanks,
1524 : lAlphaBlanks,
1525 : cAlphaFields,
1526 : cNumericFields);
1527 :
1528 133 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1529 :
1530 133 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
1531 0 : ShowSevereDuplicateName(state, eoh);
1532 0 : ErrorsFound = true;
1533 0 : continue;
1534 : }
1535 :
1536 133 : auto *sched = AddScheduleDetailed(state, Alphas(1));
1537 133 : sched->type = SchedType::File;
1538 :
1539 : // Validate ScheduleType
1540 133 : if (lAlphaBlanks(2)) {
1541 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
1542 0 : ShowContinueError(state, "Schedule will not be validated.");
1543 133 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
1544 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
1545 0 : ShowContinueError(state, "Schedule will not be validated.");
1546 : }
1547 :
1548 : // Numbers(1) - which column
1549 133 : curcolCount = Numbers(1);
1550 : // Numbers(2) - number of rows to skip
1551 133 : skiprowCount = Numbers(2);
1552 133 : if (Numbers(3) == 0) {
1553 0 : Numbers(3) = 8760.0;
1554 : }
1555 133 : if (Numbers(3) != 8760 && Numbers(3) != 8784) {
1556 0 : ShowSevereCustom(
1557 : state,
1558 : eoh,
1559 0 : format("{} must = 8760 or 8784 (for a leap year). Value = {:.0T}, Schedule not processed.", cNumericFields(3), Numbers(3)));
1560 0 : ErrorsFound = true;
1561 0 : continue;
1562 : }
1563 :
1564 133 : if (lAlphaBlanks(4) || Util::SameString(Alphas(4), "comma")) {
1565 133 : ColumnSep = CharComma;
1566 133 : Alphas(4) = "comma";
1567 0 : } else if (Util::SameString(Alphas(4), "semicolon")) {
1568 0 : ColumnSep = CharSemicolon;
1569 0 : } else if (Util::SameString(Alphas(4), "tab")) {
1570 0 : ColumnSep = CharTab;
1571 0 : } else if (Util::SameString(Alphas(4), "space")) {
1572 0 : ColumnSep = CharSpace;
1573 : } else {
1574 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(4), Alphas(4), "..must be Comma, Semicolon, Tab, or Space.");
1575 0 : ErrorsFound = true;
1576 0 : continue;
1577 : }
1578 :
1579 : // Depending on value of "Interpolate" field, the value for each time step in each hour gets processed:
1580 133 : Interpolation interp = Interpolation::No;
1581 :
1582 133 : if (!lAlphaBlanks(5)) {
1583 4 : if (BooleanSwitch bs = getYesNoValue(Alphas(5)); bs != BooleanSwitch::Invalid) {
1584 4 : interp = static_cast<bool>(bs) ? Interpolation::Average : Interpolation::Linear;
1585 : } else {
1586 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(5), Alphas(5));
1587 0 : ErrorsFound = true;
1588 : }
1589 : }
1590 :
1591 133 : sched->UseDaylightSaving = true;
1592 133 : if ((Alphas(6)) == "NO") {
1593 2 : sched->UseDaylightSaving = false;
1594 : }
1595 :
1596 : // is it a sub-hourly schedule or not?
1597 133 : int MinutesPerItem = Constant::iMinutesInHour;
1598 133 : if (NumNumbers > 3) {
1599 133 : MinutesPerItem = int(Numbers(4));
1600 : // int NumExpectedItems = 1440 / MinutesPerItem;
1601 133 : if (mod(Constant::iMinutesInHour, MinutesPerItem) != 0) {
1602 0 : ShowSevereCustom(
1603 0 : state, eoh, format("Requested {} field value ({}) not evenly divisible into 60", cNumericFields(4), MinutesPerItem));
1604 0 : ErrorsFound = true;
1605 0 : continue;
1606 : }
1607 : }
1608 :
1609 133 : int numHourlyValues = Numbers(3);
1610 133 : int rowLimitCount = (Numbers(3) * Constant::rMinutesInHour) / MinutesPerItem;
1611 133 : int hrLimitCount = Constant::iMinutesInHour / MinutesPerItem;
1612 :
1613 133 : std::string contextString = format("{}=\"{}\", {}: ", CurrentModuleObject, Alphas(1), cAlphaFields(3));
1614 :
1615 133 : state.files.TempFullFilePath.filePath = CheckForActualFilePath(state, Alphas(3), contextString);
1616 : // Setup file reading parameters
1617 133 : if (state.files.TempFullFilePath.filePath.empty()) {
1618 0 : ErrorsFound = true;
1619 : } else {
1620 133 : auto result = s_sched->UniqueProcessedExternalFiles.find(state.files.TempFullFilePath.filePath);
1621 133 : if (result == s_sched->UniqueProcessedExternalFiles.end()) {
1622 9 : FileSystem::FileTypes const ext = FileSystem::getFileType(state.files.TempFullFilePath.filePath);
1623 9 : if (FileSystem::is_flat_file_type(ext)) {
1624 9 : auto const schedule_data = FileSystem::readFile(state.files.TempFullFilePath.filePath);
1625 9 : CsvParser csvParser;
1626 9 : auto it = s_sched->UniqueProcessedExternalFiles.emplace(state.files.TempFullFilePath.filePath,
1627 9 : csvParser.decode(schedule_data, ColumnSep, skiprowCount));
1628 9 : if (csvParser.hasErrors()) {
1629 0 : for (const auto &[error, isContinued] : csvParser.errors()) {
1630 0 : if (isContinued) {
1631 0 : ShowContinueError(state, error);
1632 : } else {
1633 0 : ShowSevereError(state, error);
1634 : }
1635 0 : }
1636 0 : ShowContinueError(state, fmt::format("Error Occurred in {}", state.files.TempFullFilePath.filePath));
1637 0 : ShowFatalError(state, "Program terminates due to previous condition.");
1638 : }
1639 9 : result = it.first;
1640 9 : } else if (FileSystem::is_all_json_type(ext)) {
1641 0 : auto it = s_sched->UniqueProcessedExternalFiles.emplace(state.files.TempFullFilePath.filePath,
1642 0 : FileSystem::readJSON(state.files.TempFullFilePath.filePath));
1643 0 : result = it.first;
1644 0 : } else {
1645 0 : ShowSevereCustom(
1646 : state,
1647 : eoh,
1648 0 : format("{} = {} has an unknown file extension and cannot be read by this program.", cAlphaFields(3), Alphas(3)));
1649 0 : ShowFatalError(state, "Program terminates due to previous condition.");
1650 : }
1651 : }
1652 :
1653 133 : auto const &column_json = result->second["values"][curcolCount - 1];
1654 133 : rowCnt = column_json.size();
1655 133 : auto const column_values = column_json.get<std::vector<Real64>>(); // (AUTO_OK_OBJ)
1656 :
1657 : // schedule values have been filled into the hourlyFileValues array.
1658 :
1659 133 : if (numerrors > 0) {
1660 0 : ShowWarningCustom(state,
1661 : eoh,
1662 0 : format("{} records had errors - these values are set to 0."
1663 : "Use Output:Diagnostics,DisplayExtraWarnings; to see individual records in error.",
1664 : numerrors));
1665 : }
1666 :
1667 133 : if (rowCnt < rowLimitCount) {
1668 0 : ShowWarningCustom(state,
1669 : eoh,
1670 0 : format("less than {} hourly values read from file."
1671 : "..Number read={}.",
1672 : numHourlyValues,
1673 0 : (rowCnt * Constant::iMinutesInHour) / MinutesPerItem));
1674 : }
1675 :
1676 : // process the data into the normal schedule data structures
1677 : // note -- schedules are ALWAYS 366 days so some special measures have to be done at 29 Feb "day of year" (60)
1678 133 : int iDay = 0;
1679 133 : int hDay = 0;
1680 133 : int ifld = 0;
1681 : while (true) {
1682 : // create string of which day of year
1683 48678 : ++iDay;
1684 48678 : ++hDay;
1685 48678 : if (iDay > 366) {
1686 133 : break;
1687 : }
1688 : // increment both since a week schedule is being defined for each day so that a day is valid
1689 : // no matter what the day type that is used in a design day.
1690 :
1691 : // define day schedule
1692 48545 : auto *daySched = AddDaySchedule(state, format("{}_dy_{}", Alphas(1), iDay));
1693 48545 : daySched->schedTypeNum = sched->schedTypeNum;
1694 :
1695 : // define week schedule
1696 48545 : auto *weekSched = AddWeekSchedule(state, format("{}_wk_{}", Alphas(1), iDay));
1697 :
1698 : // for all day types point the week schedule to the newly defined day schedule
1699 631085 : for (int kDayType = 1; kDayType < (int)DayType::Num; ++kDayType) {
1700 582540 : weekSched->dayScheds[kDayType] = daySched;
1701 : }
1702 :
1703 : // schedule is pointing to the week schedule
1704 48545 : sched->weekScheds[iDay] = weekSched;
1705 :
1706 48545 : if (MinutesPerItem == Constant::iMinutesInHour) {
1707 100375 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
1708 96360 : Real64 curHrVal = column_values[ifld]; // hourlyFileValues((hDay - 1) * 24 + jHour)
1709 96360 : ++ifld;
1710 481800 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
1711 385440 : daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = curHrVal;
1712 385440 : daySched->sumTsVals += daySched->tsVals[hr * s_glob->TimeStepsInHour + ts];
1713 : }
1714 : }
1715 : } else { // Minutes Per Item < 60
1716 1113250 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
1717 1068720 : int endMin = MinutesPerItem - 1;
1718 1068720 : int begMin = 0;
1719 5694000 : for (int NumFields = 1; NumFields <= hrLimitCount; ++NumFields) {
1720 68748480 : for (int iMin = begMin; iMin <= endMin; ++iMin) {
1721 64123200 : minuteVals[hr * Constant::iMinutesInHour + iMin] = column_values[ifld];
1722 : }
1723 :
1724 4625280 : ++ifld;
1725 4625280 : begMin = endMin + 1;
1726 4625280 : endMin += MinutesPerItem;
1727 : }
1728 : }
1729 :
1730 44530 : daySched->interpolation = interp;
1731 44530 : daySched->populateFromMinuteVals(state, minuteVals);
1732 : }
1733 48545 : if (iDay == 59 && rowCnt < 8784 * hrLimitCount) { // 28 Feb
1734 : // Dup 28 Feb to 29 Feb (60)
1735 133 : ++iDay;
1736 133 : sched->weekScheds[iDay] = sched->weekScheds[iDay - 1];
1737 : }
1738 48545 : }
1739 133 : }
1740 :
1741 133 : if (s_glob->AnyEnergyManagementSystemInModel) { // setup constant schedules as actuators
1742 0 : SetupEMSActuator(state, "Schedule:File", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal);
1743 : }
1744 133 : }
1745 :
1746 803 : if (NumCommaFileShading != 0) {
1747 1 : auto const &values_json = schedule_file_shading_result->second["values"];
1748 1 : auto const headers = schedule_file_shading_result->second["header"].get<std::vector<std::string>>(); // (AUTO_OK_OBJ)
1749 1 : auto const headers_set = schedule_file_shading_result->second["header"].get<std::set<std::string>>(); // (AUTO_OK_OBJ)
1750 :
1751 115 : for (auto const &header : headers_set) {
1752 114 : size_t column = 0;
1753 114 : auto column_it = std::find(headers.begin(), headers.end(), header);
1754 114 : if (column_it != headers.end()) {
1755 228 : column = std::distance(headers.begin(), column_it);
1756 : }
1757 114 : if (column == 0) {
1758 1 : continue; // Skip timestamp column and any duplicate column, which will be 0 as well since it won't be found.
1759 : }
1760 113 : auto const column_values = values_json.at(column).get<std::vector<Real64>>(); // (AUTO_OK_OBJ)
1761 :
1762 113 : std::string curName = format("{}_shading", header);
1763 113 : std::string curNameUC = Util::makeUPPER(curName);
1764 :
1765 113 : if (s_sched->scheduleMap.find(curNameUC) != s_sched->scheduleMap.end()) {
1766 0 : ShowSevereError(state, format("Duplicate schedule name {}", curName));
1767 0 : ErrorsFound = true;
1768 0 : continue;
1769 : }
1770 :
1771 113 : auto *schedShading = AddScheduleDetailed(state, curName);
1772 113 : schedShading->type = SchedType::File;
1773 :
1774 113 : int iDay = 0;
1775 113 : int ifld = 0;
1776 : while (true) {
1777 : // create string of which day of year
1778 41358 : ++iDay;
1779 41358 : if (iDay > 366) {
1780 113 : break;
1781 : }
1782 :
1783 : // day schedule
1784 41245 : auto *daySched = AddDaySchedule(state, format("{}_dy_{}", curName, iDay));
1785 41245 : daySched->schedTypeNum = schedShading->schedTypeNum;
1786 :
1787 : // define week schedule
1788 41245 : auto *weekSched = AddWeekSchedule(state, format("{}_wk_{}", curName, iDay));
1789 :
1790 : // for all day types point the week schedule to the newly defined day schedule
1791 536185 : for (int kDayType = 1; kDayType < (int)DayType::Num; ++kDayType) {
1792 494940 : weekSched->dayScheds[kDayType] = daySched;
1793 : }
1794 :
1795 : // schedule is pointing to the week schedule
1796 41245 : schedShading->weekScheds[iDay] = weekSched;
1797 :
1798 1031125 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
1799 4949400 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
1800 3959520 : daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = column_values[ifld];
1801 3959520 : ++ifld;
1802 : }
1803 : }
1804 :
1805 41245 : if (iDay == 59 && !state.dataEnvrn->CurrentYearIsLeapYear) { // 28 Feb
1806 : // Dup 28 Feb to 29 Feb (60)
1807 113 : ++iDay;
1808 113 : schedShading->weekScheds[iDay] = schedShading->weekScheds[iDay - 1];
1809 : }
1810 41245 : }
1811 115 : }
1812 1 : }
1813 :
1814 : // Constant Schedules
1815 803 : CurrentModuleObject = "Schedule:Constant";
1816 1068 : for (int Loop = 1; Loop <= NumConstantSchedules; ++Loop) {
1817 265 : s_ip->getObjectItem(state,
1818 : CurrentModuleObject,
1819 : Loop,
1820 : Alphas,
1821 : NumAlphas,
1822 : Numbers,
1823 : NumNumbers,
1824 : Status,
1825 : lNumericBlanks,
1826 : lAlphaBlanks,
1827 : cAlphaFields,
1828 : cNumericFields);
1829 :
1830 265 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1831 :
1832 265 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
1833 0 : ShowSevereDuplicateName(state, eoh);
1834 0 : ErrorsFound = true;
1835 0 : continue;
1836 : }
1837 :
1838 265 : auto *sched = AddScheduleConstant(state, Alphas(1), Numbers(1));
1839 :
1840 : // Validate ScheduleType
1841 265 : if (lAlphaBlanks(2)) { // No warning here for constant schedules
1842 34 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
1843 102 : ShowContinueError(state, "Schedule will not be validated.");
1844 231 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
1845 2 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
1846 6 : ShowContinueError(state, "Schedule will not be validated.");
1847 : }
1848 :
1849 265 : if (s_glob->AnyEnergyManagementSystemInModel) { // setup constant schedules as actuators
1850 94 : SetupEMSActuator(state, "Schedule:Constant", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal);
1851 : }
1852 : }
1853 : // When InitConstantScheduleData is called, TimeStepsInHour is 0, so we delay it here
1854 803 : static_cast<ScheduleConstant *>(s_sched->schedules[SchedNum_AlwaysOff])->tsVals.assign(Constant::iHoursInDay * s_glob->TimeStepsInHour, 0.0);
1855 803 : static_cast<ScheduleConstant *>(s_sched->schedules[SchedNum_AlwaysOn])->tsVals.assign(Constant::iHoursInDay * s_glob->TimeStepsInHour, 1.0);
1856 :
1857 803 : CurrentModuleObject = "ExternalInterface:Schedule";
1858 803 : for (int Loop = 1; Loop <= NumExternalInterfaceSchedules; ++Loop) {
1859 0 : s_ip->getObjectItem(state,
1860 : CurrentModuleObject,
1861 : Loop,
1862 : Alphas,
1863 : NumAlphas,
1864 : Numbers,
1865 : NumNumbers,
1866 : Status,
1867 : lNumericBlanks,
1868 : lAlphaBlanks,
1869 : cAlphaFields,
1870 : cNumericFields);
1871 :
1872 0 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1873 :
1874 0 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
1875 0 : ShowSevereDuplicateName(state, eoh);
1876 0 : ErrorsFound = true;
1877 0 : continue;
1878 : }
1879 :
1880 0 : auto *sched = AddScheduleDetailed(state, Alphas(1));
1881 0 : sched->type = SchedType::External;
1882 :
1883 : // Validate ScheduleType
1884 0 : if (lAlphaBlanks(2)) {
1885 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
1886 0 : ShowContinueError(state, "Schedule will not be validated.");
1887 0 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
1888 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
1889 0 : ShowContinueError(state, "Schedule will not be validated.");
1890 : }
1891 :
1892 : // TODO: I'm not sure this Jazz is necessary
1893 : // Add day schedule
1894 0 : auto *daySched = AddDaySchedule(state, format("{}_xi_dy_", Alphas(1)));
1895 0 : daySched->isUsed = true;
1896 0 : daySched->schedTypeNum = sched->schedTypeNum;
1897 :
1898 : // Initialize the ExternalInterface day schedule for the ExternalInterface compact schedule.
1899 : // It will be overwritten during run time stepping after the warm up period
1900 0 : if (NumNumbers < 1) {
1901 0 : ShowWarningCustom(state, eoh, "Initial value is not numeric or is missing. Fix idf file.");
1902 0 : NumErrorFlag = true;
1903 : }
1904 0 : ExternalInterfaceSetSchedule(state, daySched->Num, Numbers(1));
1905 :
1906 0 : auto *weekSched = AddWeekSchedule(state, format("{}_xi_wk_", Alphas(1)));
1907 0 : weekSched->isUsed = true;
1908 0 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
1909 0 : weekSched->dayScheds[iDayType] = daySched;
1910 : }
1911 :
1912 0 : for (int iDay = 1; iDay <= 366; ++iDay) {
1913 0 : sched->weekScheds[iDay] = weekSched;
1914 : }
1915 : } // for (Loop)
1916 :
1917 : // added for FMU Import
1918 803 : CurrentModuleObject = "ExternalInterface:FunctionalMockupUnitImport:To:Schedule";
1919 805 : for (int Loop = 1; Loop <= NumExternalInterfaceFunctionalMockupUnitImportSchedules; ++Loop) {
1920 2 : s_ip->getObjectItem(state,
1921 : CurrentModuleObject,
1922 : Loop,
1923 : Alphas,
1924 : NumAlphas,
1925 : Numbers,
1926 : NumNumbers,
1927 : Status,
1928 : lNumericBlanks,
1929 : lAlphaBlanks,
1930 : cAlphaFields,
1931 : cNumericFields);
1932 :
1933 2 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
1934 :
1935 2 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
1936 0 : ShowSevereDuplicateName(state, eoh);
1937 0 : if (NumExternalInterfaceSchedules >= 1) {
1938 0 : ShowContinueError(
1939 : state,
1940 0 : format("{} defined as an ExternalInterface:Schedule and ExternalInterface:FunctionalMockupUnitImport:To:Schedule."
1941 : "This will cause the schedule to be overwritten by PtolemyServer and FunctionalMockUpUnitImport)",
1942 : cAlphaFields(1)));
1943 : }
1944 0 : ErrorsFound = true;
1945 0 : continue;
1946 : }
1947 :
1948 2 : auto *sched = AddScheduleDetailed(state, Alphas(1));
1949 2 : sched->type = SchedType::External;
1950 :
1951 : // Validate ScheduleType
1952 2 : if (lAlphaBlanks(2)) {
1953 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
1954 0 : ShowContinueError(state, "Schedule will not be validated.");
1955 2 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
1956 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
1957 0 : ShowContinueError(state, "Schedule will not be validated.");
1958 : }
1959 :
1960 : // TODO: I'm not sure this Jazz is necessary
1961 : // Add day schedule
1962 2 : auto *daySched = AddDaySchedule(state, format("{}_xi_dy_", Alphas(1)));
1963 2 : daySched->isUsed = true;
1964 2 : daySched->schedTypeNum = sched->schedTypeNum;
1965 :
1966 : // Initialize the ExternalInterface day schedule for the ExternalInterface compact schedule.
1967 : // It will be overwritten during run time stepping after the warm up period
1968 2 : if (NumNumbers < 1) {
1969 0 : ShowWarningCustom(state, eoh, "Initial value is not numeric or is missing. Fix idf file.");
1970 0 : NumErrorFlag = true;
1971 : }
1972 2 : ExternalInterfaceSetSchedule(state, daySched->Num, Numbers(1));
1973 :
1974 2 : auto *weekSched = AddWeekSchedule(state, format("{}_xi_wk_", Alphas(1)));
1975 2 : weekSched->isUsed = true;
1976 26 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
1977 24 : weekSched->dayScheds[iDayType] = daySched;
1978 : }
1979 :
1980 734 : for (int iDay = 1; iDay <= 366; ++iDay) {
1981 732 : sched->weekScheds[iDay] = weekSched;
1982 : }
1983 : }
1984 :
1985 : // added for FMU Export
1986 803 : CurrentModuleObject = "ExternalInterface:FunctionalMockupUnitExport:To:Schedule";
1987 803 : for (int Loop = 1; Loop <= NumExternalInterfaceFunctionalMockupUnitExportSchedules; ++Loop) {
1988 0 : s_ip->getObjectItem(state,
1989 : CurrentModuleObject,
1990 : Loop,
1991 : Alphas,
1992 : NumAlphas,
1993 : Numbers,
1994 : NumNumbers,
1995 : Status,
1996 : lNumericBlanks,
1997 : lAlphaBlanks,
1998 : cAlphaFields,
1999 : cNumericFields);
2000 :
2001 0 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
2002 :
2003 0 : if (s_sched->scheduleMap.find(Alphas(1)) != s_sched->scheduleMap.end()) {
2004 0 : ShowSevereDuplicateName(state, eoh);
2005 0 : if (NumExternalInterfaceSchedules >= 1) {
2006 0 : ShowContinueError(
2007 : state,
2008 0 : format("{} defined as an ExternalInterface:Schedule and ExternalInterface:FunctionalMockupUnitImport:To:Schedule."
2009 : "This will cause the schedule to be overwritten by PtolemyServer and FunctionalMockUpUnitImport)",
2010 : cAlphaFields(1)));
2011 : }
2012 0 : ErrorsFound = true;
2013 0 : continue;
2014 : }
2015 :
2016 0 : auto *sched = AddScheduleDetailed(state, Alphas(1));
2017 0 : sched->type = SchedType::External;
2018 :
2019 : // Validate ScheduleType
2020 0 : if (lAlphaBlanks(2)) {
2021 0 : ShowWarningEmptyField(state, eoh, cAlphaFields(2));
2022 0 : ShowContinueError(state, "Schedule will not be validated.");
2023 0 : } else if ((sched->schedTypeNum = GetScheduleTypeNum(state, Alphas(2))) == SchedNum_Invalid) {
2024 0 : ShowWarningItemNotFound(state, eoh, cAlphaFields(2), Alphas(2));
2025 0 : ShowContinueError(state, "Schedule will not be validated.");
2026 : }
2027 :
2028 : // TODO: I'm not sure this Jazz is necessary
2029 : // Add day schedule
2030 0 : auto *daySched = AddDaySchedule(state, format("{}_xi_dy_", Alphas(1)));
2031 0 : daySched->isUsed = true;
2032 0 : daySched->schedTypeNum = sched->schedTypeNum;
2033 :
2034 : // Initialize the ExternalInterface day schedule for the ExternalInterface compact schedule.
2035 : // It will be overwritten during run time stepping after the warm up period
2036 0 : if (NumNumbers < 1) {
2037 0 : ShowWarningCustom(state, eoh, "Initial value is not numeric or is missing. Fix idf file.");
2038 0 : NumErrorFlag = true;
2039 : }
2040 0 : ExternalInterfaceSetSchedule(state, daySched->Num, Numbers(1));
2041 :
2042 0 : auto *weekSched = AddWeekSchedule(state, format("{}_xi_wk_", Alphas(1)));
2043 0 : weekSched->isUsed = true;
2044 0 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
2045 0 : weekSched->dayScheds[iDayType] = daySched;
2046 : }
2047 :
2048 0 : std::fill(sched->weekScheds.begin() + 1, sched->weekScheds.end(), weekSched);
2049 : } // for (Loop)
2050 :
2051 : // Validate by ScheduleLimitsType
2052 19699 : for (auto *sched : s_sched->schedules) {
2053 :
2054 18896 : if (sched->schedTypeNum == SchedNum_Invalid) {
2055 1765 : continue;
2056 : }
2057 :
2058 17131 : auto const *schedType = s_sched->scheduleTypes[sched->schedTypeNum];
2059 17131 : if (!schedType->isLimited) {
2060 3562 : continue;
2061 : }
2062 :
2063 13569 : if (!sched->checkMinMaxVals(state, Clusive::In, schedType->minVal, Clusive::In, schedType->maxVal)) {
2064 0 : ErrorObjectHeader eoh{routineName, "Schedule", sched->Name};
2065 0 : ShowSevereBadMinMax(state, eoh, "", "", Clusive::In, schedType->minVal, Clusive::In, schedType->maxVal);
2066 0 : ErrorsFound = true;
2067 : }
2068 803 : }
2069 :
2070 803 : if (ErrorsFound) {
2071 0 : ShowFatalError(state, format("{}: Preceding Errors cause termination.", routineName));
2072 : }
2073 :
2074 803 : if (s_sched->scheduleTypes.size() + s_sched->daySchedules.size() + s_sched->weekSchedules.size() + s_sched->schedules.size() > 0) {
2075 803 : CurrentModuleObject = "Output:Schedules";
2076 803 : NumFields = s_ip->getNumObjectsFound(state, CurrentModuleObject);
2077 :
2078 : // Report the ScheduleTypeLimits only once, not for each report level.
2079 803 : if (NumFields > 0) {
2080 21 : ReportScheduleTypeLimits(state);
2081 : }
2082 :
2083 803 : std::set<ReportLevel> reportLevelSet;
2084 : // RptSchedule=.FALSE.
2085 826 : for (int Count = 1; Count <= NumFields; ++Count) {
2086 23 : s_ip->getObjectItem(state, CurrentModuleObject, Count, Alphas, NumAlphas, Numbers, NumNumbers, Status);
2087 :
2088 23 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
2089 :
2090 : // IDD only allows Hourly or Timestep as valid values on the required field, anything else should be an error in the input processor
2091 23 : ReportLevel reportLevel = static_cast<ReportLevel>(getEnumValue(reportLevelNamesUC, Alphas(1)));
2092 23 : if (reportLevel == ReportLevel::Invalid) {
2093 0 : ShowWarningInvalidKey(state, eoh, cAlphaFields(1), Alphas(1), "HOURLY report will be done");
2094 0 : reportLevel = ReportLevel::Hourly;
2095 : }
2096 23 : if (auto [it, inserted] = reportLevelSet.insert(reportLevel); inserted) {
2097 : // If this is the first time we see this report level, we need to report the details
2098 23 : ReportScheduleDetails(state, reportLevel);
2099 : } else {
2100 0 : ShowWarningCustom(state,
2101 : eoh,
2102 0 : format("Report level {} has already been processed. This report level will not be processed again.",
2103 0 : reportLevelNames[(int)reportLevel]));
2104 0 : continue;
2105 23 : }
2106 : }
2107 803 : }
2108 :
2109 803 : Alphas.deallocate();
2110 803 : cAlphaFields.deallocate();
2111 803 : cNumericFields.deallocate();
2112 803 : Numbers.deallocate();
2113 803 : lAlphaBlanks.deallocate();
2114 803 : lNumericBlanks.deallocate();
2115 :
2116 803 : print(state.files.audit, "{}\n", " Processing Schedule Input -- Complete");
2117 36025 : } // ProcessScheduleInput()
2118 :
2119 21 : void ReportScheduleTypeLimits(EnergyPlusData &state)
2120 : {
2121 21 : std::string YesNoLimited;
2122 21 : std::string minValStr;
2123 21 : std::string maxValStr;
2124 21 : std::string YesNoContinous;
2125 :
2126 21 : std::string_view constexpr scheduleTypeLimitTableName("ScheduleTypeLimits");
2127 21 : print(state.files.eio, "! <{}>,Name,Limited? {{Yes/No}},Minimum,Maximum,Continuous? {{Yes/No - Discrete}}\n", scheduleTypeLimitTableName);
2128 :
2129 155 : for (auto const *schedType : state.dataSched->scheduleTypes) {
2130 134 : if (schedType->isLimited) {
2131 98 : YesNoLimited = "Yes";
2132 98 : minValStr = format("{:.2R}", schedType->minVal);
2133 98 : strip(minValStr);
2134 98 : maxValStr = format("{:.2R}", schedType->maxVal);
2135 98 : strip(maxValStr);
2136 98 : if (schedType->isReal) {
2137 61 : YesNoContinous = "Yes";
2138 : } else {
2139 37 : YesNoContinous = "No";
2140 37 : minValStr = fmt::to_string((int)schedType->minVal);
2141 37 : maxValStr = fmt::to_string((int)schedType->maxVal);
2142 : }
2143 : } else {
2144 36 : YesNoLimited = "No";
2145 36 : minValStr = "N/A";
2146 36 : maxValStr = "N/A";
2147 36 : YesNoContinous = "N/A";
2148 : }
2149 134 : print(state.files.eio,
2150 : "{},{},{},{},{},{}\n",
2151 : scheduleTypeLimitTableName,
2152 134 : schedType->Name,
2153 : YesNoLimited,
2154 : minValStr,
2155 : maxValStr,
2156 : YesNoContinous);
2157 21 : }
2158 21 : }
2159 :
2160 23 : void ReportScheduleDetails(EnergyPlusData &state,
2161 : ReportLevel const LevelOfDetail) // =1: hourly; =2: timestep; = 3: make IDF excerpt
2162 : {
2163 : // SUBROUTINE INFORMATION:
2164 : // AUTHOR Linda K. Lawrie
2165 : // DATE WRITTEN January 2003
2166 : // MODIFIED February 2008 - add IDF outputs (compact schedules)
2167 :
2168 : // PURPOSE OF THIS SUBROUTINE:
2169 : // This subroutine puts the details of the Schedules on the .eio file (Inits file).
2170 :
2171 : // SUBROUTINE PARAMETER DEFINITIONS:
2172 :
2173 23 : assert(LevelOfDetail != ReportLevel::Invalid);
2174 :
2175 23 : constexpr std::array<std::string_view, 12> Months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2176 23 : constexpr std::array<std::string_view, 25> HrField = {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
2177 : "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"};
2178 :
2179 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2180 :
2181 23 : auto const &s_glob = state.dataGlobal;
2182 23 : auto const &s_sched = state.dataSched;
2183 :
2184 23 : std::vector<std::string> times;
2185 23 : int const NumTimesInDay = (LevelOfDetail == ReportLevel::TimeStep) ? s_glob->TimeStepsInHour * Constant::iHoursInDay : Constant::iHoursInDay;
2186 23 : if (LevelOfDetail == ReportLevel::TimeStep) {
2187 20 : times.reserve(NumTimesInDay);
2188 3 : } else if (LevelOfDetail == ReportLevel::Hourly) {
2189 3 : times.reserve(NumTimesInDay);
2190 : } else {
2191 0 : assert(false); // Invalid report level
2192 : }
2193 :
2194 575 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
2195 552 : if (LevelOfDetail == ReportLevel::TimeStep) {
2196 2784 : for (int ts = 0; ts < s_glob->TimeStepsInHour - 1; ++ts) {
2197 4608 : times.emplace_back(fmt::format("{}:{:02}", HrField[hr], ((ts + 1)) * s_glob->MinutesInTimeStep));
2198 : }
2199 : }
2200 1104 : times.emplace_back(fmt::format("{}:00", HrField[hr + 1]));
2201 : }
2202 23 : assert(times.size() == NumTimesInDay);
2203 :
2204 23 : std::string_view const &reportLevelName = reportLevelNames[(int)LevelOfDetail];
2205 23 : std::string const dayScheduleTableName = format("DaySchedule - {}", reportLevelName);
2206 23 : std::string const weekScheduleTableName = format("WeekSchedule - {}", reportLevelName); // TODO: "Schedule:Week:Daily" ?
2207 23 : std::string const scheduleTableName = format("Schedule - {}", reportLevelName); // TODO: "Detailed Schedule" maybe?
2208 :
2209 : // Schedule Types Header
2210 : {
2211 : // This is just a top level separator, for convenience/neatness, will not match any actual data
2212 23 : print(state.files.eio, "! Schedule Details Report={} =====================\n", reportLevelName);
2213 :
2214 : // ! <DaySchedule_Hourly>,Name,ScheduleType,Interpolated {Yes/No},Time (HH:MM) =>,00:15,00:30,..23:45,24.00
2215 23 : print(state.files.eio, "! <{}>,Name,ScheduleType,Interpolated {{Average/Linear/No}},Time (HH:MM) =>", dayScheduleTableName);
2216 2879 : for (const auto &time : times) {
2217 2856 : print(state.files.eio, ",{}", time);
2218 23 : }
2219 23 : print(state.files.eio, "\n");
2220 :
2221 : // ! <WeekSchedule_Hourly>,Name,Sunday,Monday,...,Saturday,Holiday,SummerDesignDay,WinterDesignDay,CustomDay1,CustomDay2
2222 23 : print(state.files.eio, "! <{}>,Name", weekScheduleTableName);
2223 299 : for (int Count = 1; Count < (int)DayType::Num; ++Count) {
2224 276 : print(state.files.eio, ",{}", dayTypeNames[Count]);
2225 : }
2226 23 : print(state.files.eio, "\n");
2227 :
2228 : // ! <Schedule_Hourly>,Name,ScheduleType,{Until Date,WeekSchedule}** Repeated until Dec 31
2229 23 : print(state.files.eio, "! <{}>,Name,ScheduleType,{{Until Date,WeekSchedule}}** Repeated until Dec 31\n", scheduleTableName);
2230 : }
2231 :
2232 944 : for (auto *daySched : s_sched->daySchedules) {
2233 :
2234 921 : print(state.files.eio,
2235 : "{},{},{},{},{}",
2236 : dayScheduleTableName,
2237 921 : daySched->Name,
2238 1842 : (daySched->schedTypeNum == SchedNum_Invalid) ? "" : s_sched->scheduleTypes[daySched->schedTypeNum]->Name,
2239 921 : interpolationNames[(int)daySched->interpolation],
2240 : "Values:");
2241 :
2242 921 : if (LevelOfDetail == ReportLevel::Hourly) {
2243 575 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
2244 552 : print(state.files.eio, ",{:.2R}", daySched->tsVals[(hr + 1) * s_glob->TimeStepsInHour - 1]);
2245 : }
2246 898 : } else if (LevelOfDetail == ReportLevel::TimeStep) {
2247 22450 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
2248 150000 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
2249 128448 : print(state.files.eio, ",{:.2R}", daySched->tsVals[hr * s_glob->TimeStepsInHour + ts]);
2250 : }
2251 : }
2252 : } else {
2253 0 : assert(false);
2254 : }
2255 921 : print(state.files.eio, "\n");
2256 23 : }
2257 :
2258 672 : for (auto *weekSched : s_sched->weekSchedules) {
2259 649 : print(state.files.eio, "{},{}", weekScheduleTableName, weekSched->Name);
2260 8437 : for (int DayNum = 1; DayNum < (int)DayType::Num; ++DayNum) {
2261 7788 : print(state.files.eio, ",{}", weekSched->dayScheds[DayNum]->Name);
2262 : }
2263 649 : print(state.files.eio, "\n");
2264 23 : }
2265 :
2266 712 : for (auto *sched : s_sched->schedules) {
2267 :
2268 689 : if (sched->type == SchedType::Constant) {
2269 48 : continue;
2270 : }
2271 :
2272 641 : auto *schedDetailed = dynamic_cast<ScheduleDetailed *>(sched);
2273 641 : assert(schedDetailed != nullptr);
2274 :
2275 641 : print(state.files.eio,
2276 : "{},{},{}",
2277 : scheduleTableName,
2278 641 : schedDetailed->Name,
2279 1282 : (sched->schedTypeNum == SchedNum_Invalid) ? "" : s_sched->scheduleTypes[sched->schedTypeNum]->Name);
2280 :
2281 641 : int PMon = 0;
2282 641 : int PDay = 0;
2283 641 : std::string_view constexpr ThruFmt(",Through {} {:02},{}");
2284 641 : int DayNum = 1;
2285 1290 : while (DayNum <= 366) {
2286 649 : auto *weekSched = schedDetailed->weekScheds[DayNum];
2287 234614 : while (schedDetailed->weekScheds[DayNum] == weekSched && DayNum <= 366) {
2288 234606 : if (DayNum == 366) {
2289 641 : General::InvOrdinalDay(DayNum, PMon, PDay, 1);
2290 641 : print(state.files.eio, ThruFmt, Months[PMon - 1], PDay, weekSched->Name);
2291 : }
2292 234606 : ++DayNum;
2293 234606 : if (DayNum > 366) {
2294 641 : break; // compound If might have a problem unless this included.
2295 : }
2296 : }
2297 649 : if (DayNum <= 366) {
2298 8 : General::InvOrdinalDay(DayNum - 1, PMon, PDay, 1);
2299 8 : print(state.files.eio, ThruFmt, Months[PMon - 1], PDay, weekSched->Name);
2300 : }
2301 : }
2302 641 : print(state.files.eio, "\n");
2303 23 : }
2304 :
2305 23 : } // ReportScheduleDetails()
2306 :
2307 0 : Real64 GetCurrentScheduleValue(EnergyPlusData const &state, int const schedNum)
2308 : {
2309 : // Wrapper for method
2310 0 : return state.dataSched->schedules[schedNum]->getCurrentVal();
2311 : }
2312 :
2313 5697462 : void UpdateScheduleVals(EnergyPlusData &state)
2314 : {
2315 : // SUBROUTINE INFORMATION:
2316 : // AUTHOR Linda Lawrie
2317 : // DATE WRITTEN August 2011; adapted from Autodesk (time reduction)
2318 :
2319 : // PURPOSE OF THIS SUBROUTINE:
2320 : // This routine calculates all the scheduled values as a time reduction measure and
2321 : // stores them in the CurrentValue item of the schedule data structure.
2322 :
2323 : // METHODOLOGY EMPLOYED:
2324 : // Use internal Schedule data structure to calculate current value. Note that missing values in
2325 :
2326 5697462 : auto const &s_sched = state.dataSched;
2327 5697462 : auto const &s_glob = state.dataGlobal;
2328 :
2329 145662997 : for (auto *sched : s_sched->schedules) {
2330 139965535 : if (sched->EMSActuatedOn) {
2331 182271 : sched->currentVal = sched->EMSVal;
2332 : } else {
2333 139783264 : sched->currentVal = sched->getHrTsVal(state, s_glob->HourOfDay, s_glob->TimeStep);
2334 : }
2335 5697462 : }
2336 5697462 : }
2337 :
2338 130782663 : Real64 ScheduleDetailed::getHrTsVal(EnergyPlusData &state,
2339 : int hr,
2340 : int ts // Negative => unspecified
2341 : ) const
2342 : {
2343 : // FUNCTION INFORMATION:
2344 : // AUTHOR Linda K. Lawrie
2345 : // DATE WRITTEN January 2003
2346 : // PURPOSE OF THIS FUNCTION:
2347 : // This function provides a method to look up schedule values for any hour, timestep, day
2348 : // of the year (rather than just the "current time").
2349 130782663 : auto const &s_glob = state.dataGlobal;
2350 :
2351 130782663 : if (this->EMSActuatedOn) {
2352 0 : return this->EMSVal;
2353 : }
2354 :
2355 : // so, current date, but maybe TimeStep added
2356 :
2357 : // Hourly Value
2358 130782663 : if (hr > Constant::iHoursInDay) {
2359 0 : ShowFatalError(state, format("LookUpScheduleValue called with thisHour={}", hr));
2360 : }
2361 :
2362 130782663 : int thisHr = hr + state.dataEnvrn->DSTIndicator * this->UseDaylightSaving;
2363 :
2364 130782663 : int thisDayOfYear = state.dataEnvrn->DayOfYear_Schedule;
2365 130782663 : int thisDayOfWeek = state.dataEnvrn->DayOfWeek;
2366 130782663 : int thisHolidayNum = state.dataEnvrn->HolidayIndex;
2367 130782663 : if (thisHr > Constant::iHoursInDay) { // In case HourOfDay is 24 and DSTIndicator is 1, you're actually the next day
2368 51146 : thisDayOfYear += 1;
2369 51146 : thisHr -= Constant::iHoursInDay;
2370 51146 : thisDayOfWeek = state.dataEnvrn->DayOfWeekTomorrow;
2371 51146 : thisHolidayNum = state.dataEnvrn->HolidayIndexTomorrow;
2372 : }
2373 :
2374 : // In the case where DST is applied on 12/31 at 24:00, which is the case for a Southern Hemisphere location for eg
2375 : // (DayOfYear_Schedule is a bit weird, ScheduleManager always assumes LeapYear)
2376 130782663 : if (thisDayOfYear == 367) {
2377 0 : thisDayOfYear = 1;
2378 : }
2379 :
2380 130782663 : auto const *weekSched = this->weekScheds[thisDayOfYear];
2381 130782663 : auto const *daySched = (thisHolidayNum > 0) ? weekSched->dayScheds[thisHolidayNum] : weekSched->dayScheds[thisDayOfWeek];
2382 130782663 : if (daySched == nullptr) {
2383 : // We already warned in ProcessScheduleInput that there were missing days: Missing day types will have 0.0 as Schedule Values
2384 0 : return 0.0;
2385 : }
2386 :
2387 : // If Unspecified or equal to zero, use NumOfTimeStepInHour, otherwise use supplied
2388 130782663 : if (ts <= 0) {
2389 1157613 : ts = s_glob->TimeStepsInHour;
2390 : }
2391 :
2392 130782663 : return daySched->tsVals[(thisHr - 1) * s_glob->TimeStepsInHour + (ts - 1)];
2393 : } // ScheduleDetailed::getHrTsVal()
2394 :
2395 13027261 : Real64 ScheduleConstant::getHrTsVal([[maybe_unused]] EnergyPlusData &state, [[maybe_unused]] int hr, [[maybe_unused]] int ts) const
2396 : {
2397 : // cf #10962 - We can't use currentValue as it could be overwritten by the EMS Sensor
2398 13027261 : return this->tsVals.front();
2399 : } // ScheduleConstant::getHrTsVal()
2400 :
2401 22564 : Sched::Schedule *GetScheduleAlwaysOn(EnergyPlusData &state)
2402 : {
2403 22564 : return state.dataSched->schedules[SchedNum_AlwaysOn];
2404 : }
2405 :
2406 16 : Sched::Schedule *GetScheduleAlwaysOff(EnergyPlusData &state)
2407 : {
2408 16 : return state.dataSched->schedules[SchedNum_AlwaysOff];
2409 : }
2410 :
2411 128643 : Sched::Schedule *GetSchedule(EnergyPlusData &state, std::string const &name)
2412 : {
2413 : // FUNCTION INFORMATION:
2414 : // AUTHOR Linda K. Lawrie
2415 : // DATE WRITTEN September 1997
2416 :
2417 : // PURPOSE OF THIS FUNCTION:
2418 : // This function returns the internal pointer to Schedule "ScheduleName".
2419 128643 : auto const &s_sched = state.dataSched;
2420 :
2421 128643 : auto found = s_sched->scheduleMap.find(name);
2422 128643 : if (found == s_sched->scheduleMap.end()) {
2423 57458 : return nullptr;
2424 : }
2425 :
2426 71185 : int schedNum = found->second;
2427 :
2428 71185 : auto *sched = s_sched->schedules[schedNum];
2429 :
2430 71185 : if (!sched->isUsed) {
2431 16379 : sched->isUsed = true;
2432 :
2433 16379 : if (sched->type != SchedType::Constant) {
2434 :
2435 16163 : auto *schedDetailed = dynamic_cast<ScheduleDetailed *>(sched);
2436 16163 : assert(schedDetailed != nullptr);
2437 :
2438 16163 : schedDetailed->isUsed = true;
2439 5931821 : for (int iWeek = 1; iWeek <= 366; ++iWeek) {
2440 5915658 : if (auto *weekSched = schedDetailed->weekScheds[iWeek]; weekSched != nullptr) {
2441 5915658 : if (weekSched->isUsed) {
2442 5867113 : continue;
2443 : }
2444 :
2445 48545 : weekSched->isUsed = true;
2446 631085 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
2447 582540 : auto *daySched = weekSched->dayScheds[iDayType];
2448 582540 : daySched->isUsed = true;
2449 : }
2450 : }
2451 : }
2452 : }
2453 : }
2454 71185 : return sched;
2455 128643 : } // GetSchedule()
2456 :
2457 2 : int GetScheduleNum(EnergyPlusData &state, std::string const &name)
2458 : {
2459 2 : auto *sched = GetSchedule(state, name);
2460 2 : return (sched == nullptr) ? -1 : sched->Num;
2461 : }
2462 :
2463 406 : Sched::WeekSchedule *GetWeekSchedule(EnergyPlusData &state, std::string const &name)
2464 : {
2465 406 : auto const &s_sched = state.dataSched;
2466 :
2467 406 : auto found = s_sched->weekScheduleMap.find(name);
2468 406 : if (found == s_sched->weekScheduleMap.end()) {
2469 0 : return nullptr;
2470 : }
2471 :
2472 406 : int weekSchedNum = found->second;
2473 :
2474 406 : auto *weekSched = s_sched->weekSchedules[weekSchedNum];
2475 :
2476 406 : if (!weekSched->isUsed) {
2477 399 : weekSched->isUsed = true;
2478 5187 : for (int iDayType = 1; iDayType < (int)DayType::Num; ++iDayType) {
2479 4788 : auto *daySched = weekSched->dayScheds[iDayType];
2480 4788 : if (daySched == nullptr) {
2481 0 : continue;
2482 : }
2483 4788 : daySched->isUsed = true;
2484 : }
2485 : }
2486 406 : return weekSched;
2487 406 : } // GetWeekSchedule()
2488 :
2489 0 : int GetWeekScheduleNum(EnergyPlusData &state, std::string const &name)
2490 : {
2491 0 : auto *weekSched = GetWeekSchedule(state, name);
2492 0 : return (weekSched == nullptr) ? -1 : weekSched->Num;
2493 : }
2494 :
2495 4271 : Sched::DaySchedule *GetDaySchedule(EnergyPlusData &state, std::string const &name)
2496 : {
2497 : // FUNCTION INFORMATION:
2498 : // AUTHOR Linda K. Lawrie
2499 : // DATE WRITTEN September 1997
2500 :
2501 : // PURPOSE OF THIS FUNCTION:
2502 : // This function returns the internal pointer to Schedule "ScheduleName".
2503 4271 : auto const &s_sched = state.dataSched;
2504 :
2505 4271 : auto found = s_sched->dayScheduleMap.find(name);
2506 4271 : if (found == s_sched->dayScheduleMap.end()) {
2507 0 : return nullptr;
2508 : }
2509 :
2510 4271 : int daySchedNum = found->second;
2511 :
2512 4271 : auto *daySched = s_sched->daySchedules[daySchedNum];
2513 :
2514 4271 : daySched->isUsed = true;
2515 :
2516 4271 : return daySched;
2517 4271 : } // GetDaySchedule()
2518 :
2519 0 : int GetDayScheduleNum(EnergyPlusData &state, std::string const &name)
2520 : {
2521 0 : auto *daySched = GetDaySchedule(state, name);
2522 0 : return (daySched == nullptr) ? -1 : daySched->Num;
2523 : }
2524 :
2525 452 : void ScheduleConstant::setMinMaxVals([[maybe_unused]] EnergyPlusData &state)
2526 : {
2527 452 : assert(!isMinMaxSet);
2528 452 : minVal = maxVal = currentVal;
2529 452 : isMinMaxSet = true;
2530 452 : }
2531 :
2532 0 : std::vector<Real64> const &ScheduleConstant::getDayVals(EnergyPlusData &state, [[maybe_unused]] int jDay, [[maybe_unused]] int dayofWeek)
2533 : {
2534 0 : assert((int)tsVals.size() == Constant::iHoursInDay * state.dataGlobal->TimeStepsInHour);
2535 0 : return this->tsVals;
2536 : } // ScheduleConstant::getDayVals()
2537 :
2538 33269 : std::vector<Real64> const &ScheduleDetailed::getDayVals(EnergyPlusData &state, int jDay, int dayOfWeek)
2539 : {
2540 : // PURPOSE OF THIS SUBROUTINE:
2541 : // This subroutine returns an entire day's worth of schedule values.
2542 33269 : auto const &s_env = state.dataEnvrn;
2543 :
2544 : // Determine which Week Schedule is used
2545 33269 : auto const *weekSched = this->weekScheds[(jDay == -1) ? state.dataEnvrn->DayOfYear_Schedule : jDay];
2546 :
2547 33269 : DaySchedule *daySched = nullptr;
2548 : // Now, which day?
2549 33269 : if (dayOfWeek == -1) {
2550 28227 : daySched = weekSched->dayScheds[(s_env->HolidayIndex > 0) ? s_env->HolidayIndex : s_env->DayOfWeek];
2551 5042 : } else if (dayOfWeek <= 7 && s_env->HolidayIndex > 0) {
2552 5042 : daySched = weekSched->dayScheds[s_env->HolidayIndex];
2553 : } else {
2554 0 : daySched = weekSched->dayScheds[dayOfWeek];
2555 : }
2556 :
2557 33269 : return daySched->getDayVals(state);
2558 : } // ScheduleDetailed::getDayVals()
2559 :
2560 73538 : void ExternalInterfaceSetSchedule(EnergyPlusData &state,
2561 : int schedNum,
2562 : Real64 value // The new value for the schedule
2563 : )
2564 : {
2565 : // FUNCTION INFORMATION:
2566 : // AUTHOR Michael Wetter
2567 : // DATE WRITTEN February 2010
2568 :
2569 : // PURPOSE OF THIS SUBROUTINE:
2570 : // This subroutine sets all values of the schedule referenced by 'ScheduleIndex'
2571 : // to the value specified by 'Value'. The subroutine is used by the ExternalInterface to
2572 : // write real-time data into a schedule so that EnergyPlus modules can use
2573 : // real-time data by referencing a schedule. This allows overwriting setpoint
2574 : // for supervisory controls or internal gains obtained from real-time occupancy
2575 : // measurements.
2576 73538 : auto const &s_glob = state.dataGlobal;
2577 73538 : auto const &s_sched = state.dataSched;
2578 73538 : auto *daySched = s_sched->daySchedules[schedNum];
2579 :
2580 1838450 : for (int hr = 0; hr < Constant::iHoursInDay; ++hr) {
2581 8824560 : for (int ts = 0; ts < s_glob->TimeStepsInHour; ++ts) {
2582 7059648 : daySched->tsVals[hr * s_glob->TimeStepsInHour + ts] = value;
2583 : }
2584 : }
2585 73538 : } // ExternalInterfaceSetSchedule()
2586 :
2587 35222 : void ProcessIntervalFields(EnergyPlusData &state,
2588 : Array1S_string const Untils,
2589 : Array1S<Real64> const Numbers,
2590 : int const NumUntils,
2591 : int const NumNumbers,
2592 : std::array<Real64, Constant::iMinutesInDay> &minuteVals,
2593 : std::array<bool, Constant::iMinutesInDay> &setMinuteVals,
2594 : bool &ErrorsFound,
2595 : std::string const &DayScheduleName, // Name (used for errors)
2596 : std::string const &ErrContext, // Context (used for errors)
2597 : Interpolation interpolation // enumeration on how to interpolate values in schedule
2598 : )
2599 : {
2600 : // SUBROUTINE INFORMATION:
2601 : // AUTHOR <author>
2602 : // DATE WRITTEN <date_written>
2603 :
2604 : // PURPOSE OF THIS SUBROUTINE:
2605 : // This subroutine processes the "interval" fields with/without optional "until" in front of
2606 : // time (hh:mm).
2607 :
2608 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2609 : int HHField;
2610 : int MMField;
2611 :
2612 35222 : int begHr = 0; // starting hour
2613 35222 : int begMin = 0; // starting minute
2614 35222 : int endHr = -1; // ending hour
2615 35222 : int endMin = -1; // ending minute
2616 : std::string::size_type sFld;
2617 :
2618 : int totalMinutes;
2619 : Real64 incrementPerMinute;
2620 : Real64 curValue;
2621 :
2622 35222 : std::fill(minuteVals.begin(), minuteVals.end(), 0.0);
2623 35222 : std::fill(setMinuteVals.begin(), setMinuteVals.end(), false);
2624 :
2625 35222 : sFld = 0;
2626 :
2627 35222 : Real64 StartValue = 0;
2628 35222 : Real64 EndValue = 0;
2629 :
2630 35222 : if (NumUntils != NumNumbers) {
2631 0 : ShowSevereError(state,
2632 0 : format("ProcessScheduleInput: ProcessIntervalFields, number of Time fields does not match number of value fields, {}={}",
2633 : ErrContext,
2634 : DayScheduleName));
2635 0 : ErrorsFound = true;
2636 0 : return;
2637 : }
2638 :
2639 120361 : for (int Count = 1; Count <= NumUntils; ++Count) {
2640 85139 : std::string const &until = Untils(Count);
2641 85139 : int Pos = index(until, "UNTIL");
2642 85139 : if (Pos == 0) {
2643 80378 : if (until[5] == ':') {
2644 80356 : sFld = 6;
2645 : } else {
2646 22 : sFld = 5;
2647 : }
2648 80378 : DecodeHHMMField(state, until.substr(sFld), HHField, MMField, ErrorsFound, DayScheduleName, until, interpolation);
2649 4761 : } else if (Pos == (int)std::string::npos) {
2650 4761 : DecodeHHMMField(state, until, HHField, MMField, ErrorsFound, DayScheduleName, until, interpolation);
2651 : } else { // Until found but wasn't first field
2652 0 : ShowSevereError(state, format("ProcessScheduleInput: ProcessIntervalFields, Invalid \"Until\" field encountered={}", until));
2653 0 : ShowContinueError(state, format("Occurred in Day Schedule={}", DayScheduleName));
2654 0 : ErrorsFound = true;
2655 0 : continue;
2656 : }
2657 : // Field decoded
2658 85139 : if (HHField < 0 || HHField > Constant::iHoursInDay || MMField < 0 || MMField > Constant::iMinutesInHour) {
2659 0 : ShowSevereError(state, format("ProcessScheduleInput: ProcessIntervalFields, Invalid \"Until\" field encountered={}", until));
2660 0 : ShowContinueError(state, format("Occurred in Day Schedule={}", DayScheduleName));
2661 0 : ErrorsFound = true;
2662 0 : continue;
2663 : }
2664 85139 : if (HHField == Constant::iHoursInDay && MMField > 0 && MMField < Constant::iMinutesInHour) {
2665 0 : ShowWarningError(state, format("ProcessScheduleInput: ProcessIntervalFields, Invalid \"Until\" field encountered={}", Untils(Count)));
2666 0 : ShowContinueError(state, format("Occurred in Day Schedule={}", DayScheduleName));
2667 0 : ShowContinueError(state, "Terminating the field at 24:00");
2668 0 : MMField = 0;
2669 : }
2670 :
2671 : // Fill in values
2672 85139 : if (MMField == 0) {
2673 84295 : endHr = HHField - 1;
2674 84295 : endMin = Constant::iMinutesInHour - 1;
2675 844 : } else if (MMField < Constant::iMinutesInHour) {
2676 844 : endHr = HHField;
2677 844 : endMin = MMField - 1;
2678 : }
2679 :
2680 85139 : if (interpolation == Interpolation::Linear) {
2681 0 : totalMinutes = (endHr - begHr) * Constant::iMinutesInHour + (endMin - begMin) + 1;
2682 0 : if (totalMinutes == 0) {
2683 0 : totalMinutes = 1; // protect future division
2684 : }
2685 0 : if (Count == 1) {
2686 0 : StartValue = Numbers(Count); // assume first period is flat
2687 0 : EndValue = Numbers(Count);
2688 : } else {
2689 0 : StartValue = EndValue;
2690 0 : EndValue = Numbers(Count);
2691 : }
2692 0 : incrementPerMinute = (EndValue - StartValue) / totalMinutes;
2693 0 : curValue = StartValue + incrementPerMinute;
2694 : }
2695 :
2696 85139 : if (begHr > endHr) {
2697 8 : if (begHr == endHr + 1 && begMin == 0 && endMin == Constant::iMinutesInHour - 1) {
2698 16 : ShowWarningError(state,
2699 16 : format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, zero time interval detected, {}={}",
2700 : ErrContext,
2701 : DayScheduleName));
2702 : } else {
2703 0 : ShowSevereError(state,
2704 0 : format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, overlapping times detected, {}={}",
2705 : ErrContext,
2706 : DayScheduleName));
2707 0 : ErrorsFound = true;
2708 : }
2709 :
2710 85131 : } else if (begHr == endHr) {
2711 1546603 : for (int iMin = begMin; iMin <= endMin; ++iMin) {
2712 1520728 : if (setMinuteVals[begHr * Constant::iMinutesInHour + iMin] == true) {
2713 0 : ShowSevereError(
2714 : state,
2715 0 : format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, overlapping times detected, {}={}",
2716 : ErrContext,
2717 : DayScheduleName));
2718 0 : ErrorsFound = true;
2719 0 : goto UntilLoop_exit;
2720 : }
2721 : }
2722 :
2723 25875 : if (interpolation == Interpolation::Linear) {
2724 0 : for (int iMin = begMin; iMin <= endMin; ++iMin) {
2725 0 : minuteVals[begHr * Constant::iMinutesInHour + iMin] = curValue;
2726 0 : curValue += incrementPerMinute;
2727 0 : setMinuteVals[begHr * Constant::iMinutesInHour + iMin] = true;
2728 : }
2729 : } else {
2730 1546603 : for (int iMin = begMin; iMin <= endMin; ++iMin) {
2731 1520728 : minuteVals[begHr * Constant::iMinutesInHour + iMin] = Numbers(Count);
2732 1520728 : setMinuteVals[begHr * Constant::iMinutesInHour + iMin] = true;
2733 : }
2734 : }
2735 :
2736 25875 : begMin = endMin + 1;
2737 25875 : if (begMin >= Constant::iMinutesInHour) {
2738 25183 : ++begHr;
2739 25183 : begMin = 0;
2740 : }
2741 :
2742 : } else { // begHr < endHr
2743 59256 : if (interpolation == Interpolation::Linear) {
2744 0 : for (int iMin = begMin; iMin <= Constant::iMinutesInHour - 1; ++iMin) { // for portion of starting hour
2745 0 : minuteVals[begHr * Constant::iMinutesInHour + iMin] = curValue;
2746 0 : curValue += incrementPerMinute;
2747 0 : setMinuteVals[begHr * Constant::iMinutesInHour + iMin] = true;
2748 : }
2749 :
2750 0 : for (int iHr = begHr + 1; iHr <= endHr - 1; ++iHr) { // for intermediate hours
2751 0 : for (int iMin = 0; iMin <= Constant::iMinutesInHour - 1; ++iMin) {
2752 0 : minuteVals[iHr * Constant::iMinutesInHour + iMin] = curValue;
2753 0 : curValue += incrementPerMinute;
2754 0 : setMinuteVals[iHr * Constant::iMinutesInHour + iMin] = true;
2755 : }
2756 : }
2757 :
2758 0 : for (int iMin = 0; iMin <= endMin; ++iMin) { // for ending hour
2759 0 : minuteVals[endHr * Constant::iMinutesInHour + iMin] = curValue;
2760 0 : curValue += incrementPerMinute;
2761 0 : setMinuteVals[endHr * Constant::iMinutesInHour + iMin] = true;
2762 : }
2763 :
2764 : } else { // either no interpolation or "average" interpolation (average just is when the interval does not match the timestep)
2765 : // Fill values for first hour (which may not start at minute 0)
2766 : // For std::fill the end marker has to be 1 past the last position you want to fill
2767 3659890 : for (int iMin = begMin; iMin <= Constant::iMinutesInHour; ++iMin) {
2768 3600634 : minuteVals[begHr * Constant::iMinutesInHour + iMin] = Numbers(Count);
2769 3600634 : setMinuteVals[begHr * Constant::iMinutesInHour + iMin] = true;
2770 : }
2771 :
2772 : // Fill values for middle hours (which start at minute 0 and end in minute 59)
2773 59256 : if ((begHr + 1) <= (endHr - 1)) {
2774 756171 : for (int iHr = begHr + 1; iHr <= endHr - 1; ++iHr) {
2775 42808885 : for (int iMin = 0; iMin <= Constant::iMinutesInHour - 1; ++iMin) {
2776 42107100 : minuteVals[iHr * Constant::iMinutesInHour + iMin] = Numbers(Count);
2777 42107100 : setMinuteVals[iHr * Constant::iMinutesInHour + iMin] = true;
2778 : }
2779 : }
2780 : }
2781 :
2782 : // Fill values for last hour (which starts at minute 0 but may end on minute that isn't 59)
2783 3609730 : for (int iMin = 0; iMin <= endMin; ++iMin) {
2784 3550474 : minuteVals[endHr * Constant::iMinutesInHour + iMin] = Numbers(Count);
2785 3550474 : setMinuteVals[endHr * Constant::iMinutesInHour + iMin] = true;
2786 : }
2787 : }
2788 :
2789 59256 : begHr = endHr;
2790 59256 : begMin = endMin + 1;
2791 59256 : if (begMin >= Constant::iMinutesInHour) {
2792 59104 : ++begHr;
2793 59104 : begMin = 0;
2794 : }
2795 : }
2796 : }
2797 35222 : UntilLoop_exit:;
2798 :
2799 50754902 : for (int iMin = 0; iMin < Constant::iMinutesInDay; ++iMin) {
2800 50719680 : if (setMinuteVals[iMin] == false) {
2801 0 : ShowSevereError(state,
2802 0 : format("ProcessScheduleInput: ProcessIntervalFields, Processing time fields, incomplete day detected, {}={}",
2803 : ErrContext,
2804 : DayScheduleName));
2805 0 : ErrorsFound = true;
2806 : }
2807 : }
2808 : }
2809 :
2810 85139 : void DecodeHHMMField(EnergyPlusData &state,
2811 : std::string const &FieldValue, // Input field value
2812 : int &RetHH, // Returned "hour"
2813 : int &RetMM, // Returned "minute"
2814 : bool &ErrorsFound, // True if errors found in this field
2815 : std::string const &DayScheduleName, // originating day schedule name
2816 : std::string const &FullFieldValue, // Full Input field value
2817 : Interpolation interpolation // enumeration on how to interpolate values in schedule
2818 : )
2819 : {
2820 : // SUBROUTINE INFORMATION:
2821 : // AUTHOR Linda K Lawrie
2822 : // DATE WRITTEN January 2003
2823 :
2824 : // PURPOSE OF THIS SUBROUTINE:
2825 :
2826 : // This subroutine decodes a hhmm date field input as part of the "until" time in a schedule
2827 : // representation.
2828 :
2829 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2830 : Real64 rRetHH; // real Returned "hour"
2831 85139 : std::string hHour;
2832 85139 : std::string mMinute;
2833 :
2834 85139 : std::string String = stripped(FieldValue);
2835 85139 : std::string::size_type const Pos = index(String, ':');
2836 85139 : bool nonIntegral = false;
2837 :
2838 85139 : auto const &s_glob = state.dataGlobal;
2839 85139 : if (Pos == std::string::npos) {
2840 0 : ShowSevereError(state,
2841 0 : format("ProcessScheduleInput: DecodeHHMMField, Invalid \"until\" field submitted (no : separator in hh:mm)={}",
2842 0 : stripped(FullFieldValue)));
2843 0 : ShowContinueError(state, format("Occurred in Day Schedule={}", DayScheduleName));
2844 0 : ErrorsFound = true;
2845 0 : return;
2846 85139 : } else if (Pos == 0) {
2847 0 : RetHH = 0;
2848 : } else {
2849 85139 : bool error = false;
2850 85139 : Real64 rRetHH = Util::ProcessNumber(String.substr(0, Pos), error);
2851 85139 : RetHH = int(rRetHH);
2852 85139 : if (double(RetHH) != rRetHH || error || rRetHH < 0.0) {
2853 0 : if (double(RetHH) != rRetHH && rRetHH >= 0.0) {
2854 0 : ShowWarningError(state,
2855 0 : format("ProcessScheduleInput: DecodeHHMMField, Invalid \"until\" field submitted (non-integer numeric in HH)={}",
2856 0 : stripped(FullFieldValue)));
2857 0 : ShowContinueError(state, format("Other errors may result. Occurred in Day Schedule={}", DayScheduleName));
2858 0 : nonIntegral = true;
2859 : } else {
2860 0 : ShowSevereError(state,
2861 0 : format("ProcessScheduleInput: DecodeHHMMField, Invalid \"until\" field submitted (invalid numeric in HH)={}",
2862 0 : stripped(FullFieldValue)));
2863 0 : ShowContinueError(
2864 0 : state, format("Field values must be integer and represent hours:minutes. Occurred in Day Schedule={}", DayScheduleName));
2865 0 : ErrorsFound = true;
2866 0 : return;
2867 : }
2868 : }
2869 : }
2870 :
2871 85139 : String.erase(0, Pos + 1);
2872 85139 : bool error = false;
2873 85139 : Real64 rRetMM = Util::ProcessNumber(String, error);
2874 85139 : RetMM = int(rRetMM);
2875 85139 : if (double(RetMM) != rRetMM || error || rRetMM < 0.0) {
2876 0 : if (double(RetMM) != rRetMM && rRetMM >= 0.0) {
2877 0 : ShowWarningError(state,
2878 0 : format("ProcessScheduleInput: DecodeHHMMField, Invalid \"until\" field submitted (non-integer numeric in MM)={}",
2879 0 : stripped(FullFieldValue)));
2880 0 : ShowContinueError(state, format("Other errors may result. Occurred in Day Schedule={}", DayScheduleName));
2881 0 : nonIntegral = true;
2882 : } else {
2883 0 : ShowSevereError(state,
2884 0 : format("ProcessScheduleInput: DecodeHHMMField, Invalid \"until\" field submitted (invalid numeric in MM)={}",
2885 0 : stripped(FullFieldValue)));
2886 0 : ShowContinueError(state,
2887 0 : format("Field values must be integer and represent hours:minutes. Occurred in Day Schedule={}", DayScheduleName));
2888 0 : ErrorsFound = true;
2889 0 : return;
2890 : }
2891 : }
2892 :
2893 85139 : if (nonIntegral) {
2894 0 : std::string hHour; // these haven't been initialized?
2895 0 : std::string mMinute;
2896 0 : ShowContinueError(state, format("Until value to be used will be: {:2.2F}:{:2.2F}", hHour, mMinute));
2897 0 : }
2898 85139 : if (interpolation == Interpolation::No) {
2899 83817 : if (!isMinuteMultipleOfTimestep(RetMM, s_glob->MinutesInTimeStep)) {
2900 82 : ShowWarningError(
2901 : state,
2902 82 : format(
2903 : "ProcessScheduleInput: DecodeHHMMField, Invalid \"until\" field value is not a multiple of the minutes for each timestep: {}",
2904 82 : stripped(FullFieldValue)));
2905 41 : ShowContinueError(state, format("Other errors may result. Occurred in Day Schedule={}", DayScheduleName));
2906 : }
2907 : }
2908 85139 : }
2909 :
2910 83817 : bool isMinuteMultipleOfTimestep(int minute, int numMinutesPerTimestep)
2911 : {
2912 83817 : if (minute != 0) {
2913 252 : return (minute % numMinutesPerTimestep == 0);
2914 : } else {
2915 83565 : return true;
2916 : }
2917 : }
2918 :
2919 34957 : void ProcessForDayTypes(EnergyPlusData &state,
2920 : std::string const &ForDayField, // Field containing the "FOR:..."
2921 : std::array<bool, (int)DayType::Num> &these, // Array to contain returned "true" days
2922 : std::array<bool, (int)DayType::Num> &already, // Array of days already done
2923 : bool &ErrorsFound // Will be true if error found.
2924 : )
2925 : {
2926 : // SUBROUTINE INFORMATION:
2927 : // AUTHOR Linda K. Lawrie
2928 : // DATE WRITTEN February 2003
2929 :
2930 : // PURPOSE OF THIS SUBROUTINE:
2931 : // This subroutine processes a field "For: day types" and returns
2932 : // those day types (can be multiple) from field.
2933 : // Argument array dimensioning
2934 :
2935 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2936 34957 : bool OneValid = false;
2937 34957 : bool DupAssignment = false;
2938 :
2939 : // Just test for specific days
2940 34957 : if (has(ForDayField, "WEEKDAY")) {
2941 4910 : these[iDayType_Mon] = these[iDayType_Tue] = these[iDayType_Wed] = these[iDayType_Thu] = these[iDayType_Fri] = true;
2942 4910 : if (already[iDayType_Mon] || already[iDayType_Tue] || already[iDayType_Wed] || already[iDayType_Thu] || already[iDayType_Fri]) {
2943 0 : DupAssignment = true;
2944 : }
2945 4910 : already[iDayType_Mon] = already[iDayType_Tue] = already[iDayType_Wed] = already[iDayType_Thu] = already[iDayType_Fri] = true;
2946 4910 : OneValid = true;
2947 : }
2948 34957 : if (has(ForDayField, "MONDAY")) { // Should this be an else-if
2949 106 : these[iDayType_Mon] = true;
2950 106 : if (already[iDayType_Mon]) {
2951 0 : DupAssignment = true;
2952 : } else {
2953 106 : already[iDayType_Mon] = true;
2954 : }
2955 106 : OneValid = true;
2956 : }
2957 34957 : if (has(ForDayField, "TUESDAY")) {
2958 144 : these[iDayType_Tue] = true;
2959 144 : if (already[iDayType_Tue]) {
2960 0 : DupAssignment = true;
2961 : } else {
2962 144 : already[iDayType_Tue] = true;
2963 : }
2964 144 : OneValid = true;
2965 : }
2966 34957 : if (has(ForDayField, "WEDNESDAY")) {
2967 88 : these[iDayType_Wed] = true;
2968 88 : if (already[iDayType_Wed]) {
2969 0 : DupAssignment = true;
2970 : } else {
2971 88 : already[iDayType_Wed] = true;
2972 : }
2973 88 : OneValid = true;
2974 : }
2975 34957 : if (has(ForDayField, "THURSDAY")) {
2976 88 : these[iDayType_Thu] = true;
2977 88 : if (already[iDayType_Thu]) {
2978 0 : DupAssignment = true;
2979 : } else {
2980 88 : already[iDayType_Thu] = true;
2981 : }
2982 88 : OneValid = true;
2983 : }
2984 34957 : if (has(ForDayField, "FRIDAY")) {
2985 126 : these[iDayType_Fri] = true;
2986 126 : if (already[iDayType_Fri]) {
2987 0 : DupAssignment = true;
2988 : } else {
2989 126 : already[iDayType_Fri] = true;
2990 : }
2991 126 : OneValid = true;
2992 : }
2993 34957 : if (has(ForDayField, "WEEKEND")) {
2994 2077 : these[iDayType_Sun] = these[iDayType_Sat] = true;
2995 2077 : if (already[iDayType_Sun] || already[iDayType_Sat]) {
2996 0 : DupAssignment = true;
2997 : }
2998 2077 : already[iDayType_Sun] = already[iDayType_Sat] = true;
2999 2077 : OneValid = true;
3000 : }
3001 :
3002 34957 : if (has(ForDayField, "SATURDAY")) {
3003 1715 : these[iDayType_Sat] = true;
3004 1715 : if (already[iDayType_Sat]) {
3005 0 : DupAssignment = true;
3006 : } else {
3007 1715 : already[iDayType_Sat] = true;
3008 : }
3009 1715 : OneValid = true;
3010 : }
3011 34957 : if (has(ForDayField, "SUNDAY")) {
3012 1204 : these[iDayType_Sun] = true;
3013 1204 : if (already[iDayType_Sun]) {
3014 0 : DupAssignment = true;
3015 : } else {
3016 1204 : already[iDayType_Sun] = true;
3017 : }
3018 1204 : OneValid = true;
3019 : }
3020 34957 : if (has(ForDayField, "CUSTOMDAY1")) {
3021 1786 : these[iDayType_Cus1] = true;
3022 1786 : if (already[iDayType_Cus1]) {
3023 0 : DupAssignment = true;
3024 : } else {
3025 1786 : already[iDayType_Cus1] = true;
3026 : }
3027 1786 : OneValid = true;
3028 : }
3029 34957 : if (has(ForDayField, "CUSTOMDAY2")) {
3030 1768 : these[iDayType_Cus2] = true;
3031 1768 : if (already[iDayType_Cus2]) {
3032 0 : DupAssignment = true;
3033 : } else {
3034 1768 : already[iDayType_Cus2] = true;
3035 : }
3036 1768 : OneValid = true;
3037 : }
3038 34957 : if (has(ForDayField, "ALLDAY")) {
3039 247730 : for (int iDay = 0; iDay < (int)DayType::Num; ++iDay) {
3040 230035 : these[iDay] = true;
3041 230035 : if (already[iDay]) {
3042 0 : DupAssignment = true;
3043 : } else {
3044 230035 : already[iDay] = true;
3045 : }
3046 : }
3047 17695 : OneValid = true;
3048 : }
3049 34957 : if (has(ForDayField, "HOLIDAY")) {
3050 3248 : these[iDayType_Hol] = true;
3051 3248 : if (already[iDayType_Hol]) {
3052 0 : DupAssignment = true;
3053 : } else {
3054 3248 : already[iDayType_Hol] = true;
3055 : }
3056 3248 : OneValid = true;
3057 : }
3058 34957 : if (has(ForDayField, "SUMMER")) {
3059 4850 : these[iDayType_SumDes] = true;
3060 4850 : if (already[iDayType_SumDes]) {
3061 0 : DupAssignment = true;
3062 : } else {
3063 4850 : already[iDayType_SumDes] = true;
3064 : }
3065 4850 : OneValid = true;
3066 : }
3067 34957 : if (has(ForDayField, "WINTER")) {
3068 4562 : these[iDayType_WinDes] = true;
3069 4562 : if (already[iDayType_WinDes]) {
3070 0 : DupAssignment = true;
3071 : } else {
3072 4562 : already[iDayType_WinDes] = true;
3073 : }
3074 4562 : OneValid = true;
3075 : }
3076 34957 : if (has(ForDayField, "ALLOTHERDAY")) {
3077 53326 : for (int iDay = 0; iDay < (int)DayType::Num; ++iDay) {
3078 49517 : if (!already[iDay]) {
3079 21562 : these[iDay] = already[iDay] = true;
3080 : }
3081 : }
3082 3809 : OneValid = true;
3083 : }
3084 :
3085 34957 : if (DupAssignment) {
3086 0 : ShowSevereError(state, format("ProcessForDayTypes: Duplicate assignment attempted in \"for\" days field={}", ForDayField));
3087 0 : ErrorsFound = true;
3088 : }
3089 34957 : if (!OneValid) {
3090 0 : ShowSevereError(state, format("ProcessForDayTypes: No valid day assignments found in \"for\" days field={}", ForDayField));
3091 0 : ErrorsFound = true;
3092 : }
3093 34957 : } // ProcessScheduleInput()
3094 :
3095 74051 : void DaySchedule::setMinMaxVals([[maybe_unused]] EnergyPlusData &state)
3096 : {
3097 74051 : assert(!this->isMinMaxSet);
3098 :
3099 74051 : auto const &s_glob = state.dataGlobal;
3100 :
3101 74051 : this->minVal = this->maxVal = this->tsVals[0];
3102 8609435 : for (int i = 0; i < Constant::iHoursInDay * s_glob->TimeStepsInHour; ++i) {
3103 8535384 : Real64 value = this->tsVals[i];
3104 8535384 : if (value < this->minVal) {
3105 11694 : this->minVal = value;
3106 8523690 : } else if (value > this->maxVal) {
3107 257760 : this->maxVal = value;
3108 : }
3109 : }
3110 :
3111 74051 : this->isMinMaxSet = true;
3112 74051 : }
3113 :
3114 62370 : void WeekSchedule::setMinMaxVals(EnergyPlusData &state)
3115 : {
3116 62370 : assert(!this->isMinMaxSet);
3117 :
3118 62370 : auto *daySched1 = this->dayScheds[1];
3119 62370 : if (!daySched1->isMinMaxSet) {
3120 62176 : daySched1->setMinMaxVals(state);
3121 : }
3122 :
3123 62370 : this->minVal = daySched1->minVal;
3124 62370 : this->maxVal = daySched1->maxVal;
3125 :
3126 62370 : auto *daySchedPrev = daySched1;
3127 748440 : for (int iDay = 2; iDay < (int)DayType::Num; ++iDay) {
3128 686070 : auto *daySched = this->dayScheds[iDay];
3129 686070 : if (daySched == daySchedPrev) {
3130 661086 : continue;
3131 : }
3132 :
3133 24984 : if (!daySched->isMinMaxSet) {
3134 11861 : daySched->setMinMaxVals(state);
3135 : }
3136 24984 : if (daySched->minVal < this->minVal) {
3137 1805 : this->minVal = daySched->minVal;
3138 : }
3139 24984 : if (daySched->maxVal > this->maxVal) {
3140 4525 : this->maxVal = daySched->maxVal;
3141 : }
3142 24984 : daySchedPrev = daySched;
3143 : }
3144 :
3145 62370 : this->isMinMaxSet = true;
3146 62370 : } // ScheduleWeek::setMinMaxVals()
3147 :
3148 16085 : void ScheduleDetailed::setMinMaxVals(EnergyPlusData &state)
3149 : {
3150 16085 : assert(!this->isMinMaxSet);
3151 :
3152 16085 : auto *weekSched1 = this->weekScheds[1];
3153 16085 : if (!weekSched1->isMinMaxSet) {
3154 16081 : weekSched1->setMinMaxVals(state);
3155 : }
3156 :
3157 16085 : this->minVal = weekSched1->minVal;
3158 16085 : this->maxVal = weekSched1->maxVal;
3159 :
3160 16085 : auto *weekSchedPrev = weekSched1;
3161 :
3162 5887110 : for (int iWeek = 2; iWeek <= 366; ++iWeek) {
3163 5871025 : auto *weekSched = this->weekScheds[iWeek];
3164 5871025 : if (iWeek == 366 && weekSched == nullptr) {
3165 0 : continue;
3166 : }
3167 5871025 : if (weekSched == weekSchedPrev) {
3168 5824733 : continue;
3169 : }
3170 46292 : if (!weekSched->isMinMaxSet) {
3171 46289 : weekSched->setMinMaxVals(state);
3172 : }
3173 :
3174 46292 : if (weekSched->minVal < this->minVal) {
3175 1141 : this->minVal = weekSched->minVal;
3176 : }
3177 46292 : if (weekSched->maxVal > this->maxVal) {
3178 2721 : this->maxVal = weekSched->maxVal;
3179 : }
3180 46292 : weekSchedPrev = weekSched;
3181 : }
3182 :
3183 16085 : this->isMinMaxSet = true;
3184 16085 : }
3185 :
3186 0 : bool CheckScheduleValueMin(EnergyPlusData &state,
3187 : int const schedNum, // Which Schedule being tested
3188 : Clusive clu,
3189 : Real64 const min // Minimum desired value
3190 : )
3191 : {
3192 : // FUNCTION INFORMATION:
3193 : // AUTHOR Linda K. Lawrie
3194 : // DATE WRITTEN February 2003
3195 :
3196 : // PURPOSE OF THIS FUNCTION:
3197 : // This function checks the indicated schedule values for validity.
3198 :
3199 : // METHODOLOGY EMPLOYED:
3200 : // Schedule data structure stores this on first validity check. If there, then is returned else
3201 : // looks up minimum and maximum values for the schedule and then sets result of function based on
3202 : // requested minimum/maximum checks.
3203 :
3204 0 : return state.dataSched->schedules[schedNum]->checkMinVal(state, clu, min);
3205 : }
3206 :
3207 22849 : bool ScheduleBase::checkMinVal(EnergyPlusData &state, Clusive clu, Real64 min)
3208 : {
3209 22849 : if (!this->isMinMaxSet) { // Set Minimum/Maximums for this schedule
3210 1949 : this->setMinMaxVals(state);
3211 : }
3212 :
3213 : // Min/max for schedule has been set. Test.
3214 22849 : return (clu == Clusive::In) ? (FLT_EPSILON >= min - this->minVal) : (this->minVal > min);
3215 : } // ScheduleDetailed::checkMinVal()
3216 :
3217 2676 : bool ScheduleBase::checkMaxVal(EnergyPlusData &state, Clusive cluMax, Real64 const max)
3218 : {
3219 2676 : if (!this->isMinMaxSet) {
3220 0 : this->setMinMaxVals(state);
3221 : }
3222 :
3223 2676 : return (cluMax == Clusive::Ex) ? (this->maxVal < max) : (this->maxVal - max <= FLT_EPSILON);
3224 : }
3225 :
3226 28134 : bool ScheduleBase::checkMinMaxVals(EnergyPlusData &state,
3227 : Clusive cluMin, // Minimum indicator ('>', '>=')
3228 : Real64 const min, // Minimum desired value
3229 : Clusive cluMax, // Maximum indicator ('<', ',=')
3230 : Real64 const max) // Maximum desired value
3231 : {
3232 : // FUNCTION INFORMATION:
3233 : // AUTHOR Linda K. Lawrie
3234 : // DATE WRITTEN February 2003
3235 :
3236 : // PURPOSE OF THIS FUNCTION:
3237 : // This function checks the indicated schedule values for validity.
3238 :
3239 : // METHODOLOGY EMPLOYED:
3240 : // Schedule data structure stores this on first validity check. If there, then is returned else
3241 : // looks up minimum and maximum values for the schedule and then sets result of function based on
3242 : // requested minimum/maximum checks.
3243 :
3244 28134 : if (!this->isMinMaxSet) {
3245 14186 : this->setMinMaxVals(state);
3246 : }
3247 :
3248 28134 : bool minOk = (cluMin == Clusive::Ex) ? (this->minVal > min) : (FLT_EPSILON >= min - this->minVal);
3249 28134 : bool maxOk = (cluMax == Clusive::Ex) ? (this->maxVal < max) : (this->maxVal - max <= FLT_EPSILON);
3250 :
3251 28134 : return (minOk && maxOk);
3252 : } // ScheduleBase::checkMinMaxVals()
3253 :
3254 0 : bool CheckScheduleValueMinMax(EnergyPlusData &state,
3255 : int const schedNum, // Which Schedule being tested
3256 : Clusive cluMin, // Minimum indicator ('>', '>=')
3257 : Real64 const min, // Minimum desired value
3258 : Clusive cluMax, // Maximum indicator ('<', ',=')
3259 : Real64 const max // Maximum desired value
3260 : )
3261 : {
3262 : // Wrapper for method
3263 0 : return state.dataSched->schedules[schedNum]->checkMinMaxVals(state, cluMin, min, cluMax, max);
3264 : } // CheckScheduleValueMinMax()
3265 :
3266 59 : bool ScheduleConstant::hasVal([[maybe_unused]] EnergyPlusData &state, Real64 const value) const
3267 : {
3268 59 : return value == this->currentVal;
3269 : } // ScheduleConstant::hasVal()
3270 :
3271 11769 : bool ScheduleDetailed::hasVal(EnergyPlusData &state, Real64 const value) const
3272 : {
3273 11769 : auto const &s_sched = state.dataSched;
3274 11769 : auto const &s_glob = state.dataGlobal;
3275 :
3276 : // These arrays make sure you don't check the same day or week schedule twice
3277 11769 : std::vector<bool> weekSchedChecked;
3278 11769 : weekSchedChecked.resize(s_sched->weekSchedules.size());
3279 11769 : std::fill(weekSchedChecked.begin(), weekSchedChecked.end(), false);
3280 :
3281 11769 : std::vector<bool> daySchedChecked;
3282 11769 : daySchedChecked.resize(s_sched->daySchedules.size());
3283 11769 : std::fill(daySchedChecked.begin(), daySchedChecked.end(), false);
3284 :
3285 4176483 : for (int iWeek = 1; iWeek <= 366; ++iWeek) {
3286 4165104 : auto const *weekSched = this->weekScheds[iWeek];
3287 4165104 : if (weekSchedChecked[weekSched->Num]) {
3288 4151397 : continue;
3289 : }
3290 :
3291 173513 : for (int iDay = 1; iDay < (int)DayType::Num; ++iDay) {
3292 160196 : auto const *daySched = weekSched->dayScheds[iDay];
3293 160196 : if (daySchedChecked[daySched->Num]) {
3294 144799 : continue;
3295 : }
3296 :
3297 1933429 : for (int i = 0; i < Constant::iHoursInDay * s_glob->TimeStepsInHour; ++i) {
3298 1918422 : if (daySched->tsVals[i] == value) {
3299 390 : return true;
3300 : }
3301 : }
3302 15007 : daySchedChecked[daySched->Num] = true;
3303 : }
3304 13317 : weekSchedChecked[weekSched->Num] = true;
3305 : }
3306 :
3307 11379 : return false;
3308 11769 : } // ScheduleDetailed::hasVal()
3309 :
3310 0 : bool CheckScheduleValue(EnergyPlusData &state,
3311 : int const schedNum, // Which Schedule being tested
3312 : Real64 const value // Actual desired value
3313 : )
3314 : {
3315 : // Method wrapper
3316 0 : return state.dataSched->schedules[schedNum]->hasVal(state, value);
3317 : }
3318 :
3319 0 : bool CheckDayScheduleMinValues(EnergyPlusData &state,
3320 : int const schedNum, // Which Day Schedule being tested
3321 : Clusive cluMin,
3322 : Real64 const min)
3323 : {
3324 : // Method wrapper
3325 0 : return state.dataSched->daySchedules[schedNum]->checkMinVal(state, cluMin, min);
3326 : } // CheckDayScheduleMinValues()
3327 :
3328 0 : bool ScheduleConstant::hasFractionalVal([[maybe_unused]] EnergyPlusData &state) const
3329 : {
3330 0 : return (this->currentVal > 0.0) && (this->currentVal < 1.0);
3331 : } // ScheduleYear::hasFractionalVal()
3332 :
3333 160 : bool ScheduleDetailed::hasFractionalVal(EnergyPlusData &state) const
3334 : {
3335 160 : auto const &s_sched = state.dataSched;
3336 160 : auto const &s_glob = state.dataGlobal;
3337 :
3338 : // These arrays make sure you don't check the same day or week schedule twice
3339 160 : std::vector<bool> weekSchedChecked;
3340 160 : weekSchedChecked.resize(s_sched->weekSchedules.size());
3341 160 : std::fill(weekSchedChecked.begin(), weekSchedChecked.end(), false);
3342 :
3343 160 : std::vector<bool> daySchedChecked;
3344 160 : daySchedChecked.resize(s_sched->daySchedules.size());
3345 160 : std::fill(daySchedChecked.begin(), daySchedChecked.end(), false);
3346 :
3347 58720 : for (int iWeek = 1; iWeek <= 366; ++iWeek) {
3348 58560 : auto const *weekSched = this->weekScheds[iWeek];
3349 58560 : if (weekSchedChecked[weekSched->Num]) {
3350 58350 : continue;
3351 : }
3352 :
3353 2730 : for (int iDay = 1; iDay < (int)DayType::Num; ++iDay) {
3354 2520 : auto const *daySched = weekSched->dayScheds[iDay];
3355 2520 : if (daySchedChecked[daySched->Num]) {
3356 2188 : continue;
3357 : }
3358 :
3359 38060 : for (int i = 0; i < Constant::iHoursInDay * s_glob->TimeStepsInHour; ++i) {
3360 37728 : if (daySched->tsVals[i] > 0.0 && daySched->tsVals[i] < 1.0) {
3361 0 : return true;
3362 : }
3363 : }
3364 332 : daySchedChecked[daySched->Num] = true;
3365 : }
3366 210 : weekSchedChecked[weekSched->Num] = true;
3367 : }
3368 :
3369 160 : return false;
3370 160 : } // ScheduleDetailed::hasFractionalVal()
3371 :
3372 332 : std::pair<Real64, Real64> ScheduleConstant::getMinMaxValsByDayType([[maybe_unused]] EnergyPlusData &state,
3373 : [[maybe_unused]] DayTypeGroup const days)
3374 : {
3375 332 : return std::make_pair(this->currentVal, this->currentVal);
3376 : } // ScheduleConstant::getMinMaxValsByDayType()
3377 :
3378 53692 : std::pair<Real64, Real64> ScheduleDetailed::getMinMaxValsByDayType(EnergyPlusData &state, DayTypeGroup const days)
3379 : {
3380 : // J. Glazer - March 2024
3381 : // finds the minimum and maximum for a specific set of day types for a given schedule
3382 53692 : constexpr std::array<std::array<bool, (int)DayType::Num>, (int)DayTypeGroup::Num> dayTypeFilters = {{
3383 : // Unused Sun Mon Tues Wed Thur Fri Sat Hol Summer Winter Cust1 Cust2
3384 : {false, false, true, true, true, true, true, false, false, false, false, false, false}, // Weekday
3385 : {false, true, false, false, false, false, false, true, true, false, false, false, false}, // WeekendHoliday
3386 : {false, false, false, false, false, false, false, false, false, true, false, false, false}, // SummerDesign
3387 : {false, false, false, false, false, false, false, false, false, false, true, false, false} // WinterDesign
3388 : }};
3389 :
3390 53692 : auto const &s_sched = state.dataSched;
3391 :
3392 53692 : if (!this->isMinMaxSet) {
3393 0 : this->setMinMaxVals(state);
3394 : }
3395 :
3396 53692 : if (!this->MaxMinByDayTypeSet[(int)days]) {
3397 :
3398 9592 : bool firstSet = true;
3399 9592 : std::array<bool, (int)DayType::Num> const &dayTypeFilter = dayTypeFilters[(int)days];
3400 :
3401 : // These arrays make sure you don't check the same day or week schedule twice
3402 9592 : std::vector<bool> weekSchedChecked;
3403 9592 : weekSchedChecked.resize(s_sched->weekSchedules.size());
3404 9592 : std::fill(weekSchedChecked.begin(), weekSchedChecked.end(), false);
3405 :
3406 9592 : std::vector<bool> daySchedChecked;
3407 9592 : daySchedChecked.resize(s_sched->daySchedules.size());
3408 9592 : std::fill(daySchedChecked.begin(), daySchedChecked.end(), false);
3409 :
3410 9592 : this->MinByDayType[(int)days] = this->MaxByDayType[(int)days] = 0.0;
3411 :
3412 3520264 : for (int iDay = 1; iDay <= 366; ++iDay) {
3413 3510672 : auto const *weekSched = this->weekScheds[iDay];
3414 3510672 : if (weekSchedChecked[weekSched->Num]) {
3415 3496864 : continue;
3416 : }
3417 :
3418 179504 : for (int jDayType = 1; jDayType < (int)DayType::Num; ++jDayType) {
3419 165696 : if (!dayTypeFilter[jDayType]) {
3420 131176 : continue;
3421 : }
3422 :
3423 34520 : auto *daySched = weekSched->dayScheds[jDayType];
3424 34520 : if (daySchedChecked[daySched->Num]) {
3425 20473 : continue;
3426 : }
3427 :
3428 14047 : if (!daySched->isMinMaxSet) {
3429 0 : daySched->setMinMaxVals(state);
3430 : }
3431 :
3432 14047 : if (firstSet) {
3433 9592 : this->MinByDayType[(int)days] = daySched->minVal;
3434 9592 : this->MaxByDayType[(int)days] = daySched->maxVal;
3435 9592 : firstSet = false;
3436 : } else {
3437 4455 : this->MinByDayType[(int)days] = min(this->MinByDayType[(int)days], daySched->minVal);
3438 4455 : this->MaxByDayType[(int)days] = max(this->MaxByDayType[(int)days], daySched->maxVal);
3439 : }
3440 :
3441 14047 : daySchedChecked[daySched->Num] = true;
3442 : }
3443 13808 : weekSchedChecked[weekSched->Num] = true;
3444 : }
3445 9592 : this->MaxMinByDayTypeSet[(int)days] = true;
3446 9592 : }
3447 107384 : return std::make_pair(this->MinByDayType[(int)days], this->MaxByDayType[(int)days]);
3448 : } // ScheduleDetailed::getMinMaxValsByDayType()
3449 :
3450 2799876 : void ReportScheduleVals(EnergyPlusData &state)
3451 : {
3452 : // SUBROUTINE INFORMATION:
3453 : // AUTHOR Linda Lawrie
3454 : // DATE WRITTEN February 2004
3455 :
3456 : // PURPOSE OF THIS SUBROUTINE:
3457 : // This subroutine puts the proper current schedule values into the "reporting"
3458 : // slot for later reporting.
3459 2799876 : auto const &s_sched = state.dataSched;
3460 :
3461 2799876 : if (s_sched->DoScheduleReportingSetup) { // CurrentModuleObject='Any Schedule'
3462 19699 : for (auto *sched : s_sched->schedules) {
3463 : // No variables for the built-in AlwaysOn and AlwaysOff schedules
3464 18896 : if (sched->Num == SchedNum_AlwaysOff || sched->Num == SchedNum_AlwaysOn) {
3465 1606 : continue;
3466 : }
3467 :
3468 : // Set Up Reporting
3469 34580 : SetupOutputVariable(state,
3470 : "Schedule Value",
3471 : Constant::Units::None,
3472 17290 : sched->currentVal,
3473 : OutputProcessor::TimeStepType::Zone,
3474 : OutputProcessor::StoreType::Average,
3475 17290 : sched->Name);
3476 803 : }
3477 803 : s_sched->DoScheduleReportingSetup = false;
3478 : }
3479 :
3480 2799876 : UpdateScheduleVals(state);
3481 2799876 : }
3482 :
3483 780 : void ReportOrphanSchedules(EnergyPlusData &state)
3484 : {
3485 : // SUBROUTINE INFORMATION:
3486 : // AUTHOR Linda Lawrie
3487 : // DATE WRITTEN April 2008
3488 :
3489 : // PURPOSE OF THIS SUBROUTINE:
3490 : // In response to CR7498, report orphan (unused) schedule items.
3491 :
3492 780 : bool NeedOrphanMessage = true;
3493 780 : bool NeedUseMessage = false;
3494 780 : int NumCount = 0;
3495 :
3496 780 : auto const &s_sched = state.dataSched;
3497 780 : auto const &s_glob = state.dataGlobal;
3498 :
3499 18666 : for (auto const *sched : s_sched->schedules) {
3500 17886 : if (sched->isUsed) {
3501 17180 : continue;
3502 : }
3503 706 : if (NeedOrphanMessage && s_glob->DisplayUnusedSchedules) {
3504 2 : ShowWarningError(state, "The following schedule names are \"Unused Schedules\". These schedules are in the idf");
3505 2 : ShowContinueError(state, " file but are never obtained by the simulation and therefore are NOT used.");
3506 1 : NeedOrphanMessage = false;
3507 : }
3508 706 : if (s_glob->DisplayUnusedSchedules) {
3509 1 : ShowMessage(state, format("Schedule:Year or Schedule:Compact or Schedule:File or Schedule:Constant={}", sched->Name));
3510 : } else {
3511 705 : ++NumCount;
3512 : }
3513 780 : }
3514 :
3515 780 : if (NumCount > 0) {
3516 95 : ShowMessage(state, format("There are {} unused schedules in input.", NumCount));
3517 95 : NeedUseMessage = true;
3518 : }
3519 :
3520 780 : NeedOrphanMessage = true;
3521 780 : NumCount = 0;
3522 :
3523 112878 : for (auto *weekSched : s_sched->weekSchedules) {
3524 112098 : if (weekSched->isUsed) {
3525 70853 : continue;
3526 : }
3527 41245 : if (weekSched->Name.empty()) {
3528 0 : continue;
3529 : }
3530 41245 : if (NeedOrphanMessage && s_glob->DisplayUnusedSchedules) {
3531 0 : ShowWarningError(state, "The following week schedule names are \"Unused Schedules\". These schedules are in the idf");
3532 0 : ShowContinueError(state, " file but are never obtained by the simulation and therefore are NOT used.");
3533 0 : NeedOrphanMessage = false;
3534 : }
3535 41245 : if (s_glob->DisplayUnusedSchedules) {
3536 0 : ShowMessage(state, format("Schedule:Week:Daily or Schedule:Week:Compact={}", weekSched->Name));
3537 : } else {
3538 41245 : ++NumCount;
3539 : }
3540 780 : }
3541 :
3542 780 : if (NumCount > 0) {
3543 2 : ShowMessage(state, fmt::format("There are {} unused week schedules in input.", NumCount));
3544 1 : NeedUseMessage = true;
3545 : }
3546 :
3547 780 : NeedOrphanMessage = true;
3548 780 : NumCount = 0;
3549 :
3550 123602 : for (auto *daySched : s_sched->daySchedules) {
3551 122822 : if (daySched->isUsed) {
3552 81571 : continue;
3553 : }
3554 41251 : if (daySched->Name.empty()) {
3555 0 : continue;
3556 : }
3557 41251 : if (NeedOrphanMessage && s_glob->DisplayUnusedSchedules) {
3558 0 : ShowWarningError(state, "The following day schedule names are \"Unused Schedules\". These schedules are in the idf");
3559 0 : ShowContinueError(state, " file but are never obtained by the simulation and therefore are NOT used.");
3560 0 : NeedOrphanMessage = false;
3561 : }
3562 :
3563 41251 : if (s_glob->DisplayUnusedSchedules) {
3564 0 : ShowMessage(state, format("Schedule:Day:Hourly or Schedule:Day:Interval or Schedule:Day:List={}", daySched->Name));
3565 : } else {
3566 41251 : ++NumCount;
3567 : }
3568 780 : }
3569 :
3570 780 : if (NumCount > 0) {
3571 4 : ShowMessage(state, format("There are {} unused day schedules in input.", NumCount));
3572 4 : NeedUseMessage = true;
3573 : }
3574 :
3575 780 : if (NeedUseMessage) {
3576 288 : ShowMessage(state, "Use Output:Diagnostics,DisplayUnusedSchedules; to see them.");
3577 : }
3578 780 : } // ReportOrphanSchedules()
3579 :
3580 : // returns the annual full load hours for a schedule - essentially the sum of the hourly values
3581 24 : Real64 ScheduleConstant::getAnnualHoursFullLoad([[maybe_unused]] EnergyPlusData &state,
3582 : int const StartDayOfWeek, // Day of week for start of year
3583 : bool const isLeapYear // true if it is a leap year containing February 29
3584 : )
3585 : {
3586 24 : if (StartDayOfWeek < iDayType_Sun || StartDayOfWeek > iDayType_Sat) {
3587 0 : return 0.0; // Assert this instead?
3588 : }
3589 :
3590 24 : int DaysInYear = (isLeapYear) ? 366 : 365;
3591 24 : return DaysInYear * Constant::iHoursInDay * this->currentVal;
3592 : }
3593 :
3594 : // returns the annual full load hours for a schedule - essentially the sum of the hourly values
3595 10648 : Real64 ScheduleDetailed::getAnnualHoursFullLoad(EnergyPlusData &state,
3596 : int const StartDayOfWeek, // Day of week for start of year
3597 : bool const isLeapYear // true if it is a leap year containing February 29
3598 : )
3599 : {
3600 : // J. Glazer - July 2017
3601 : // adapted from Linda K. Lawrie original code for ScheduleAverageHoursPerWeek()
3602 10648 : auto const &s_glob = state.dataGlobal;
3603 :
3604 10648 : int DaysInYear = (isLeapYear) ? 366 : 365;
3605 :
3606 10648 : int DayT = StartDayOfWeek;
3607 10648 : Real64 TotalHours = 0.0;
3608 :
3609 10648 : if (DayT < iDayType_Sun || DayT > iDayType_Sat) {
3610 0 : return TotalHours;
3611 : }
3612 :
3613 3897184 : for (int iDay = 1; iDay <= DaysInYear; ++iDay) {
3614 3886536 : auto const *weekSched = this->weekScheds[iDay];
3615 3886536 : auto const *daySched = weekSched->dayScheds[DayT];
3616 :
3617 3886536 : TotalHours += daySched->sumTsVals / double(s_glob->TimeStepsInHour);
3618 3886536 : ++DayT;
3619 3886536 : if (DayT > iDayType_Sat) {
3620 553712 : DayT = iDayType_Sun;
3621 : }
3622 : }
3623 :
3624 10648 : return TotalHours;
3625 : }
3626 :
3627 : // returns the average number of hours per week based on the schedule index provided
3628 4135 : Real64 Schedule::getAverageWeeklyHoursFullLoad(EnergyPlusData &state,
3629 : int const StartDayOfWeek, // Day of week for start of year
3630 : bool const isLeapYear // true if it is a leap year containing February 29
3631 : )
3632 : {
3633 : // FUNCTION INFORMATION:
3634 : // AUTHOR Linda K. Lawrie
3635 : // DATE WRITTEN August 2006
3636 : // MODIFIED September 2012; Glazer - CR8849
3637 :
3638 : // PURPOSE OF THIS FUNCTION:
3639 : // This function returns the "average" hours per week for a schedule over
3640 : // the entire year.
3641 :
3642 4135 : Real64 WeeksInYear = (isLeapYear) ? (366.0 / 7.0) : (365.0 / 7.0);
3643 4135 : return this->getAnnualHoursFullLoad(state, StartDayOfWeek, isLeapYear) / WeeksInYear;
3644 : }
3645 :
3646 : // returns the annual hours greater than 1% for a schedule - essentially the number of hours with any operation
3647 6516 : Real64 ScheduleDetailed::getAnnualHoursGreaterThan1Percent(EnergyPlusData &state,
3648 : int const StartDayOfWeek, // Day of week for start of year
3649 : bool const isItLeapYear // true if it is a leap year containing February 29
3650 : )
3651 : {
3652 : // J. Glazer - July 2017
3653 : // adapted from Linda K. Lawrie original code for ScheduleAverageHoursPerWeek()
3654 6516 : auto const &s_glob = state.dataGlobal;
3655 :
3656 6516 : int DaysInYear = (isItLeapYear) ? 366 : 365;
3657 :
3658 6516 : int DayT = StartDayOfWeek;
3659 6516 : Real64 TotalHours = 0.0;
3660 :
3661 6516 : if (DayT < iDayType_Sun || DayT > iDayType_Sat) {
3662 0 : return TotalHours;
3663 : }
3664 :
3665 2384867 : for (int iDay = 1; iDay <= DaysInYear; ++iDay) {
3666 2378351 : auto const *weekSched = this->weekScheds[iDay];
3667 2378351 : auto const *daySched = weekSched->dayScheds[DayT];
3668 294096167 : for (int i = 0; i < Constant::iHoursInDay * s_glob->TimeStepsInHour; ++i) {
3669 291717816 : if (daySched->tsVals[i] > 0.0) {
3670 213742489 : TotalHours += s_glob->TimeStepZone;
3671 : }
3672 : }
3673 :
3674 2378351 : ++DayT;
3675 2378351 : if (DayT > iDayType_Sat) {
3676 338843 : DayT = iDayType_Sun;
3677 : }
3678 : }
3679 :
3680 6516 : return TotalHours;
3681 : } // ScheduleDetailed::getAnnualHoursGreaterThan1Percent()
3682 :
3683 : // returns the annual hours greater than 1% for a schedule - essentially the number of hours with any operation
3684 21 : Real64 ScheduleConstant::getAnnualHoursGreaterThan1Percent([[maybe_unused]] EnergyPlusData &state,
3685 : int const StartDayOfWeek, // Day of week for start of year
3686 : bool const isItLeapYear // true if it is a leap year containing February 29
3687 : )
3688 : {
3689 21 : int DaysInYear = (isItLeapYear) ? 366 : 365;
3690 :
3691 21 : if (StartDayOfWeek < iDayType_Sun || StartDayOfWeek > iDayType_Sat) {
3692 0 : return 0.0; // Assert this instead?
3693 : }
3694 :
3695 21 : return (this->currentVal > 0.0) ? (Constant::rHoursInDay * DaysInYear) : 0;
3696 : } // ScheduleConstant::getHoursGreaterThan1Percent()
3697 :
3698 : // returns the temperature value from a schedule at a certain time for the first day of the week in either January or July
3699 : std::tuple<Real64, int, std::string>
3700 3568 : ScheduleDetailed::getValAndCountOnDay(EnergyPlusData &state, bool const isSummer, DayType const dayOfWeek, int const hourOfDay)
3701 : {
3702 : // J.Glazer - Aug 2017
3703 :
3704 3568 : auto const &s_glob = state.dataGlobal;
3705 :
3706 : // determine month to use based on hemiphere and season
3707 : int month;
3708 3568 : if (isSummer) {
3709 1766 : month = (state.dataEnvrn->Latitude > 0.) ? 7 : 1;
3710 : } else {
3711 1802 : month = (state.dataEnvrn->Latitude > 0.) ? 1 : 7;
3712 : }
3713 :
3714 3568 : std::string monthName = (month == 1) ? "January" : "July";
3715 :
3716 3568 : int jdateSelect = General::nthDayOfWeekOfMonth(state, (int)dayOfWeek, 1, month);
3717 :
3718 : // determine number of days in year
3719 3568 : int DaysInYear = (state.dataEnvrn->CurrentYearIsLeapYear) ? 366 : 365;
3720 :
3721 : // should adjust date if lands on a holiday but for now assume that it does not
3722 :
3723 : // adjust time of day for daylight savings time
3724 3568 : int hourSelect = hourOfDay + state.dataWeather->DSTIndex(jdateSelect);
3725 :
3726 : // get the value at the selected time
3727 3568 : int constexpr firstTimeStep = 1;
3728 3568 : auto const *weekSched = this->weekScheds[jdateSelect];
3729 3568 : auto const *daySched = weekSched->dayScheds[(int)dayOfWeek];
3730 :
3731 3568 : Real64 value = daySched->tsVals[(hourSelect - 1) * state.dataGlobal->TimeStepsInHour + (firstTimeStep - 1)];
3732 3568 : int countOfSame = 0;
3733 :
3734 : // count the number of times with that same value
3735 1305896 : for (int jdateOfYear = 1; jdateOfYear <= DaysInYear; ++jdateOfYear) {
3736 1302328 : auto const *wSched = this->weekScheds[jdateOfYear];
3737 1302328 : if (wSched == weekSched) { // if same week schedule can short circuit rest of testing and increment counter
3738 1291440 : ++countOfSame;
3739 1291440 : continue;
3740 : }
3741 :
3742 10888 : auto const *dSched = wSched->dayScheds[(int)dayOfWeek];
3743 10888 : if (dSched == daySched) { // if same day schedule can short circuit rest of testing and increment counter
3744 12 : ++countOfSame;
3745 12 : continue;
3746 : }
3747 :
3748 10876 : if (dSched->tsVals[(hourSelect - 1) * s_glob->TimeStepsInHour + (firstTimeStep - 1)] == value) {
3749 4310 : ++countOfSame;
3750 : }
3751 : }
3752 :
3753 7136 : return std::make_tuple(value, countOfSame, monthName);
3754 3568 : } // ScheduleDetailed::getValAndCountOnDay()
3755 :
3756 : // returns the temperature value from a schedule at a certain time for the first day of the week in either January or July
3757 18 : std::tuple<Real64, int, std::string> ScheduleConstant::getValAndCountOnDay(EnergyPlusData &state,
3758 : bool const isSummer,
3759 : [[maybe_unused]] DayType const dayOfWeek,
3760 : [[maybe_unused]] int const hourOfDay)
3761 : {
3762 : // determine month to use based on hemiphere and season
3763 : int month;
3764 18 : if (isSummer) {
3765 10 : month = (state.dataEnvrn->Latitude > 0.) ? 7 : 1;
3766 : } else {
3767 8 : month = (state.dataEnvrn->Latitude > 0.) ? 1 : 7;
3768 : }
3769 :
3770 18 : std::string monthName = (month == 1) ? "January" : "July";
3771 18 : int DaysInYear = (state.dataEnvrn->CurrentYearIsLeapYear) ? 366 : 365;
3772 36 : return std::make_tuple(this->currentVal, DaysInYear, monthName);
3773 18 : } // ScheduleConstant::getValAndCountOnDay()
3774 :
3775 0 : void ShowSevereBadMin(EnergyPlusData &state,
3776 : ErrorObjectHeader const &eoh,
3777 : std::string_view fieldName,
3778 : std::string_view fieldVal,
3779 : Clusive cluMin,
3780 : Real64 minVal,
3781 : std::string_view msg)
3782 : {
3783 0 : ShowSevereError(state, format("{}: {} = {}", eoh.routineName, eoh.objectType, eoh.objectName));
3784 0 : ShowContinueError(
3785 0 : state, format("{} = {}, schedule contains values that are {} {}", fieldName, fieldVal, cluMin == Clusive::In ? "<" : "<=", minVal));
3786 0 : if (!msg.empty()) {
3787 0 : ShowContinueError(state, format("{}", msg));
3788 : }
3789 0 : }
3790 :
3791 0 : void ShowSevereBadMax(EnergyPlusData &state,
3792 : ErrorObjectHeader const &eoh,
3793 : std::string_view fieldName,
3794 : std::string_view fieldVal,
3795 : Clusive cluMax,
3796 : Real64 maxVal,
3797 : std::string_view msg)
3798 : {
3799 0 : ShowSevereError(state, format("{}: {} = {}", eoh.routineName, eoh.objectType, eoh.objectName));
3800 0 : ShowContinueError(
3801 0 : state, format("{} = {}, schedule contains values that are {} {}", fieldName, fieldVal, cluMax == Clusive::In ? ">" : ">=", maxVal));
3802 0 : if (!msg.empty()) {
3803 0 : ShowContinueError(state, format("{}", msg));
3804 : }
3805 0 : }
3806 :
3807 0 : void ShowSevereBadMinMax(EnergyPlusData &state,
3808 : ErrorObjectHeader const &eoh,
3809 : std::string_view fieldName,
3810 : std::string_view fieldVal,
3811 : Clusive cluMin,
3812 : Real64 minVal,
3813 : Clusive cluMax,
3814 : Real64 maxVal,
3815 : std::string_view msg)
3816 : {
3817 0 : ShowSevereError(state, format("{}: {} = {}", eoh.routineName, eoh.objectType, eoh.objectName));
3818 0 : ShowContinueError(state,
3819 0 : format("{} = {}, schedule contains values that are {} {} and/or {} {}",
3820 : fieldName,
3821 : fieldVal,
3822 0 : cluMin == Clusive::In ? "<" : "<=",
3823 : minVal,
3824 0 : cluMax == Clusive::In ? ">" : ">=",
3825 : maxVal));
3826 0 : if (!msg.empty()) {
3827 0 : ShowContinueError(state, format("{}", msg));
3828 : }
3829 0 : }
3830 :
3831 0 : void ShowWarningBadMin(EnergyPlusData &state,
3832 : ErrorObjectHeader const &eoh,
3833 : std::string_view fieldName,
3834 : std::string_view fieldVal,
3835 : Clusive cluMin,
3836 : Real64 minVal,
3837 : std::string_view msg)
3838 : {
3839 0 : ShowWarningError(state, format("{}: {} = {}", eoh.routineName, eoh.objectType, eoh.objectName));
3840 0 : ShowContinueError(
3841 0 : state, format("{} = {}, schedule contains values that are {} {}", fieldName, fieldVal, cluMin == Clusive::In ? "<" : "<=", minVal));
3842 0 : if (!msg.empty()) {
3843 0 : ShowContinueError(state, format("{}", msg));
3844 : }
3845 0 : }
3846 :
3847 0 : void ShowWarningBadMax(EnergyPlusData &state,
3848 : ErrorObjectHeader const &eoh,
3849 : std::string_view fieldName,
3850 : std::string_view fieldVal,
3851 : Clusive cluMax,
3852 : Real64 maxVal,
3853 : std::string_view msg)
3854 : {
3855 0 : ShowWarningError(state, format("{}: {} = {}", eoh.routineName, eoh.objectType, eoh.objectName));
3856 0 : ShowContinueError(
3857 0 : state, format("{} = {}, schedule contains values that are {} {}", fieldName, fieldVal, cluMax == Clusive::In ? ">" : ">=", maxVal));
3858 0 : if (!msg.empty()) {
3859 0 : ShowContinueError(state, format("{}", msg));
3860 : }
3861 0 : }
3862 :
3863 0 : void ShowWarningBadMinMax(EnergyPlusData &state,
3864 : ErrorObjectHeader const &eoh,
3865 : std::string_view fieldName,
3866 : std::string_view fieldVal,
3867 : Clusive cluMin,
3868 : Real64 minVal,
3869 : Clusive cluMax,
3870 : Real64 maxVal,
3871 : std::string_view msg)
3872 : {
3873 0 : ShowWarningError(state, format("{}: {} = {}", eoh.routineName, eoh.objectType, eoh.objectName));
3874 0 : ShowContinueError(state,
3875 0 : format("{} = {}, schedule contains values that are {} {} and/or {} {}",
3876 : fieldName,
3877 : fieldVal,
3878 0 : cluMin == Clusive::In ? "<" : "<=",
3879 : minVal,
3880 0 : cluMax == Clusive::In ? ">" : ">=",
3881 : maxVal));
3882 0 : if (!msg.empty()) {
3883 0 : ShowContinueError(state, format("{}", msg));
3884 : }
3885 0 : }
3886 :
3887 : } // namespace Sched
3888 :
3889 : } // namespace EnergyPlus
|