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 : #ifndef ResultsSchema_hh_INCLUDED
49 : #define ResultsSchema_hh_INCLUDED
50 :
51 : #include <memory>
52 : #include <unordered_map>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array1D.hh>
56 : #include <ObjexxFCL/Array2D.hh>
57 :
58 : #include <nlohmann/json.hpp>
59 :
60 : // EnergyPlus Headers
61 : #include <EnergyPlus/Data/BaseData.hh>
62 : #include <EnergyPlus/DataGlobals.hh>
63 : #include <EnergyPlus/EnergyPlus.hh>
64 : #include <EnergyPlus/OutputProcessor.hh>
65 :
66 : namespace EnergyPlus {
67 :
68 : // Forward declarations
69 : class EnergyPlusFixture;
70 : class ResultsFrameworkFixture;
71 : struct EnergyPlusData;
72 : struct JsonOutputFilePaths;
73 :
74 : namespace ResultsFramework {
75 :
76 : using json = nlohmann::json;
77 :
78 : using OutputProcessor::ReportFreq;
79 : using OutputProcessor::TimeStepType;
80 :
81 : // trim string
82 : std::string trim(std::string_view const s);
83 :
84 : // base result object
85 : class BaseResultObject
86 : {
87 : public:
88 : BaseResultObject() = default;
89 : };
90 :
91 : class SimInfo : public BaseResultObject
92 : {
93 : public:
94 : void setProgramVersion(const std::string &programVersion);
95 : std::string getProgramVersion() const;
96 : void setSimulationEnvironment(const std::string &simulationEnvironment);
97 : void setInputModelURI(const std::string &inputModelURI);
98 : void setStartDateTimeStamp(const std::string &startDateTimeStamp);
99 : void setRunTime(const std::string &elapsedTime);
100 : void setNumErrorsWarmup(const std::string &numWarningsDuringWarmup, const std::string &numSevereDuringWarmup);
101 : void setNumErrorsSizing(const std::string &numWarningsDuringSizing, const std::string &numSevereDuringSizing);
102 : void setNumErrorsSummary(const std::string &numWarnings, const std::string &numSevere);
103 : json getJSON() const;
104 :
105 : protected:
106 : std::string ProgramVersion;
107 : std::string SimulationEnvironment;
108 : std::string InputModelURI;
109 : std::string StartDateTimeStamp;
110 : std::string RunTime;
111 : std::string NumWarningsDuringWarmup, NumSevereDuringWarmup, NumWarningsDuringSizing, NumSevereDuringSizing, NumWarnings, NumSevere;
112 : };
113 :
114 : class Variable : public BaseResultObject
115 : {
116 : public:
117 29549 : Variable() = default;
118 : Variable(const std::string &VarName,
119 : const ReportFreq reportFrequency,
120 : const OutputProcessor::TimeStepType timeStepType,
121 : const int ReportID,
122 : Constant::Units units);
123 : Variable(const std::string &VarName,
124 : const ReportFreq reportFrequency,
125 : const OutputProcessor::TimeStepType timeStepType,
126 : const int ReportID,
127 : Constant::Units units,
128 : const std::string &customUnits);
129 :
130 : std::string variableName() const;
131 : void setVariableName(const std::string &VarName);
132 :
133 : std::string sReportFrequency() const;
134 : ReportFreq iReportFrequency() const;
135 : void setReportFrequency(const ReportFreq reportFrequency);
136 :
137 : OutputProcessor::TimeStepType timeStepType() const;
138 : void setTimeStepType(const OutputProcessor::TimeStepType timeStepType);
139 :
140 : int reportID() const;
141 : void setReportID(const int Id);
142 :
143 : Constant::Units units() const;
144 : void setUnits(Constant::Units units);
145 :
146 : std::string customUnits() const;
147 : void setCustomUnits(const std::string &customUnits);
148 :
149 : void pushValue(const double val);
150 : double value(size_t index) const;
151 : size_t numValues() const;
152 :
153 : virtual json getJSON() const;
154 :
155 : protected:
156 : std::string m_varName;
157 : ReportFreq m_reportFreq = ReportFreq::EachCall;
158 : OutputProcessor::TimeStepType m_timeStepType = OutputProcessor::TimeStepType::Zone;
159 : int m_rptID = -1;
160 : Constant::Units m_units;
161 : std::string m_customUnits;
162 : std::vector<double> m_values;
163 : };
164 :
165 : class OutputVariable : public Variable
166 : {
167 : public:
168 : OutputVariable(const std::string &VarName,
169 : const ReportFreq reportFrequency,
170 : const OutputProcessor::TimeStepType timeStepType,
171 : const int ReportID,
172 : Constant::Units units);
173 :
174 : OutputVariable(const std::string &VarName,
175 : const ReportFreq reportFrequency,
176 : const OutputProcessor::TimeStepType timeStepType,
177 : const int ReportID,
178 : Constant::Units units,
179 : const std::string &customUnits);
180 : };
181 :
182 : class MeterVariable : public Variable
183 : {
184 : public:
185 0 : MeterVariable() = default;
186 : MeterVariable(const std::string &VarName,
187 : const ReportFreq reportFrequency,
188 : const int ReportID,
189 : Constant::Units units,
190 : const bool MeterOnly,
191 : const bool Acculumative = false);
192 :
193 : bool accumulative() const;
194 : void setAccumulative(bool state);
195 : bool meterOnly() const;
196 : void setMeterOnly(bool state);
197 :
198 : json getJSON() const override;
199 :
200 : protected:
201 : bool acc = false;
202 : bool meter_only = true;
203 : };
204 :
205 : class DataFrame : public BaseResultObject
206 : {
207 : public:
208 : typedef std::pair<int, Variable> VarPtrPair;
209 :
210 : explicit DataFrame(const std::string &ReportFreq);
211 12816 : virtual ~DataFrame() = default;
212 :
213 : void addVariable(Variable const &var);
214 :
215 : void setDataFrameEnabled(bool state);
216 :
217 : bool dataFrameEnabled() const;
218 :
219 : void setVariablesScanned(bool state);
220 :
221 : bool variablesScanned() const;
222 :
223 : void newRow(const int month, const int dayOfMonth, int hourOfDay, int curMin, int calendarYear);
224 : // void newRow(const std::string &ts);
225 : virtual void pushVariableValue(const int reportID, double value);
226 :
227 : Variable &lastVariable();
228 :
229 : json getVariablesJSON();
230 : json getJSON() const;
231 :
232 : void writeReport(JsonOutputFilePaths &jsonOutputFilePaths, bool outputJSON, bool outputCBOR, bool outputMsgPack);
233 :
234 : // Need to find a way to protect these, they can't be changed on the fly
235 : bool iso8601 = false;
236 : bool beginningOfInterval = false;
237 :
238 : protected:
239 : bool DataFrameEnabled = false;
240 : bool VariablesScanned = false;
241 : int lastHour = 0;
242 : int lastMinute = 0;
243 : std::string ReportFrequency;
244 : std::vector<std::string> TS;
245 : std::map<int, Variable> variableMap;
246 : int lastVarID = -1;
247 : };
248 :
249 : class MeterDataFrame : public DataFrame
250 : {
251 : public:
252 : // clang-format off
253 5607 : explicit MeterDataFrame(const std::string &ReportFreq) : DataFrame(ReportFreq) {};
254 : // clang-format on
255 5607 : virtual ~MeterDataFrame() = default;
256 :
257 : void addVariable(MeterVariable const &var);
258 :
259 : void pushVariableValue(const int reportID, double value) override;
260 :
261 : json getJSON(bool meterOnlyCheck = false) const;
262 :
263 : protected:
264 : std::map<int, MeterVariable> meterMap;
265 : };
266 :
267 : class Table : public BaseResultObject
268 : {
269 : public:
270 : std::string TableName;
271 : std::string FootnoteText;
272 : std::vector<std::string> ColHeaders;
273 : std::vector<std::string> RowHeaders;
274 : std::vector<std::vector<std::string>> Data;
275 :
276 : Table(Array2D_string const &body,
277 : Array1D_string const &rowLabels,
278 : Array1D_string const &columnLabels,
279 : std::string const &tableName,
280 : std::string const &footnoteText);
281 :
282 : json getJSON() const;
283 : };
284 :
285 : class Report : public BaseResultObject
286 : {
287 : public:
288 : std::string ReportName;
289 : std::string ReportForString;
290 : std::vector<Table> Tables;
291 :
292 : json getJSON() const;
293 : };
294 :
295 : class ReportsCollection : public BaseResultObject
296 : {
297 : public:
298 : typedef std::pair<std::string, Report> RptPtrPair;
299 :
300 : ReportsCollection();
301 :
302 : void addReportTable(Array2D_string const &body,
303 : Array1D_string const &rowLabels,
304 : Array1D_string const &columnLabels,
305 : std::string const &reportName,
306 : std::string const &reportForString,
307 : std::string const &tableName);
308 :
309 : void addReportTable(Array2D_string const &body,
310 : Array1D_string const &rowLabels,
311 : Array1D_string const &columnLabels,
312 : std::string const &reportName,
313 : std::string const &reportForString,
314 : std::string const &tableName,
315 : std::string const &footnoteText);
316 :
317 : json getJSON() const;
318 :
319 : protected:
320 : std::unordered_map<std::string, Report> reportsMap;
321 : Report rpt;
322 : };
323 :
324 : class CSVWriter : public BaseResultObject
325 : {
326 : public:
327 : CSVWriter() = default;
328 24 : explicit CSVWriter(std::size_t num_output_variables) : outputVariableIndices(std::vector<bool>(num_output_variables, false))
329 : {
330 12 : }
331 :
332 : void writeOutput(EnergyPlusData &state,
333 : std::vector<std::string> const &outputVariables,
334 : InputOutputFile &outputFile,
335 : bool outputControl,
336 : bool rewriteTimestamp);
337 : void parseTSOutputs(EnergyPlusData &state, json const &data, std::vector<std::string> const &outputVariables, ReportFreq reportingFrequency);
338 :
339 : private:
340 : friend class EnergyPlus::EnergyPlusFixture;
341 : friend class EnergyPlus::ResultsFrameworkFixture;
342 :
343 : char s[129] = {0};
344 : ReportFreq smallestReportFreq = ReportFreq::Year;
345 : std::map<std::string, std::vector<std::string>> outputs;
346 : std::vector<bool> outputVariableIndices;
347 :
348 : static std::string &convertToMonth(std::string &datetime);
349 : void updateReportFreq(ReportFreq reportingFrequency);
350 : // void readRVI();
351 : // void readMVI();
352 : };
353 :
354 : class ResultsFramework : public BaseResultObject
355 : {
356 : public:
357 5607 : ResultsFramework() = default;
358 :
359 1602 : virtual ~ResultsFramework() = default;
360 :
361 : void setupOutputOptions(EnergyPlusData &state);
362 :
363 : bool timeSeriesEnabled() const;
364 :
365 : bool timeSeriesAndTabularEnabled() const;
366 :
367 : bool JSONEnabled() const;
368 :
369 : bool CBOREnabled() const;
370 :
371 : bool MsgPackEnabled() const;
372 :
373 : void initializeTSDataFrame(const ReportFreq reportFrequency,
374 : const std::vector<OutputProcessor::OutVar *> &Variables,
375 : const OutputProcessor::TimeStepType timeStepType = OutputProcessor::TimeStepType::Zone);
376 :
377 : void initializeMeters(const std::vector<OutputProcessor::Meter *> &EnergyMeters, const ReportFreq reportFrequency);
378 :
379 : std::array<DataFrame, (int)TimeStepType::Num> detailedTSData = {// DataFrame("Dummy"),
380 : DataFrame("Detailed-Zone"),
381 : DataFrame("Detailed-HVAC")};
382 :
383 : std::array<DataFrame, (int)ReportFreq::Num> freqTSData = {DataFrame("Each Call"),
384 : DataFrame("TimeStep"),
385 : DataFrame("Hourly"),
386 : DataFrame("Daily"),
387 : DataFrame("Monthly"),
388 : DataFrame("RunPeriod"),
389 : DataFrame("Yearly")};
390 :
391 : std::array<MeterDataFrame, (int)ReportFreq::Num> Meters = {MeterDataFrame("Each Call"),
392 : MeterDataFrame("TimeStep"),
393 : MeterDataFrame("Hourly"),
394 : MeterDataFrame("Daily"),
395 : MeterDataFrame("Monthly"),
396 : MeterDataFrame("RunPeriod"),
397 : MeterDataFrame("Yearly")};
398 :
399 1 : void setISO8601(const bool value)
400 : {
401 1 : rewriteTimestamp = !value;
402 3 : for (int iTimeStep = (int)TimeStepType::Zone; iTimeStep < (int)TimeStepType::Num; ++iTimeStep) {
403 2 : detailedTSData[iTimeStep].iso8601 = value;
404 : }
405 :
406 7 : for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
407 6 : freqTSData[iFreq].iso8601 = Meters[iFreq].iso8601 = value;
408 : }
409 1 : }
410 :
411 1 : void setBeginningOfInterval(const bool value)
412 : {
413 3 : for (int iTimeStep = 0; iTimeStep < (int)TimeStepType::Num; ++iTimeStep) {
414 2 : detailedTSData[iTimeStep].beginningOfInterval = value;
415 : }
416 :
417 8 : for (int iFreq = 0; iFreq < (int)ReportFreq::Num; ++iFreq) {
418 7 : freqTSData[iFreq].beginningOfInterval = Meters[iFreq].beginningOfInterval = value;
419 : }
420 1 : }
421 :
422 : void writeOutputs(EnergyPlusData &state);
423 :
424 : void addReportVariable(std::string_view const keyedValue,
425 : std::string_view const variableName,
426 : std::string_view const units,
427 : ReportFreq const reportingInterval);
428 :
429 : void addReportMeter(std::string const &meter, std::string_view const units, ReportFreq const reportingInterval);
430 :
431 : SimInfo SimulationInformation;
432 :
433 : std::vector<std::string> MDD;
434 : std::vector<std::string> RDD;
435 : ReportsCollection TabularReportsCollection;
436 :
437 : protected:
438 : bool tsEnabled = false;
439 : bool tsAndTabularEnabled = false;
440 : bool outputJSON = false;
441 : bool outputCBOR = false;
442 : bool outputMsgPack = false;
443 : bool rewriteTimestamp = true; // Convert monthly data timestamp to month name
444 : std::vector<std::string> outputVariables;
445 :
446 : void writeTimeSeriesReports(JsonOutputFilePaths &jsonOutputFilePaths);
447 :
448 : void writeReport(JsonOutputFilePaths &jsonOutputFilePaths);
449 :
450 : void writeCSVOutput(EnergyPlusData &state);
451 :
452 : private:
453 : friend class EnergyPlus::EnergyPlusFixture;
454 : friend class EnergyPlus::ResultsFrameworkFixture;
455 :
456 : protected:
457 32 : inline bool hasDetailedTSData(TimeStepType timeStepType) const
458 : {
459 32 : return detailedTSData[(int)timeStepType].dataFrameEnabled();
460 : }
461 :
462 60 : inline bool hasFreqTSData(ReportFreq freq) const
463 : {
464 60 : return freqTSData[(int)freq].dataFrameEnabled();
465 : }
466 :
467 : #ifdef GET_OUT
468 : inline bool hasRIDetailedZoneTSData() const
469 : {
470 : return detailedTSData[(int)TimeStepType::Zone].iDataFrameEnabled() || detailedTSData[(int)TimeStepType::Zone].rDataFrameEnabled();
471 : };
472 :
473 : inline bool hasRIDetailedHVACTSData() const
474 : {
475 : return detailedTSData[(int)TimeStepType::System].iDataFrameEnabled() || detailedTSData[(int)TimeStepType::System].rDataFrameEnabled();
476 : };
477 :
478 : // This API can be condensed in an obvious way
479 : inline bool hasRITimestepTSData() const
480 : {
481 : return freqTSData[(int)ReportFreq::TimeStep].iDataFrameEnabled() || freqTSData[(int)ReportFreq::TimeStep].rDataFrameEnabled();
482 : };
483 :
484 : inline bool hasRIHourlyTSData() const
485 : {
486 : return freqTSData[(int)ReportFreq::Hour].iDataFrameEnabled() || freqTSData[(int)ReportFreq::Hour].rDataFrameEnabled();
487 : };
488 :
489 : inline bool hasRIDailyTSData() const
490 : {
491 : return freqTSData[(int)ReportFreq::Day].iDataFrameEnabled() || freqTSData[(int)ReportFreq::Day].rDataFrameEnabled();
492 : };
493 :
494 : inline bool hasRIMonthlyTSData() const
495 : {
496 : return freqTSData[(int)ReportFreq::Month].iDataFrameEnabled() || freqTSData[(int)ReportFreq::Month].rDataFrameEnabled();
497 : };
498 :
499 : inline bool hasRIRunPeriodTSData() const
500 : {
501 : return freqTSData[(int)ReportFreq::Simulation].iDataFrameEnabled() || freqTSData[(int)ReportFreq::Simulation].rDataFrameEnabled();
502 : };
503 :
504 : inline bool hasRIYearlyTSData() const
505 : {
506 : return freqTSData[(int)ReportFreq::Year].iDataFrameEnabled() || freqTSData[(int)ReportFreq::Year].rDataFrameEnabled();
507 : };
508 :
509 : inline bool hasTSMeters() const
510 : {
511 : return Meters[(int)ReportFreq::TimeStep].dataFrameEnabled();
512 : };
513 :
514 : inline bool hasHRMeters() const
515 : {
516 : return Meters[(int)ReportFreq::Hour].dataFrameEnabled();
517 : };
518 :
519 : inline bool hasDYMeters() const
520 : {
521 : return Meters[(int)ReportFreq::Day].dataFrameEnabled();
522 : };
523 :
524 : inline bool hasMNMeters() const
525 : {
526 : return Meters[(int)ReportFreq::Month].dataFrameEnabled();
527 : };
528 :
529 : inline bool hasSMMeters() const
530 : {
531 : return Meters[(int)ReportFreq::Simulation].dataFrameEnabled();
532 : };
533 :
534 : inline bool hasYRMeters() const
535 : {
536 : return Meters[(int)ReportFreq::Year].dataFrameEnabled();
537 : };
538 :
539 : #endif //
540 86 : inline bool hasMeters(ReportFreq freq) const
541 : {
542 86 : return Meters[(int)freq].dataFrameEnabled();
543 : }
544 :
545 6 : inline bool hasMeterData() const
546 : {
547 12 : return hasMeters(ReportFreq::TimeStep) || hasMeters(ReportFreq::Hour) || hasMeters(ReportFreq::Day) || hasMeters(ReportFreq::Month) ||
548 12 : hasMeters(ReportFreq::Simulation) || hasMeters(ReportFreq::Year);
549 : };
550 :
551 36 : inline bool hasTSData(ReportFreq freq, TimeStepType timeStepType = TimeStepType::Invalid) const
552 : {
553 36 : assert(freq != ReportFreq::Invalid && (freq != ReportFreq::EachCall || timeStepType != TimeStepType::Invalid));
554 36 : return (freq == ReportFreq::EachCall) ? detailedTSData[(int)timeStepType].dataFrameEnabled() : freqTSData[(int)freq].dataFrameEnabled();
555 : };
556 :
557 6 : inline bool hasAnyTSData() const
558 : {
559 12 : for (int iTimeStep = 0; iTimeStep < (int)TimeStepType::Num; ++iTimeStep) {
560 10 : if (detailedTSData[iTimeStep].dataFrameEnabled()) {
561 4 : return true;
562 : }
563 : }
564 3 : for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
565 3 : if (freqTSData[iFreq].dataFrameEnabled()) {
566 2 : return true;
567 : }
568 : }
569 0 : return false;
570 : };
571 :
572 6 : inline bool hasOutputData() const
573 : {
574 6 : return hasAnyTSData() || hasMeterData();
575 : };
576 : };
577 :
578 : } // namespace ResultsFramework
579 :
580 : struct ResultsFrameworkData : BaseGlobalStruct
581 : {
582 :
583 : std::unique_ptr<ResultsFramework::ResultsFramework> resultsFramework = std::make_unique<ResultsFramework::ResultsFramework>();
584 :
585 801 : void init_constant_state([[maybe_unused]] EnergyPlusData &state) override
586 : {
587 801 : }
588 :
589 801 : void init_state([[maybe_unused]] EnergyPlusData &state) override
590 : {
591 801 : }
592 :
593 0 : void clear_state() override
594 : {
595 : using OutputProcessor::ReportFreq;
596 0 : for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
597 0 : auto &meters = this->resultsFramework->Meters[iFreq];
598 0 : meters.setDataFrameEnabled(false);
599 0 : meters.setVariablesScanned(false);
600 : }
601 0 : }
602 : };
603 :
604 : } // namespace EnergyPlus
605 :
606 : #endif
|