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