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 <algorithm>
50 : #include <cassert>
51 : #include <memory>
52 : #include <string>
53 : #include <unordered_set>
54 :
55 : // ObjexxFCL Headers
56 : #include <ObjexxFCL/Array.functions.hh>
57 : #include <ObjexxFCL/Fmath.hh>
58 : #include <ObjexxFCL/string.functions.hh>
59 :
60 : // EnergyPlus Headers
61 : #include "re2/re2.h"
62 : #include <EnergyPlus/Data/EnergyPlusData.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataGlobalConstants.hh>
65 : #include <EnergyPlus/DataHVACGlobals.hh>
66 : #include <EnergyPlus/DataIPShortCuts.hh>
67 : #include <EnergyPlus/DataOutputs.hh>
68 : #include <EnergyPlus/DataStringGlobals.hh>
69 : #include <EnergyPlus/DataSystemVariables.hh>
70 : #include <EnergyPlus/General.hh>
71 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
72 : #include <EnergyPlus/OutputProcessor.hh>
73 : #include <EnergyPlus/OutputReportPredefined.hh>
74 : #include <EnergyPlus/ResultsFramework.hh>
75 : #include <EnergyPlus/SQLiteProcedures.hh>
76 : #include <EnergyPlus/ScheduleManager.hh>
77 : #include <EnergyPlus/UtilityRoutines.hh>
78 :
79 : #include <fmt/ostream.h>
80 : #include <milo/dtoa.h>
81 :
82 : namespace EnergyPlus {
83 :
84 : namespace OutputProcessor {
85 :
86 : // MODULE INFORMATION:
87 : // AUTHOR Linda Lawrie
88 : // DATE WRITTEN December 1998
89 : // MODIFIED na
90 : // RE-ENGINEERED na
91 :
92 : // PURPOSE OF THIS MODULE:
93 : // This module contains the major Output Processor routines.
94 : // In addition, in this file are several routines which can be called
95 : // without using the OutputProcessor Module
96 :
97 : // METHODOLOGY EMPLOYED:
98 : // Lots of pointers and other fancy data stuff. (I didn't see a single pointer here)
99 :
100 : // Routines tagged on the end of this module:
101 : // AddDDOutVar
102 : // GenOutputVariablesAuditReport
103 : // GetCurrentMeterValue
104 : // GetInstantMeterValue
105 : // GetInternalVariableValue
106 : // GetInternalVariableValueExternalInterface
107 : // GetMeteredVariables
108 : // GetMeterIndex
109 : // GetMeterResourceType
110 : // GetNumMeteredVariables
111 : // GetVariableKeyCountandType
112 : // GetVariableKeys
113 : // InitPollutionMeterReporting
114 : // ProduceRDDMDD
115 : // ReportingThisVariable
116 : // SetInitialMeterReportingAndOutputNames
117 : // SetupOutputVariable
118 : // UpdateDataandReport
119 : // UpdateMeterReporting
120 :
121 : // Functions
122 :
123 91 : int DetermineMinuteForReporting(EnergyPlusData const &state)
124 : {
125 :
126 : // FUNCTION INFORMATION:
127 : // AUTHOR Linda Lawrie
128 : // DATE WRITTEN January 2012
129 : // MODIFIED na
130 : // RE-ENGINEERED na
131 :
132 : // PURPOSE OF THIS FUNCTION:
133 : // When reporting peaks, minutes are used but not necessarily easily calculated.
134 :
135 91 : Real64 constexpr FracToMin(60.0);
136 91 : return ((state.dataGlobal->CurrentTime + state.dataHVACGlobal->SysTimeElapsed) - int(state.dataGlobal->CurrentTime)) * FracToMin;
137 : }
138 :
139 875 : void InitializeOutput(EnergyPlusData &state)
140 : {
141 : // SUBROUTINE INFORMATION:
142 : // AUTHOR Linda K. Lawrie
143 : // DATE WRITTEN December 1998
144 :
145 : // PURPOSE OF THIS SUBROUTINE:
146 : // This subroutine initializes the OutputProcessor data structures.
147 :
148 875 : auto &op = state.dataOutputProcessor;
149 :
150 : // Initialize end use category names - the indices must match up with endUseNames in OutputReportTabular
151 875 : op->EndUseCategory.allocate((int)Constant::EndUse::Num);
152 875 : op->EndUseCategory((int)Constant::EndUse::Heating + 1).Name = "Heating";
153 875 : op->EndUseCategory((int)Constant::EndUse::Cooling + 1).Name = "Cooling";
154 875 : op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).Name = "InteriorLights";
155 875 : op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).Name = "ExteriorLights";
156 875 : op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).Name = "InteriorEquipment";
157 875 : op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).Name = "ExteriorEquipment";
158 875 : op->EndUseCategory((int)Constant::EndUse::Fans + 1).Name = "Fans";
159 875 : op->EndUseCategory((int)Constant::EndUse::Pumps + 1).Name = "Pumps";
160 875 : op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).Name = "HeatRejection";
161 875 : op->EndUseCategory((int)Constant::EndUse::Humidification + 1).Name = "Humidifier";
162 875 : op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).Name = "HeatRecovery";
163 875 : op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).Name = "WaterSystems";
164 875 : op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).Name = "Refrigeration";
165 875 : op->EndUseCategory((int)Constant::EndUse::Cogeneration + 1).Name = "Cogeneration";
166 :
167 : // Initialize display names for output table - this could go away if end use key names are changed to match
168 875 : op->EndUseCategory((int)Constant::EndUse::Heating + 1).DisplayName = "Heating";
169 875 : op->EndUseCategory((int)Constant::EndUse::Cooling + 1).DisplayName = "Cooling";
170 875 : op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).DisplayName = "Interior Lighting";
171 875 : op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).DisplayName = "Exterior Lighting";
172 875 : op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).DisplayName = "Interior Equipment";
173 875 : op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).DisplayName = "Exterior Equipment";
174 875 : op->EndUseCategory((int)Constant::EndUse::Fans + 1).DisplayName = "Fans";
175 875 : op->EndUseCategory((int)Constant::EndUse::Pumps + 1).DisplayName = "Pumps";
176 875 : op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).DisplayName = "Heat Rejection";
177 875 : op->EndUseCategory((int)Constant::EndUse::Humidification + 1).DisplayName = "Humidification";
178 875 : op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).DisplayName = "Heat Recovery";
179 875 : op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).DisplayName = "Water Systems";
180 875 : op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).DisplayName = "Refrigeration";
181 875 : op->EndUseCategory((int)Constant::EndUse::Cogeneration + 1).DisplayName = "Generators";
182 :
183 875 : op->OutputInitialized = true;
184 :
185 875 : op->TimeStepZoneSec = double(state.dataGlobal->MinutesInTimeStep) * 60.0;
186 :
187 1750 : state.files.mtd.ensure_open(state, "InitializeMeters", state.files.outputControl.mtd);
188 875 : } // InitializeOutput()
189 :
190 6960 : void addEndUseSubcategory(EnergyPlusData &state, EndUseCat endUseCat, std::string_view const endUseSubName)
191 : {
192 : // SUBROUTINE INFORMATION:
193 : // AUTHOR Peter Graham Ellis
194 : // DATE WRITTEN February 2006
195 :
196 : // PURPOSE OF THIS SUBROUTINE:
197 : // This subroutine manages the list of subcategories for each end-use category.
198 :
199 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
200 6960 : auto &op = state.dataOutputProcessor;
201 :
202 6960 : Constant::EndUse endUse = endUseCat2endUse[(int)endUseCat];
203 6960 : if (endUse == Constant::EndUse::Invalid) {
204 34 : ShowSevereError(state, format("Nonexistent end use passed to addEndUseSpaceType={}", endUseCatNames[(int)endUseCat]));
205 34 : return;
206 : }
207 :
208 6926 : auto &endUseCategory = op->EndUseCategory((int)endUse + 1);
209 :
210 6998 : for (int EndUseSubNum = 1; EndUseSubNum <= endUseCategory.NumSubcategories; ++EndUseSubNum) {
211 5759 : if (Util::SameString(endUseCategory.SubcategoryName(EndUseSubNum), endUseSubName)) {
212 5687 : return; // Subcategory already exists, no further action required
213 : }
214 : }
215 :
216 : // Add the subcategory by reallocating the array
217 1239 : endUseCategory.SubcategoryName.redimension(++endUseCategory.NumSubcategories);
218 1239 : endUseCategory.SubcategoryName(endUseCategory.NumSubcategories) = endUseSubName;
219 :
220 1239 : if (endUseCategory.NumSubcategories > op->MaxNumSubcategories) {
221 40 : op->MaxNumSubcategories = endUseCategory.NumSubcategories;
222 : }
223 : } // addEndUseSubcategory()
224 :
225 221 : void addEndUseSpaceType(EnergyPlusData &state, OutputProcessor::EndUseCat sovEndUseCat, std::string_view const EndUseSpaceTypeName)
226 : {
227 221 : auto &op = state.dataOutputProcessor;
228 :
229 221 : Constant::EndUse endUse = endUseCat2endUse[(int)sovEndUseCat];
230 :
231 221 : if (endUse == Constant::EndUse::Invalid) {
232 0 : ShowSevereError(state, format("Nonexistent end use passed to addEndUseSpaceType={}", endUseCatNames[(int)sovEndUseCat]));
233 0 : return;
234 : }
235 :
236 221 : auto &endUseCat = op->EndUseCategory((int)endUse + 1);
237 :
238 221 : for (int endUseSpTypeNum = 1; endUseSpTypeNum <= endUseCat.numSpaceTypes; ++endUseSpTypeNum) {
239 126 : if (Util::SameString(endUseCat.spaceTypeName(endUseSpTypeNum), EndUseSpaceTypeName)) {
240 126 : return; // SpaceType already exists, no further action required
241 : }
242 : }
243 :
244 : // Add the space type by reallocating the array
245 95 : endUseCat.spaceTypeName.redimension(++endUseCat.numSpaceTypes);
246 95 : endUseCat.spaceTypeName(endUseCat.numSpaceTypes) = EndUseSpaceTypeName;
247 :
248 95 : if (endUseCat.numSpaceTypes > op->maxNumEndUseSpaceTypes) {
249 0 : op->maxNumEndUseSpaceTypes = endUseCat.numSpaceTypes;
250 : }
251 : } // addEndUseSpaceType()
252 :
253 238 : void SetupTimePointers(EnergyPlusData &state,
254 : TimeStepType const timeStep, // Which timestep is being set up, 'Zone'=1, 'HVAC'=2
255 : Real64 &TimeStep // The timestep variable. Used to get the address
256 : )
257 : {
258 :
259 : // SUBROUTINE INFORMATION:
260 : // AUTHOR Linda K. Lawrie
261 : // DATE WRITTEN December 1998
262 : // MODIFIED na
263 : // RE-ENGINEERED na
264 :
265 : // PURPOSE OF THIS SUBROUTINE:
266 : // This subroutine sets up the derived type for the output processor that
267 : // contains pointers to the TimeStep values used in the simulation.
268 :
269 : // METHODOLOGY EMPLOYED:
270 : // Indicate that the TimeStep passed in is a target for the pointer
271 : // attributes in the derived types.
272 :
273 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
274 : // ValidateTimeStepType will throw a Fatal if not valid
275 238 : if (state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep != nullptr) {
276 0 : ShowFatalError(state, format("SetupTimePointers was already called for {}", timeStepTypeNames[(int)timeStep]));
277 : }
278 238 : state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep = &TimeStep;
279 238 : }
280 :
281 176370 : void CheckReportVariable(EnergyPlusData &state, std::string_view const Name, std::string const &Key, std::vector<int> &reqVarList)
282 : {
283 :
284 : // SUBROUTINE INFORMATION:
285 : // AUTHOR Linda K. Lawrie
286 : // DATE WRITTEN December 1998
287 :
288 : // PURPOSE OF THIS SUBROUTINE:
289 : // This subroutine will get the report variable information from input and
290 : // determine if this variable (KeyedValue and VariableName) should be reported
291 : // and, if so, what frequency to report.
292 :
293 : // This routine is called when SetupOutputVariable is called with no "optional"
294 : // Reporting Frequency. It is expected that SetupOutputVariable would only be
295 : // called once for each keyed variable to be triggered for output (from the input
296 : // requests). The optional report frequency would only be used for debugging
297 : // purposes. Therefore, this routine will collect all occasions where this
298 : // passed variablename would be reported from the requested input. It builds
299 : // a list of these requests (ReportList) so that the calling routine can propagate
300 : // the requests into the correct data structure.
301 :
302 : // METHODOLOGY EMPLOYED:
303 : // This instance being requested will always have a key associated with it. Matching
304 : // instances (from input) may or may not have keys, but only one instance of a reporting
305 : // frequency per variable is allowed. ReportList will be populated with ReqRepVars indices
306 : // of those extra things from input that satisfy this condition.
307 :
308 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
309 :
310 : // Make sure that input has been read
311 176370 : GetReportVariableInput(state);
312 :
313 176370 : auto const &op = state.dataOutputProcessor;
314 :
315 670922 : for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
316 494552 : auto *reqVar = op->reqVars[iReqVar];
317 :
318 494552 : if (!Util::SameString(reqVar->name, Name)) {
319 488300 : continue;
320 : }
321 :
322 6970 : if (!reqVar->key.empty() && !(reqVar->is_simple_string && Util::SameString(reqVar->key, Key)) &&
323 6970 : !(!reqVar->is_simple_string && RE2::FullMatch(std::string{Key}, *(reqVar->case_insensitive_pattern)))) {
324 699 : continue;
325 : }
326 :
327 : // A match. Make sure doesn't duplicate
328 5553 : reqVar->Used = true;
329 5553 : bool Dup = false;
330 : // op->ReportList is allocated to a large value, so we can't use a std::find_if on it (why not?)
331 5629 : for (int iReqVar2 : reqVarList) {
332 77 : if (op->reqVars[iReqVar2]->freq == reqVar->freq && op->reqVars[iReqVar2]->sched == reqVar->sched) {
333 1 : Dup = true;
334 1 : break;
335 : }
336 5553 : }
337 :
338 5553 : if (!Dup) {
339 5552 : reqVarList.push_back(iReqVar);
340 : }
341 : }
342 176370 : }
343 :
344 : constexpr std::array<std::string_view, (int)ReportFreq::Num> reportingFrequencyNoticeStrings = {
345 : " !Each Call", // EachCall
346 : " !TimeStep", // TimeStep
347 : " !Hourly", // Hourly
348 : " !Daily [Value,Min,Hour,Minute,Max,Hour,Minute]", // Daily
349 : " !Monthly [Value,Min,Day,Hour,Minute,Max,Day,Hour,Minute]", // Monthly
350 : " !RunPeriod [Value,Min,Month,Day,Hour,Minute,Max,Month,Day,Hour,Minute]", // Simulation
351 : " !Annual [Value,Min,Month,Day,Hour,Minute,Max,Month,Day,Hour,Minute]" // Yearly
352 : };
353 :
354 838 : ReportFreq determineFrequency(EnergyPlusData &state, const std::string_view FreqString)
355 : {
356 :
357 : // SUBROUTINE INFORMATION:
358 : // AUTHOR Linda K. Lawrie
359 : // DATE WRITTEN December 1998
360 : // MODIFIED December 2017; Jason DeGraw
361 :
362 : // REFERENCES:
363 : // \field Reporting Frequency
364 : // \type choice
365 : // \key Detailed
366 : // \note Detailed lists every instance (i.e. HVAC variable timesteps)
367 : // \key Timestep
368 : // \note Timestep refers to the zone Timestep/Number of Timesteps in hour value
369 : // \note RunPeriod, Environment, and Annual are the same
370 : // \key Hourly
371 : // \key Daily
372 : // \key Monthly
373 : // \key RunPeriod
374 : // \key Environment
375 : // \key Annual
376 : // \default Hourly
377 : // \note RunPeriod and Environment are synonymous
378 :
379 : // SUBROUTINE PARAMETER DEFINITIONS:
380 : static constexpr std::array<std::string_view, (int)ReportFreq::Num + 1> PossibleFreqs = {
381 : "DETA", "TIME", "HOUR", "DAIL", "MONT", "RUNP", "ENVI", "ANNU"};
382 : //=(/'detail','Timestep','Hourly','Daily','Monthly','RunPeriod','Environment','Annual'/)
383 : static constexpr std::array<std::string_view, (int)ReportFreq::Num + 1> ExactFreqStrings = {
384 : "Detailed", "Timestep", "Hourly", "Daily", "Monthly", "RunPeriod", "Environment", "Annual"};
385 : static constexpr std::array<std::string_view, (int)ReportFreq::Num + 1> ExactFreqStringsUC = {
386 : "DETAILED", "TIMESTEP", "HOURLY", "DAILY", "MONTHLY", "RUNPERIOD", "ENVIRONMENT", "ANNUAL"};
387 : // Vector of the result, was { -1, 0, 1, 2, 3, 4, 4, 4 } before the addition of Yearly;
388 : static constexpr std::array<ReportFreq, (int)ReportFreq::Num + 1> FreqValues = {ReportFreq::EachCall,
389 : ReportFreq::TimeStep,
390 : ReportFreq::Hour,
391 : ReportFreq::Day,
392 : ReportFreq::Month,
393 : ReportFreq::Simulation,
394 : ReportFreq::Simulation,
395 : ReportFreq::Year};
396 :
397 838 : ReportFreq freq = ReportFreq::Hour; // Default
398 : // TODO: I think it's supposed to be upper case already, but tests aren't doing that at least...
399 838 : const std::string FreqStringUpper = Util::makeUPPER(FreqString);
400 838 : std::string::size_type const LenString = min(len(FreqString), static_cast<std::string::size_type>(4u));
401 :
402 838 : if (LenString < 4u) {
403 0 : return freq;
404 : }
405 :
406 838 : std::string const FreqStringTrim(FreqStringUpper.substr(0, LenString));
407 5264 : for (unsigned Loop = 0; Loop < FreqValues.size(); ++Loop) {
408 2631 : if (FreqStringTrim == PossibleFreqs[Loop]) {
409 837 : if (FreqStringUpper != ExactFreqStringsUC[Loop]) {
410 0 : ShowWarningError(state, format("DetermineFrequency: Entered frequency=\"{}\" is not an exact match to key strings.", FreqString));
411 0 : ShowContinueError(state, format("Frequency={} will be used.", ExactFreqStrings[Loop]));
412 : }
413 837 : freq = std::max(FreqValues[Loop], state.dataOutputProcessor->minimumReportFreq);
414 837 : break;
415 : }
416 : }
417 838 : return freq;
418 838 : }
419 :
420 176405 : void GetReportVariableInput(EnergyPlusData &state)
421 : {
422 :
423 : // SUBROUTINE INFORMATION:
424 : // AUTHOR Linda K. Lawrie
425 : // DATE WRITTEN December 1998
426 : // MODIFIED December 2017; Jason DeGraw
427 :
428 : // PURPOSE OF THIS SUBROUTINE:
429 : // This subroutine gets the requested report variables from
430 : // the input file.
431 : // Report Variable,
432 : // \memo each Report Variable command picks variables to be put onto the standard output file (.eso)
433 : // \memo some variables may not be reported for every simulation
434 : // A1 , \field Key_Value
435 : // \note use '*' (without quotes) to apply this variable to all keys
436 : // A2 , \field Variable_Name
437 : // A3 , \field Reporting_Frequency
438 : // \type choice
439 : // \key detailed
440 : // \key timestep
441 : // \key hourly
442 : // \key daily
443 : // \key monthly
444 : // \key runperiod
445 : // A4 ; \field Schedule_Name
446 : // \type object-list
447 : // \object-list ScheduleNames
448 :
449 176405 : constexpr std::string_view routineName = "GetReportVariableInput";
450 :
451 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
452 : int NumAlpha;
453 : int NumNumbers;
454 : int IOStat;
455 176405 : bool ErrorsFound(false); // If errors detected in input
456 176405 : std::string cCurrentModuleObject;
457 176405 : Array1D_string cAlphaArgs(4);
458 176405 : Array1D_string cAlphaFieldNames(4);
459 176405 : Array1D_bool lAlphaBlanks(4);
460 176405 : Array1D<Real64> rNumericArgs(1);
461 176405 : Array1D_string cNumericFieldNames(1);
462 176405 : Array1D_bool lNumericBlanks(1);
463 176405 : auto &op = state.dataOutputProcessor;
464 :
465 : // Bail out if the input has already been read in
466 176405 : if (!op->GetOutputInputFlag) {
467 175534 : return;
468 : }
469 871 : op->GetOutputInputFlag = false;
470 :
471 : // First check environment variable to see of possible override for minimum reporting frequency
472 871 : if (!state.dataSysVars->MinReportFrequency.empty()) {
473 : // Formats
474 : static constexpr std::string_view Format_800("! <Minimum Reporting Frequency (overriding input value)>, Value, Input Value\n");
475 : static constexpr std::string_view Format_801(" Minimum Reporting Frequency, {},{}\n");
476 0 : op->minimumReportFreq = determineFrequency(state, state.dataSysVars->MinReportFrequency);
477 0 : print(state.files.eio, Format_800);
478 0 : print(state.files.eio, Format_801, reportingFrequencyNoticeStrings[(int)op->minimumReportFreq], state.dataSysVars->MinReportFrequency);
479 : }
480 :
481 871 : cCurrentModuleObject = "Output:Variable";
482 871 : int numReqVariables = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
483 :
484 1603 : for (int Loop = 1; Loop <= numReqVariables; ++Loop) {
485 :
486 732 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
487 : cCurrentModuleObject,
488 : Loop,
489 : cAlphaArgs,
490 : NumAlpha,
491 : rNumericArgs,
492 : NumNumbers,
493 : IOStat,
494 : lNumericBlanks,
495 : lAlphaBlanks,
496 : cAlphaFieldNames,
497 : cNumericFieldNames);
498 :
499 732 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
500 :
501 : // Check for duplicates?
502 732 : ReqVar *reqVar = new ReqVar();
503 732 : op->reqVars.push_back(reqVar);
504 :
505 732 : reqVar->key = cAlphaArgs(1);
506 732 : if (reqVar->key == "*") {
507 678 : reqVar->key = std::string();
508 : }
509 :
510 732 : bool is_simple_string = !DataOutputs::isKeyRegexLike(reqVar->key);
511 732 : reqVar->is_simple_string = is_simple_string;
512 732 : if (!is_simple_string) {
513 7 : reqVar->case_insensitive_pattern = std::make_shared<RE2>("(?i)" + reqVar->key);
514 : }
515 :
516 732 : std::string::size_type const lbpos = index(cAlphaArgs(2), '['); // Remove Units designation if user put it in
517 732 : if (lbpos != std::string::npos) {
518 0 : cAlphaArgs(2).erase(lbpos);
519 : // right trim
520 0 : cAlphaArgs(2) = cAlphaArgs(2).substr(0, std::min(cAlphaArgs(2).find_last_not_of(" \f\n\r\t\v") + 1, cAlphaArgs(2).size()));
521 : }
522 732 : reqVar->name = cAlphaArgs(2);
523 :
524 732 : reqVar->freq = determineFrequency(state, Util::makeUPPER(cAlphaArgs(3)));
525 732 : if (reqVar->freq == ReportFreq::Invalid) {
526 0 : ShowSevereInvalidKey(state, eoh, cAlphaFieldNames(3), cAlphaArgs(3));
527 0 : ErrorsFound = true;
528 : }
529 :
530 : // Schedule information
531 732 : if (lAlphaBlanks(4)) {
532 732 : reqVar->sched = nullptr;
533 0 : } else if ((reqVar->sched = Sched::GetSchedule(state, cAlphaArgs(4))) == nullptr) {
534 0 : ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(4), cAlphaArgs(4));
535 0 : ErrorsFound = true;
536 : }
537 :
538 732 : reqVar->Used = false;
539 : }
540 :
541 871 : if (ErrorsFound) {
542 0 : ShowFatalError(state, format("GetReportVariableInput:{}: errors in input.", cCurrentModuleObject));
543 : }
544 1229609 : }
545 :
546 2358 : std::string produceDateString(int const date, // Date of min/max
547 : ReportFreq const freq // Reporting Frequency
548 : )
549 : {
550 :
551 : // SUBROUTINE INFORMATION:
552 : // AUTHOR Linda K. Lawrie
553 : // DATE WRITTEN December 1998
554 :
555 : // PURPOSE OF THIS SUBROUTINE:
556 : // This subroutine produces the appropriate min/max string depending
557 : // on the reporting frequency.
558 :
559 : // METHODOLOGY EMPLOYED:
560 : // Prior to calling this routine, the basic value string will be
561 : // produced, but DecodeMonDayHrMin will not have been called.
562 :
563 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
564 : int Mon;
565 : int Day;
566 : int Hour;
567 : int Minute;
568 :
569 2358 : General::DecodeMonDayHrMin(date, Mon, Day, Hour, Minute);
570 :
571 2358 : switch (freq) {
572 2024 : case ReportFreq::Day:
573 2024 : return format("{:2},{:2}", Hour, Minute);
574 286 : case ReportFreq::Month:
575 286 : return format("{:2},{:2},{:2}", Day, Hour, Minute);
576 48 : case ReportFreq::Year:
577 : case ReportFreq::Simulation:
578 48 : return format("{:2},{:2},{:2},{:2}", Mon, Day, Hour, Minute);
579 0 : default:
580 0 : return std::string();
581 : }
582 : }
583 :
584 : // *****************************************************************************
585 : // The following routines implement Energy Meters in EnergyPlus.
586 : // *****************************************************************************
587 :
588 104 : void GetCustomMeterInput(EnergyPlusData &state, bool &ErrorsFound)
589 : {
590 :
591 : // SUBROUTINE INFORMATION:
592 : // AUTHOR Linda Lawrie
593 : // DATE WRITTEN January 2006
594 :
595 : // PURPOSE OF THIS SUBROUTINE:
596 : // This routine will help implement "custom"/user defined meters. However, it must be called after all
597 : // the other meters are set up and all report variables are established.
598 :
599 : // REFERENCES:
600 : // Processes the objects:
601 : // Meter:Custom,
602 : // \extensible:2 - repeat last two fields, remembering to remove ; from "inner" fields.
603 : // \memo Used to allow users to combine specific variables and/or meters into
604 : // \memo "custom" meter configurations.
605 : // A1, \field Name
606 : // \required-field
607 : // \reference CustomMeterNames
608 : // A2, \field Fuel Type
609 : // \type choice
610 : // \key Electricity
611 : // \key NaturalGas
612 : // \key PropaneGas
613 : // \key FuelOilNo1
614 : // \key FuelOilNo2
615 : // \key Coal
616 : // \key Diesel
617 : // \key Gasoline
618 : // \key Water
619 : // \key Generic
620 : // \key OtherFuel1
621 : // \key OtherFuel2
622 : // A3, \field Key Name 1
623 : // \required-field
624 : // \begin-extensible
625 : // A4, \field Report Variable or Meter Name 1
626 : // \required-field
627 : // <etc>
628 : // AND
629 : // Meter:CustomDecrement,
630 : // \extensible:2 - repeat last two fields, remembering to remove ; from "inner" fields.
631 : // \memo Used to allow users to combine specific variables and/or meters into
632 : // \memo "custom" meter configurations.
633 : // A1, \field Name
634 : // \required-field
635 : // \reference CustomMeterNames
636 : // A2, \field Fuel Type
637 : // \type choice
638 : // \key Electricity
639 : // \key NaturalGas
640 : // \key PropaneGas
641 : // \key FuelOilNo1
642 : // \key FuelOilNo2
643 : // \key Coal
644 : // \key Diesel
645 : // \key Gasoline
646 : // \key Water
647 : // \key Generic
648 : // \key OtherFuel1
649 : // \key OtherFuel2
650 : // A3, \field Source Meter Name
651 : // \required-field
652 : // A4, \field Key Name 1
653 : // \required-field
654 : // \begin-extensible
655 : // A5, \field Report Variable or Meter Name 1
656 : // \required-field
657 : // <etc>
658 :
659 104 : constexpr std::string_view routineName = "GetCustomMeterInput";
660 :
661 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
662 104 : auto &op = state.dataOutputProcessor;
663 104 : auto &ip = state.dataInputProcessing->inputProcessor;
664 104 : auto &ipsc = state.dataIPShortCut;
665 :
666 : int NumAlpha;
667 : int NumNumbers;
668 : int IOStat;
669 104 : Array1D_string NamesOfKeys; // Specific key name
670 104 : Array1D_int IndexesForKeyVar; // Array index
671 :
672 104 : Array1D_int VarsOnSourceMeter;
673 :
674 104 : bool BigErrorsFound = false;
675 :
676 104 : int numCustomMeters = 0, numCustomDecMeters = 0;
677 104 : std::vector<std::string> customMeterNames;
678 104 : std::vector<std::string> customDecMeterNames;
679 312 : if (auto const found = ip->epJSON.find("Meter:Custom"); found != ip->epJSON.end()) {
680 28 : for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomMeters) {
681 23 : customMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
682 5 : }
683 104 : }
684 :
685 312 : if (auto const found = ip->epJSON.find("Meter:CustomDecrement"); found != ip->epJSON.end()) {
686 2 : for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomDecMeters) {
687 1 : customDecMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
688 1 : }
689 104 : }
690 :
691 104 : ipsc->cCurrentModuleObject = "Meter:Custom";
692 127 : for (int Loop = 1; Loop <= numCustomMeters; ++Loop) {
693 46 : ip->getObjectItem(state,
694 23 : ipsc->cCurrentModuleObject,
695 : Loop,
696 23 : ipsc->cAlphaArgs,
697 : NumAlpha,
698 23 : ipsc->rNumericArgs,
699 : NumNumbers,
700 : IOStat,
701 23 : ipsc->lNumericFieldBlanks,
702 23 : ipsc->lAlphaFieldBlanks,
703 23 : ipsc->cAlphaFieldNames,
704 23 : ipsc->cNumericFieldNames);
705 :
706 23 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
707 :
708 23 : std::string meterName = ipsc->cAlphaArgs(1);
709 23 : std::string::size_type lbrackPos = index(meterName, '[');
710 23 : if (lbrackPos != std::string::npos) {
711 0 : meterName.erase(lbrackPos);
712 : }
713 :
714 23 : std::string meterNameUC = Util::makeUPPER(meterName);
715 :
716 : // Check for duplicate name
717 23 : if (op->meterMap.find(meterNameUC) != op->meterMap.end()) {
718 0 : ShowSevereDuplicateName(state, eoh);
719 0 : ErrorsFound = true;
720 0 : continue;
721 : }
722 :
723 : // Check for invalid resource
724 : Constant::eResource resource =
725 23 : static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
726 23 : if (resource == Constant::eResource::Invalid) {
727 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
728 0 : ErrorsFound = true;
729 0 : continue;
730 : }
731 :
732 23 : Constant::Units units = Constant::Units::Invalid;
733 :
734 : // We essentially have to do this loop twice, once to
735 : // check for errors and once to construct the meter. The
736 : // reason is that meters are cross-linked with source
737 : // meters and variables and those back-links will be
738 : // tricky to undo later.
739 23 : bool foundBadSrc = false;
740 23 : bool itemsAssigned = false;
741 :
742 51 : for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
743 29 : if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
744 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
745 0 : foundBadSrc = true;
746 1 : break;
747 : }
748 :
749 29 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
750 29 : lbrackPos = index(meterOrVarNameUC, '[');
751 29 : if (lbrackPos != std::string::npos) {
752 0 : meterOrVarNameUC.erase(lbrackPos);
753 : }
754 :
755 : // A custom meter cannot reference another custom meter
756 29 : if (std::find(customMeterNames.begin(), customMeterNames.end(), meterOrVarNameUC) != customMeterNames.end()) {
757 2 : ShowWarningError(state,
758 4 : format(R"(Meter:Custom="{}", contains a reference to another Meter:Custom in field: {}="{}".)",
759 1 : ipsc->cAlphaArgs(1),
760 1 : ipsc->cAlphaFieldNames(fldIndex + 1),
761 1 : ipsc->cAlphaArgs(fldIndex + 1)));
762 1 : foundBadSrc = true;
763 1 : break;
764 : }
765 :
766 : // A custom meter cannot reference another customDec meter
767 28 : if (std::find(customDecMeterNames.begin(), customDecMeterNames.end(), meterOrVarNameUC) != customDecMeterNames.end()) {
768 0 : ShowWarningError(state,
769 0 : format(R"(Meter:Custom="{}", contains a reference to another Meter:CustomDecrement in field: {}="{}".)",
770 0 : ipsc->cAlphaArgs(1),
771 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
772 0 : ipsc->cAlphaArgs(fldIndex + 1)));
773 0 : foundBadSrc = true;
774 0 : break;
775 : }
776 :
777 28 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
778 0 : int srcMeterNum = foundSrcMeter->second;
779 0 : auto *srcMeter = op->meters[srcMeterNum];
780 0 : assert(srcMeter->type == MeterType::Normal);
781 :
782 : // If it's the first meter, it gets to set the units
783 0 : if (units == Constant::Units::Invalid) {
784 0 : units = srcMeter->units;
785 0 : itemsAssigned = true;
786 0 : } else if (units != srcMeter->units) {
787 0 : ShowWarningCustom(state,
788 : eoh,
789 0 : format(R"(Meter:Custom="{}", differing units in {}="{}".)",
790 0 : ipsc->cAlphaArgs(1),
791 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
792 : meterOrVarNameUC));
793 0 : ShowContinueError(state,
794 0 : format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
795 0 : Constant::unitNames[(int)units],
796 0 : Constant::unitNames[(int)srcMeter->units]));
797 0 : foundBadSrc = true;
798 0 : break;
799 : }
800 :
801 : // It's a variable
802 28 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
803 23 : int srcDDVarNum = foundSrcDDVar->second;
804 23 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
805 :
806 : // Has to be a summed variable
807 23 : if (srcDDVar->storeType != StoreType::Sum) {
808 0 : ShowWarningCustom(state,
809 : eoh,
810 0 : format(R"(Meter:Custom="{}", variable not summed variable {}="{}".)",
811 0 : ipsc->cAlphaArgs(1),
812 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
813 : meterOrVarNameUC));
814 0 : ShowContinueError(state,
815 0 : format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
816 0 : Constant::unitNames[(int)units],
817 0 : Constant::unitNames[(int)srcDDVar->units]));
818 0 : foundBadSrc = true;
819 0 : break;
820 : }
821 :
822 : // If it's the first variable, it gets to set the units
823 23 : if (units == Constant::Units::Invalid) {
824 17 : units = srcDDVar->units;
825 : // Otherwise it has to match the existing units
826 6 : } else if (units != srcDDVar->units) {
827 0 : ShowWarningCustom(
828 0 : state, eoh, format("differing units in {}=\"{}\".", ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC));
829 0 : ShowContinueError(state,
830 0 : format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
831 0 : Constant::unitNames[(int)units],
832 0 : Constant::unitNames[(int)srcDDVar->units]));
833 0 : foundBadSrc = true;
834 0 : break;
835 : }
836 :
837 23 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
838 : // Have already checked for mismatching units between meter and source variable and assigned units
839 23 : if (KeyIsStar) {
840 18 : if (srcDDVar->keyOutVarNums.empty()) {
841 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
842 0 : foundBadSrc = true;
843 0 : break;
844 : }
845 :
846 18 : itemsAssigned = true;
847 : } else { // Key is not "*"
848 5 : bool foundKey = false;
849 15 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
850 15 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
851 5 : foundKey = true;
852 5 : itemsAssigned = true;
853 5 : break;
854 : }
855 5 : }
856 5 : if (!foundKey) {
857 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
858 0 : foundBadSrc = true;
859 0 : break;
860 : }
861 : } // if (keyIsStar)
862 :
863 : // Not a meter or a variable
864 : } else {
865 : // Cannot use ShowWarningItemNotFound because this string appears in a unit test
866 10 : ShowWarningError(state,
867 20 : format(R"(Meter:Custom="{}", invalid {}="{}".)",
868 5 : ipsc->cAlphaArgs(1),
869 5 : ipsc->cAlphaFieldNames(fldIndex + 1),
870 5 : ipsc->cAlphaArgs(fldIndex + 1)));
871 15 : ShowContinueError(state, "...will not be shown with the Meter results.");
872 : // Not setting the foundBadSrc flag here.
873 56 : }
874 :
875 29 : } // for (fldIndex)
876 :
877 : // Somehow, this meter is not linked to any variables either directly or via another meter
878 23 : if (!itemsAssigned) {
879 6 : ShowWarningError(state, format("Meter:Custom=\"{}\", no items assigned ", ipsc->cAlphaArgs(1)));
880 12 : ShowContinueError(
881 : state, "...will not be shown with the Meter results. This may be caused by a Meter:Custom be assigned to another Meter:Custom.");
882 6 : continue;
883 : }
884 :
885 : // One of the sources is bad
886 17 : if (foundBadSrc) {
887 0 : continue;
888 : }
889 :
890 17 : auto *meter = new Meter(meterName);
891 17 : meter->type = MeterType::Custom;
892 17 : meter->resource = resource;
893 17 : meter->units = units;
894 17 : bool errFlag = false;
895 17 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
896 17 : if (errFlag) {
897 0 : ShowContinueError(state, format("..on {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
898 0 : ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
899 : }
900 :
901 : // This meter is good
902 17 : int meterNum = op->meters.size();
903 17 : op->meters.push_back(meter);
904 17 : op->meterMap.insert_or_assign(meterNameUC, meterNum);
905 :
906 17 : for (ReportFreq reportFreq :
907 136 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
908 102 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
909 : }
910 :
911 17 : for (ReportFreq reportFreq :
912 136 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
913 102 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
914 : }
915 :
916 : // Do the loop again, this time without error checking
917 40 : for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
918 : // No need to check for empty fields
919 23 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
920 23 : lbrackPos = index(meterOrVarNameUC, '[');
921 23 : if (lbrackPos != std::string::npos) {
922 0 : meterOrVarNameUC.erase(lbrackPos);
923 : }
924 :
925 : // No need to check for custom source meters
926 23 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
927 0 : int srcMeterNum = foundSrcMeter->second;
928 0 : auto *srcMeter = op->meters[srcMeterNum];
929 0 : assert(srcMeter->type == MeterType::Normal);
930 :
931 : // No need to check for units
932 : // No need to check for duplicates
933 :
934 : // Check for duplicates
935 0 : if (std::find(meter->srcMeterNums.begin(), meter->srcMeterNums.end(), srcMeterNum) != meter->srcMeterNums.end()) {
936 0 : ShowWarningCustom(state,
937 : eoh,
938 0 : format("{}=\"{}\" referenced multiple times, only first instance will be used",
939 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
940 : meterOrVarNameUC));
941 0 : continue;
942 : }
943 :
944 : // Link meter to src meter and var and vice versa
945 0 : meter->srcMeterNums.push_back(srcMeterNum);
946 0 : srcMeter->dstMeterNums.push_back(meterNum);
947 :
948 0 : for (int srcVarNum : srcMeter->srcVarNums) {
949 0 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
950 0 : meter->srcVarNums.push_back(srcVarNum);
951 0 : op->outVars[srcVarNum]->meterNums.push_back(meterNum);
952 : }
953 0 : }
954 :
955 : // It's a variable
956 23 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
957 23 : int srcDDVarNum = foundSrcDDVar->second;
958 23 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
959 :
960 : // No need to check for a summed variable
961 : // No need to check for units match or to assign units
962 :
963 23 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
964 : // Have already checked for mismatching units between meter and source variable and assigned units
965 23 : if (KeyIsStar) {
966 : // No need to check for empty keys
967 40 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
968 22 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
969 0 : ShowWarningCustom(state,
970 : eoh,
971 0 : format("Output variable \"{}\" referenced multiple times (directly or via meter)",
972 0 : op->outVars[keyOutVarNum]->keyColonNameUC));
973 :
974 : } else {
975 22 : meter->srcVarNums.push_back(keyOutVarNum);
976 22 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
977 : }
978 18 : }
979 : } else { // Key is not "*"
980 15 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
981 15 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
982 5 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
983 0 : ShowWarningCustom(state,
984 : eoh,
985 0 : format("Output variable \"{}\" referenced multiple times (directly or via meter)",
986 0 : op->outVars[keyOutVarNum]->keyColonNameUC));
987 : } else {
988 5 : meter->srcVarNums.push_back(keyOutVarNum);
989 5 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
990 : }
991 5 : break;
992 : }
993 5 : }
994 : } // if (keyIsStar)
995 46 : } // if (meter or variable)
996 :
997 23 : } // for (fldIndex)
998 29 : } // for (Loop)
999 :
1000 104 : ipsc->cCurrentModuleObject = "Meter:CustomDecrement";
1001 105 : for (int Loop = 1; Loop <= numCustomDecMeters; ++Loop) {
1002 2 : ip->getObjectItem(state,
1003 1 : ipsc->cCurrentModuleObject,
1004 : Loop,
1005 1 : ipsc->cAlphaArgs,
1006 : NumAlpha,
1007 1 : ipsc->rNumericArgs,
1008 : NumNumbers,
1009 : IOStat,
1010 1 : ipsc->lNumericFieldBlanks,
1011 1 : ipsc->lAlphaFieldBlanks,
1012 1 : ipsc->cAlphaFieldNames,
1013 1 : ipsc->cNumericFieldNames);
1014 :
1015 1 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
1016 :
1017 1 : std::string meterName = ipsc->cAlphaArgs(1);
1018 1 : std::string::size_type lbrackPos = index(meterName, '[');
1019 1 : if (lbrackPos != std::string::npos) {
1020 0 : meterName.erase(lbrackPos);
1021 : }
1022 1 : std::string meterNameUC = Util::makeUPPER(meterName);
1023 :
1024 : // Search for duplicate name
1025 1 : if (op->meterMap.find(meterNameUC) != op->meterMap.end()) {
1026 0 : ShowSevereDuplicateName(state, eoh);
1027 0 : ErrorsFound = true;
1028 0 : continue;
1029 : }
1030 :
1031 : // Can't use resource type in AddMeter cause it will confuse it with other meters. So, now:
1032 : Constant::eResource resource =
1033 1 : static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
1034 1 : if (resource == Constant::eResource::Invalid) {
1035 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
1036 0 : ErrorsFound = true;
1037 0 : continue;
1038 : }
1039 :
1040 1 : bool itemsAssigned = false;
1041 :
1042 1 : std::string decMeterName = ipsc->cAlphaArgs(3);
1043 1 : lbrackPos = index(decMeterName, '[');
1044 1 : if (lbrackPos != std::string::npos) {
1045 0 : decMeterName.erase(lbrackPos);
1046 : }
1047 1 : std::string decMeterNameUC = Util::makeUPPER(decMeterName);
1048 :
1049 : // DecMeter cannot be a Meter:Custom
1050 1 : if (std::find(customDecMeterNames.begin(), customDecMeterNames.end(), decMeterNameUC) != customDecMeterNames.end()) {
1051 0 : ShowWarningError(state,
1052 0 : format(R"(Meter:CustomDec="{}", contains a reference to another Meter:CustomDecrement in field: {}="{}".)",
1053 0 : ipsc->cAlphaArgs(1),
1054 0 : ipsc->cAlphaFieldNames(3),
1055 0 : ipsc->cAlphaArgs(3)));
1056 0 : ErrorsFound = true;
1057 0 : continue;
1058 : }
1059 :
1060 1 : auto foundDecMeter = op->meterMap.find(decMeterName);
1061 1 : if (foundDecMeter == op->meterMap.end()) {
1062 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), decMeterName);
1063 0 : ErrorsFound = true;
1064 0 : continue;
1065 : }
1066 :
1067 1 : int decMeterNum = foundDecMeter->second;
1068 1 : auto *decMeter = op->meters[decMeterNum];
1069 1 : assert(decMeter->type == MeterType::Normal);
1070 :
1071 1 : Constant::Units units = decMeter->units;
1072 :
1073 1 : itemsAssigned = true;
1074 :
1075 : // We essentially have to do this loop twice, once to
1076 : // check for errors and once to construct the meter. The
1077 : // reason is that meters are cross-linked with source
1078 : // meters and variables and those back-links will be
1079 : // tricky to undo later.
1080 1 : bool foundBadSrc = false;
1081 :
1082 2 : for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
1083 1 : if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
1084 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
1085 0 : foundBadSrc = true;
1086 0 : break;
1087 : }
1088 :
1089 1 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
1090 1 : lbrackPos = index(meterOrVarNameUC, '[');
1091 1 : if (lbrackPos != std::string::npos) {
1092 0 : meterOrVarNameUC.erase(lbrackPos);
1093 : }
1094 :
1095 : // A custom meter cannot reference another custom meter
1096 1 : if (std::find(customDecMeterNames.begin(), customDecMeterNames.end(), meterOrVarNameUC) != customDecMeterNames.end()) {
1097 0 : ShowWarningError(state,
1098 0 : format(R"(Meter:Custom="{}", contains a reference to another Meter:CustomDecrement in field: {}="{}".)",
1099 0 : ipsc->cAlphaArgs(1),
1100 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
1101 0 : ipsc->cAlphaArgs(fldIndex + 1)));
1102 0 : foundBadSrc = true;
1103 0 : break;
1104 : }
1105 :
1106 1 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
1107 1 : int srcMeterNum = foundSrcMeter->second;
1108 1 : auto *srcMeter = op->meters[srcMeterNum];
1109 1 : assert(srcMeter->type == MeterType::Normal || srcMeter->type == MeterType::Custom);
1110 :
1111 : // If it's the first meter, it gets to set the units
1112 1 : if (units == Constant::Units::Invalid) {
1113 0 : units = srcMeter->units;
1114 0 : itemsAssigned = true;
1115 1 : } else if (units != srcMeter->units) {
1116 0 : ShowWarningCustom(state,
1117 : eoh,
1118 0 : format(R"(Meter:Custom="{}", differing units in {}="{}".)",
1119 0 : ipsc->cAlphaArgs(1),
1120 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
1121 : meterOrVarNameUC));
1122 0 : ShowContinueError(state,
1123 0 : format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
1124 0 : Constant::unitNames[(int)units],
1125 0 : Constant::unitNames[(int)srcMeter->units]));
1126 0 : foundBadSrc = true;
1127 0 : break;
1128 : }
1129 :
1130 : // It's a variable
1131 0 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
1132 0 : int srcDDVarNum = foundSrcDDVar->second;
1133 0 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
1134 :
1135 : // Has to be a summed variable
1136 0 : if (srcDDVar->storeType != StoreType::Sum) {
1137 0 : ShowWarningCustom(state,
1138 : eoh,
1139 0 : format(R"(Meter:Custom="{}", variable not summed variable {}="{}".)",
1140 0 : ipsc->cAlphaArgs(1),
1141 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
1142 : meterOrVarNameUC));
1143 0 : ShowContinueError(state,
1144 0 : format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
1145 0 : Constant::unitNames[(int)units],
1146 0 : Constant::unitNames[(int)srcDDVar->units]));
1147 0 : foundBadSrc = true;
1148 0 : break;
1149 : }
1150 :
1151 : // If it's the first variable, it gets to set the units
1152 0 : if (units == Constant::Units::Invalid) {
1153 0 : units = srcDDVar->units;
1154 : // Otherwise it has to match the existing units
1155 0 : } else if (units != srcDDVar->units) {
1156 0 : ShowWarningCustom(
1157 0 : state, eoh, format("differing units in {}=\"{}\".", ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC));
1158 0 : ShowContinueError(state,
1159 0 : format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
1160 0 : Constant::unitNames[(int)units],
1161 0 : Constant::unitNames[(int)srcDDVar->units]));
1162 0 : foundBadSrc = true;
1163 0 : break;
1164 : }
1165 :
1166 0 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
1167 : // Have already checked for mismatching units between meter and source variable and assigned units
1168 0 : if (KeyIsStar) {
1169 0 : if (srcDDVar->keyOutVarNums.empty()) {
1170 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
1171 0 : foundBadSrc = true;
1172 0 : break;
1173 : }
1174 :
1175 0 : itemsAssigned = true;
1176 : } else { // Key is not "*"
1177 0 : bool foundKey = false;
1178 0 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1179 0 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
1180 0 : foundKey = true;
1181 0 : itemsAssigned = true;
1182 0 : break;
1183 : }
1184 0 : }
1185 0 : if (!foundKey) {
1186 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
1187 0 : foundBadSrc = true;
1188 0 : break;
1189 : }
1190 : } // if (keyIsStar)
1191 :
1192 : // Not a meter or a variable
1193 : } else {
1194 : // Cannot use ShowWarningItemNotFound because this string appears in a unit test
1195 0 : ShowWarningError(state,
1196 0 : format(R"(Meter:Custom="{}", invalid {}="{}".)",
1197 0 : ipsc->cAlphaArgs(1),
1198 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
1199 0 : ipsc->cAlphaArgs(fldIndex + 1)));
1200 0 : ShowContinueError(state, "...will not be shown with the Meter results.");
1201 0 : foundBadSrc = true;
1202 0 : break;
1203 1 : }
1204 :
1205 1 : } // for (fldIndex)
1206 :
1207 : // Somehow, this meter is not linked to any variables either directly or via another meter
1208 1 : if (!itemsAssigned) {
1209 0 : ShowWarningError(state, format("Meter:Custom=\"{}\", no items assigned ", ipsc->cAlphaArgs(1)));
1210 0 : ShowContinueError(
1211 : state, "...will not be shown with the Meter results. This may be caused by a Meter:Custom be assigned to another Meter:Custom.");
1212 0 : continue;
1213 : }
1214 :
1215 : // One of the sources is bad
1216 1 : if (foundBadSrc) {
1217 0 : continue;
1218 : }
1219 :
1220 1 : auto *meter = new Meter(meterName);
1221 1 : meter->type = MeterType::CustomDec;
1222 1 : meter->resource = resource;
1223 1 : meter->units = units;
1224 1 : bool errFlag = false;
1225 1 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
1226 1 : if (errFlag) {
1227 0 : ShowContinueError(state, format("..on {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
1228 0 : ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
1229 : }
1230 :
1231 1 : meter->decMeterNum = decMeterNum;
1232 :
1233 : // This meter is good
1234 1 : int meterNum = op->meters.size();
1235 1 : op->meters.push_back(meter);
1236 1 : op->meterMap.insert_or_assign(meterNameUC, meterNum);
1237 :
1238 1 : for (ReportFreq reportFreq :
1239 8 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1240 6 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
1241 : }
1242 :
1243 1 : for (ReportFreq reportFreq :
1244 8 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1245 6 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
1246 : }
1247 :
1248 : // Links meter to dec meter and its output variable and vice versa
1249 1 : meter->srcMeterNums.push_back(meter->decMeterNum);
1250 1 : decMeter->dstMeterNums.push_back(meterNum);
1251 :
1252 : // Not linking decMeter vars to this meter and vice versa
1253 : // for (int srcVarNum : decMeter->srcVarNums) {
1254 : // if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) != meter->srcVarNums.end()) continue; // Already linked
1255 : // meter->srcVarNums.push_back(srcVarNum);
1256 : // op->outVars[srcVarNum]->meterNums.push_back(meterNum);
1257 : // }
1258 :
1259 : // Do the loop again, this time without error checking
1260 2 : for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
1261 : // No need to check for empty fields
1262 1 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
1263 1 : lbrackPos = index(meterOrVarNameUC, '[');
1264 1 : if (lbrackPos != std::string::npos) {
1265 0 : meterOrVarNameUC.erase(lbrackPos);
1266 : }
1267 :
1268 : // No need to check for custom source meters
1269 1 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
1270 1 : int srcMeterNum = foundSrcMeter->second;
1271 1 : auto *srcMeter = op->meters[srcMeterNum];
1272 1 : assert(srcMeter->type == MeterType::Normal || srcMeter->type == MeterType::Custom);
1273 :
1274 : // No need to check for units
1275 : // No need to check for duplicates
1276 :
1277 : // Check for duplicates
1278 1 : if (std::find(meter->srcMeterNums.begin(), meter->srcMeterNums.end(), srcMeterNum) != meter->srcMeterNums.end()) {
1279 0 : ShowWarningCustom(state,
1280 : eoh,
1281 0 : format("{}=\"{}\" referenced multiple times, only first instance will be used",
1282 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
1283 : meterOrVarNameUC));
1284 0 : continue;
1285 : }
1286 :
1287 : // Link meter to src meter and var and vice versa
1288 1 : meter->srcMeterNums.push_back(srcMeterNum);
1289 1 : srcMeter->dstMeterNums.push_back(meterNum);
1290 :
1291 6 : for (int srcVarNum : srcMeter->srcVarNums) {
1292 5 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
1293 5 : meter->srcVarNums.push_back(srcVarNum);
1294 5 : op->outVars[srcVarNum]->meterNums.push_back(meterNum);
1295 : }
1296 1 : }
1297 :
1298 : // It's a variable
1299 0 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
1300 0 : int srcDDVarNum = foundSrcDDVar->second;
1301 0 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
1302 :
1303 : // No need to check for a summed variable
1304 : // No need to check for units match or to assign units
1305 :
1306 0 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
1307 : // Have already checked for mismatching units between meter and source variable and assigned units
1308 0 : if (KeyIsStar) {
1309 : // No need to check for empty keys
1310 0 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1311 0 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
1312 0 : ShowWarningCustom(state,
1313 : eoh,
1314 0 : format("Output variable \"{}\" referenced multiple times (directly or via meter)",
1315 0 : op->outVars[keyOutVarNum]->keyColonNameUC));
1316 :
1317 : } else {
1318 0 : meter->srcVarNums.push_back(keyOutVarNum);
1319 0 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
1320 : }
1321 0 : }
1322 : } else { // Key is not "*"
1323 0 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1324 0 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
1325 0 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
1326 0 : ShowWarningCustom(state,
1327 : eoh,
1328 0 : format("Output variable \"{}\" referenced multiple times (directly or via meter)",
1329 0 : op->outVars[keyOutVarNum]->keyColonNameUC));
1330 : } else {
1331 0 : meter->srcVarNums.push_back(keyOutVarNum);
1332 0 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
1333 : }
1334 0 : break;
1335 : }
1336 0 : }
1337 : } // if (keyIsStar)
1338 1 : } // if (meter or variable)
1339 :
1340 1 : } // for (fldIndex)
1341 1 : }
1342 :
1343 104 : if (BigErrorsFound) {
1344 0 : ErrorsFound = true;
1345 : }
1346 104 : }
1347 :
1348 46169 : int AddMeter(EnergyPlusData &state,
1349 : std::string const &Name, // Name for the meter
1350 : Constant::Units const units, // Units for the meter
1351 : Constant::eResource resource, // ResourceType for the meter
1352 : EndUseCat endUseCat, // EndUse for the meter
1353 : std::string_view const EndUseSub, // EndUse subcategory for the meter
1354 : Group group,
1355 : int outVarNum) // Variable index
1356 : {
1357 :
1358 : // SUBROUTINE INFORMATION:
1359 : // AUTHOR Linda Lawrie
1360 : // DATE WRITTEN January 2001
1361 :
1362 : // PURPOSE OF THIS SUBROUTINE:
1363 : // This subroutine adds a meter to the current definition set of meters. If the maximum has
1364 : // already been reached, a reallocation procedure begins. This action needs to be done at the
1365 : // start of the simulation, primarily before any output is stored.
1366 :
1367 : // Make sure this isn't already in the list of meter names
1368 46169 : auto &op = state.dataOutputProcessor;
1369 :
1370 46169 : int meterNum = -1;
1371 46169 : Meter *meter = nullptr;
1372 :
1373 46169 : std::string nameUC = Util::makeUPPER(Name);
1374 :
1375 46169 : if (auto found = op->meterMap.find(nameUC); found != op->meterMap.end()) {
1376 36721 : meterNum = found->second;
1377 36721 : meter = op->meters[meterNum];
1378 : } else {
1379 :
1380 9448 : meterNum = op->meters.size();
1381 9448 : meter = new Meter(Name);
1382 9448 : op->meters.push_back(meter);
1383 9448 : op->meterMap.insert_or_assign(nameUC, meterNum);
1384 :
1385 9448 : meter->type = MeterType::Normal;
1386 9448 : meter->resource = resource;
1387 9448 : meter->endUseCat = endUseCat;
1388 9448 : meter->EndUseSub = EndUseSub;
1389 9448 : meter->group = group;
1390 9448 : meter->units = units;
1391 9448 : meter->CurTSValue = 0.0;
1392 :
1393 9448 : for (ReportFreq reportFreq :
1394 75584 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1395 56688 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
1396 : }
1397 :
1398 9448 : for (ReportFreq reportFreq :
1399 75584 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1400 56688 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
1401 : }
1402 :
1403 9448 : if (meter->resource != Constant::eResource::Invalid) {
1404 9448 : bool errFlag = false;
1405 9448 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, units, errFlag);
1406 9448 : if (errFlag) {
1407 1 : ShowContinueError(state, format("..on Meter=\"{}\".", Name));
1408 3 : ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
1409 : }
1410 : }
1411 46169 : }
1412 :
1413 : // outVarNum == -1 is only true in unit tests
1414 46169 : if (outVarNum != -1) {
1415 45946 : OutVar *var = op->outVars[outVarNum];
1416 45946 : var->meterNums.push_back(meterNum);
1417 45946 : meter->srcVarNums.push_back(outVarNum);
1418 : }
1419 :
1420 46169 : return meterNum;
1421 46169 : }
1422 :
1423 8296 : void AttachMeters(EnergyPlusData &state,
1424 : Constant::Units const units, // Units for this meter
1425 : Constant::eResource resource, // Electricity, Gas, etc.
1426 : EndUseCat endUseCat, // End-use category (Lights, Heating, etc.)
1427 : std::string_view const EndUseSub, // End-use subcategory (user-defined, e.g., General Lights, Task Lights, etc.)
1428 : Group group, // Group key (Facility, Zone, Building, etc.)
1429 : std::string const &ZoneName, // Zone key only applicable for Building group
1430 : std::string const &SpaceType, // Space Type key only applicable for Building group
1431 : int const outVarNum // Number of this report variable
1432 : )
1433 : {
1434 :
1435 : // SUBROUTINE INFORMATION:
1436 : // AUTHOR Linda Lawrie
1437 : // DATE WRITTEN January 2001
1438 :
1439 : // PURPOSE OF THIS SUBROUTINE:
1440 : // This subroutine determines which meters this variable will be on (if any),
1441 : // creates those meters and links the variable to them (and vice versa).
1442 :
1443 8296 : std::string_view resourceName = Constant::eResourceNames[(int)resource];
1444 :
1445 8296 : std::string endUseSub = standardizeEndUseSub(endUseCat, EndUseSub);
1446 :
1447 8296 : if (!endUseSub.empty()) {
1448 6960 : addEndUseSubcategory(state, endUseCat, endUseSub);
1449 : }
1450 :
1451 8296 : if (!SpaceType.empty()) {
1452 221 : addEndUseSpaceType(state, endUseCat, SpaceType);
1453 : }
1454 :
1455 8296 : std::string meterName = format("{}:Facility", resourceName);
1456 8296 : AddMeter(state, meterName, units, resource, EndUseCat::Invalid, "", Group::Invalid, outVarNum);
1457 :
1458 8296 : if (group != Group::Invalid) {
1459 7836 : std::string groupMeterName = format("{}:{}", resourceName, groupNames[(int)group]);
1460 7836 : AddMeter(state, groupMeterName, units, resource, EndUseCat::Invalid, "", group, outVarNum);
1461 :
1462 7836 : if (group == Group::Building) {
1463 4761 : if (!ZoneName.empty()) {
1464 4719 : std::string zoneMeterName = format("{}:Zone:{}", resourceName, ZoneName);
1465 4719 : AddMeter(state, zoneMeterName, units, resource, EndUseCat::Invalid, "", Group::Zone, outVarNum);
1466 4719 : }
1467 4761 : if (!SpaceType.empty()) {
1468 221 : std::string spaceMeterName = format("{}:SpaceType:{}", resourceName, SpaceType);
1469 221 : AddMeter(state, spaceMeterName, units, resource, EndUseCat::Invalid, "", Group::SpaceType, outVarNum);
1470 221 : }
1471 : } // if (Group == "Building")
1472 7836 : }
1473 :
1474 : //!! Following if we do EndUse by ResourceType
1475 8296 : if (endUseCat != EndUseCat::Invalid) {
1476 8255 : std::string_view endUseCatName = endUseCatNames[(int)endUseCat];
1477 8255 : std::string enduseMeterName = format("{}:{}", endUseCatName, resourceName);
1478 8255 : AddMeter(state, enduseMeterName, units, resource, endUseCat, "", Group::Invalid, outVarNum);
1479 :
1480 8255 : if (group == Group::Building) { // Match to Zone and Space
1481 4761 : if (!ZoneName.empty()) {
1482 4719 : std::string enduseZoneMeterName = format("{}:{}:Zone:{}", endUseCatName, resourceName, ZoneName);
1483 4719 : AddMeter(state, enduseZoneMeterName, units, resource, endUseCat, "", Group::Zone, outVarNum);
1484 4719 : }
1485 4761 : if (!SpaceType.empty()) {
1486 221 : std::string enduseSpaceMeterName = format("{}:{}:SpaceType:{}", endUseCatName, resourceName, SpaceType);
1487 221 : AddMeter(state, enduseSpaceMeterName, units, resource, endUseCat, "", Group::SpaceType, outVarNum);
1488 221 : }
1489 : }
1490 :
1491 : // End-Use Subcategories
1492 8255 : if (!endUseSub.empty()) {
1493 6960 : std::string subEnduseMeterName = format("{}:{}:{}", endUseSub, endUseCatNames[(int)endUseCat], resourceName);
1494 6960 : AddMeter(state, subEnduseMeterName, units, resource, endUseCat, endUseSub, Group::Invalid, outVarNum);
1495 :
1496 6960 : if (group == Group::Building) { // Match to Zone and Space
1497 4761 : if (!ZoneName.empty()) {
1498 4719 : std::string subEnduseZoneMeterName = format("{}:{}:{}:Zone:{}", endUseSub, endUseCatName, resourceName, ZoneName);
1499 4719 : AddMeter(state, subEnduseZoneMeterName, units, resource, endUseCat, endUseSub, Group::Zone, outVarNum);
1500 4719 : }
1501 4761 : if (!SpaceType.empty()) {
1502 221 : std::string subEnduseSpaceMeterName = format("{}:{}:{}:SpaceType:{}", endUseSub, endUseCatName, resourceName, SpaceType);
1503 221 : AddMeter(state, subEnduseSpaceMeterName, units, resource, endUseCat, endUseSub, Group::SpaceType, outVarNum);
1504 221 : }
1505 : } // if (sovGroup == Building)
1506 6960 : } // if (!endUseSub.empty())
1507 8255 : } // if (sovEndUseCat != Invalid)
1508 8296 : } // AttachMeters()
1509 :
1510 8348 : std::string standardizeEndUseSub(EndUseCat endUseCat, std::string_view endUseSubName)
1511 : {
1512 8348 : if (!endUseSubName.empty()) {
1513 10330 : return std::string(endUseSubName);
1514 3183 : } else if (endUseCat == EndUseCat::Invalid) {
1515 82 : return "";
1516 3142 : } else if (endUseCat2endUse[(int)endUseCat] != Constant::EndUse::Invalid) {
1517 3694 : return "General";
1518 : } else {
1519 2590 : return "";
1520 : }
1521 : }
1522 :
1523 9475 : OutputProcessor::RT_IPUnits GetResourceIPUnits(EnergyPlusData &state,
1524 : Constant::eResource resource, // Resource Type
1525 : Constant::Units const units, // Meter units
1526 : bool &ErrorsFound // true if errors found during subroutine
1527 : )
1528 : {
1529 :
1530 : // SUBROUTINE INFORMATION:
1531 : // AUTHOR Linda Lawrie
1532 : // DATE WRITTEN January 2012
1533 : // MODIFIED September 2012; made into subroutine
1534 : // RE-ENGINEERED na
1535 :
1536 : // PURPOSE OF THIS SUBROUTINE:
1537 : // In order to set up tabular reports for IP units, need to search on same strings
1538 : // that tabular reports does for IP conversion.
1539 :
1540 : // REFERENCES:
1541 : // OutputReportTabular looks for:
1542 : // CONSUMP - not used in meters
1543 : // ELEC - Electricity (kWH)
1544 : // GAS - Gas (therm)
1545 : // COOL - Cooling (ton)
1546 : // and we need to add WATER (for m3/gal, etc)
1547 :
1548 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1549 : RT_IPUnits IPUnits;
1550 :
1551 : // Commented this out to avoid accidentally clearing an error condition by calling this function
1552 : // ErrorsFound = false;
1553 :
1554 9475 : switch (resource) {
1555 4812 : case Constant::eResource::Electricity:
1556 : case Constant::eResource::ElectricityProduced:
1557 : case Constant::eResource::ElectricityPurchased:
1558 : case Constant::eResource::ElectricitySurplusSold:
1559 : case Constant::eResource::ElectricityNet: {
1560 4812 : IPUnits = RT_IPUnits::Electricity;
1561 4812 : } break;
1562 356 : case Constant::eResource::NaturalGas: {
1563 356 : IPUnits = RT_IPUnits::Gas;
1564 356 : } break;
1565 463 : case Constant::eResource::Water:
1566 : case Constant::eResource::MainsWater:
1567 : case Constant::eResource::RainWater:
1568 : case Constant::eResource::WellWater:
1569 : case Constant::eResource::OnSiteWater: {
1570 463 : IPUnits = RT_IPUnits::Water;
1571 463 : } break;
1572 376 : case Constant::eResource::DistrictCooling:
1573 : case Constant::eResource::PlantLoopCoolingDemand: {
1574 376 : IPUnits = RT_IPUnits::Cooling;
1575 376 : } break;
1576 3468 : default: {
1577 3468 : if (units == Constant::Units::m3) {
1578 11 : IPUnits = RT_IPUnits::OtherM3;
1579 3457 : } else if (units == Constant::Units::kg) {
1580 293 : IPUnits = RT_IPUnits::OtherKG;
1581 3164 : } else if (units == Constant::Units::L) {
1582 11 : IPUnits = RT_IPUnits::OtherL;
1583 : } else {
1584 3153 : IPUnits = RT_IPUnits::OtherJ;
1585 : }
1586 3468 : } break;
1587 : } // switch
1588 :
1589 : // write(outputfiledebug,*) 'resourcetype=',TRIM(resourcetype)
1590 : // write(outputfiledebug,*) 'ipunits type=',CodeForIPUnits
1591 9475 : if (units != Constant::Units::kg && units != Constant::Units::J && units != Constant::Units::m3 && units != Constant::Units::L) {
1592 6 : ShowWarningMessage(
1593 6 : state, format("DetermineMeterIPUnits: Meter units not recognized for IP Units conversion=[{}].", Constant::unitNames[(int)units]));
1594 3 : ErrorsFound = true;
1595 : }
1596 9475 : return IPUnits;
1597 : }
1598 :
1599 18800 : void UpdateMeters(EnergyPlusData &state, int const TimeStamp) // Current TimeStamp (for max/min)
1600 : {
1601 :
1602 : // SUBROUTINE INFORMATION:
1603 : // AUTHOR Linda Lawrie
1604 : // DATE WRITTEN April 2001
1605 :
1606 : // PURPOSE OF THIS SUBROUTINE:
1607 : // This subroutine updates the meters with the current time step value
1608 : // for each meter. Also, sets min/max values for hourly...run period reporting.
1609 :
1610 18800 : if (state.dataGlobal->WarmupFlag) {
1611 1 : return;
1612 : }
1613 :
1614 18799 : auto &op = state.dataOutputProcessor;
1615 :
1616 18799 : if (op->meters.size() == 0 || op->meterValues.size() == 0) {
1617 2496 : return;
1618 : }
1619 :
1620 630384 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
1621 614081 : auto *meter = op->meters[iMeter];
1622 614081 : if (meter->type != MeterType::CustomDec && meter->type != MeterType::CustomDiff) {
1623 614081 : meter->periods[(int)ReportFreq::TimeStep].Value += op->meterValues[iMeter];
1624 : // Is this correct? What is going on here?
1625 : } else {
1626 0 : meter->periods[(int)ReportFreq::TimeStep].Value += op->meterValues[iMeter];
1627 : // meter->periods[(int)ReportFreq::TimeStep].Value =
1628 : // op->meters[meter->decMeterNum]->periods[(int)ReportFreq::TimeStep].Value - op->meterValues[iMeter];
1629 : }
1630 :
1631 614081 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1632 614081 : meter->periods[(int)ReportFreq::Hour].Value += TSValue;
1633 614081 : meter->periods[(int)ReportFreq::Day].Value += TSValue;
1634 614081 : meter->periods[(int)ReportFreq::Month].Value += TSValue;
1635 614081 : meter->periods[(int)ReportFreq::Year].Value += TSValue;
1636 614081 : meter->periods[(int)ReportFreq::Simulation].Value += TSValue;
1637 614081 : meter->periodFinYrSM.Value += TSValue;
1638 : } // for (iMeter)
1639 :
1640 : // Set Max
1641 630384 : for (auto *meter : op->meters) {
1642 614081 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1643 614081 : Real64 TSValueComp = TSValue; // - 0.00001;
1644 :
1645 : // Todo - HRMinVal, HRMaxVal not used
1646 614081 : auto &periodDY = meter->periods[(int)ReportFreq::Day];
1647 614081 : if (TSValueComp <= periodDY.MaxVal) {
1648 582266 : continue;
1649 : }
1650 31815 : periodDY.MaxVal = TSValue;
1651 31815 : periodDY.MaxValDate = TimeStamp;
1652 :
1653 31815 : auto &periodMN = meter->periods[(int)ReportFreq::Month];
1654 31815 : if (TSValueComp <= periodMN.MaxVal) {
1655 0 : continue;
1656 : }
1657 31815 : periodMN.MaxVal = TSValue;
1658 31815 : periodMN.MaxValDate = TimeStamp;
1659 :
1660 31815 : auto &periodYR = meter->periods[(int)ReportFreq::Year];
1661 31815 : if (TSValueComp > periodYR.MaxVal) {
1662 31815 : periodYR.MaxVal = TSValue;
1663 31815 : periodYR.MaxValDate = TimeStamp;
1664 : }
1665 :
1666 31815 : auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
1667 31815 : if (TSValueComp > periodSM.MaxVal) {
1668 31815 : periodSM.MaxVal = TSValue;
1669 31815 : periodSM.MaxValDate = TimeStamp;
1670 : }
1671 :
1672 31815 : if (TSValueComp > meter->periodFinYrSM.MaxVal) {
1673 31815 : meter->periodFinYrSM.MaxVal = TSValue;
1674 31815 : meter->periodFinYrSM.MaxValDate = TimeStamp;
1675 : }
1676 16303 : } // for (meter)
1677 :
1678 : // Set Min
1679 630384 : for (auto *meter : op->meters) {
1680 614081 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1681 614081 : Real64 TSValueComp = TSValue; // + 0.00001;
1682 :
1683 614081 : auto &periodDY = meter->periods[(int)ReportFreq::Day];
1684 614081 : if (TSValueComp >= periodDY.MinVal) {
1685 597940 : continue;
1686 : }
1687 :
1688 16141 : periodDY.MinVal = TSValue;
1689 16141 : periodDY.MinValDate = TimeStamp;
1690 :
1691 16141 : auto &periodMN = meter->periods[(int)ReportFreq::Month];
1692 16141 : if (TSValueComp >= periodMN.MinVal) {
1693 0 : continue;
1694 : }
1695 :
1696 16141 : periodMN.MinVal = TSValue;
1697 16141 : periodMN.MinValDate = TimeStamp;
1698 :
1699 16141 : auto &periodYR = meter->periods[(int)ReportFreq::Year];
1700 16141 : if (TSValueComp < periodYR.MinVal) {
1701 16141 : periodYR.MinVal = TSValue;
1702 16141 : periodYR.MinValDate = TimeStamp;
1703 : }
1704 :
1705 16141 : auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
1706 16141 : if (TSValueComp < periodSM.MinVal) {
1707 16141 : periodSM.MinVal = TSValue;
1708 16141 : periodSM.MinValDate = TimeStamp;
1709 : }
1710 :
1711 16141 : if (TSValueComp < meter->periodFinYrSM.MinVal) {
1712 16141 : meter->periodFinYrSM.MinVal = TSValue;
1713 16141 : meter->periodFinYrSM.MinValDate = TimeStamp;
1714 : }
1715 16303 : } // for (meter)
1716 :
1717 630384 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
1718 614081 : op->meterValues[iMeter] = 0.0; // Ready for next update
1719 : }
1720 : } // UpdateMeters()
1721 :
1722 0 : void ResetAccumulationWhenWarmupComplete(EnergyPlusData &state)
1723 : {
1724 : // SUBROUTINE INFORMATION:
1725 : // AUTHOR Jason Glazer
1726 : // DATE WRITTEN June 2015
1727 :
1728 : // PURPOSE OF THIS SUBROUTINE:
1729 : // Resets the accumulating meter values. Needed after warmup period is over to
1730 : // reset the totals on meters so that they are not accumulated over the warmup period
1731 :
1732 0 : auto const &op = state.dataOutputProcessor;
1733 :
1734 0 : for (auto *meter : op->meters) {
1735 0 : for (int iPeriod = (int)ReportFreq::Hour; iPeriod < (int)ReportFreq::Num; ++iPeriod) {
1736 0 : meter->periods[iPeriod].resetVals();
1737 : }
1738 0 : meter->periodFinYrSM.resetVals();
1739 0 : }
1740 :
1741 0 : for (auto *var : op->outVars) {
1742 0 : if (var->freq == ReportFreq::Month || var->freq == ReportFreq::Year || var->freq == ReportFreq::Simulation) {
1743 0 : var->StoreValue = 0.0;
1744 0 : var->NumStored = 0;
1745 : }
1746 0 : }
1747 0 : } // ResetAccumulationWhenWarmupComplete()
1748 :
1749 18802 : void ReportTSMeters(EnergyPlusData &state,
1750 : Real64 const StartMinute, // Start Minute for TimeStep
1751 : Real64 const EndMinute, // End Minute for TimeStep
1752 : bool &PrintESOTimeStamp, // True if the ESO Time Stamp also needs to be printed
1753 : bool PrintTimeStampToSQL // Print Time Stamp to SQL file
1754 : )
1755 : {
1756 :
1757 : // SUBROUTINE INFORMATION:
1758 : // AUTHOR Linda Lawrie
1759 : // DATE WRITTEN January 2001
1760 : // MODIFIED na
1761 : // RE-ENGINEERED na
1762 :
1763 : // PURPOSE OF THIS SUBROUTINE:
1764 : // This subroutine reports on the meters that have been requested for
1765 : // reporting on each time step.
1766 :
1767 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1768 : bool PrintTimeStamp;
1769 : int CurDayType;
1770 18802 : auto &op = state.dataOutputProcessor;
1771 18802 : auto &rf = state.dataResultsFramework->resultsFramework;
1772 18802 : auto &rfMetersTS = rf->Meters[(int)ReportFreq::TimeStep];
1773 :
1774 18802 : if (!rfMetersTS.dataFrameEnabled()) {
1775 18705 : rf->initializeMeters(op->meters, ReportFreq::TimeStep);
1776 : }
1777 :
1778 18802 : PrintTimeStamp = true;
1779 717182 : for (int Loop = 0; Loop < (int)op->meters.size(); ++Loop) {
1780 698380 : auto *meter = op->meters[Loop];
1781 698380 : auto &periodTS = meter->periods[(int)ReportFreq::TimeStep];
1782 698380 : meter->CurTSValue = periodTS.Value;
1783 698380 : if (!periodTS.Rpt && !periodTS.accRpt) {
1784 698273 : continue;
1785 : }
1786 107 : if (PrintTimeStamp) {
1787 105 : CurDayType = state.dataEnvrn->DayOfWeek;
1788 105 : if (state.dataEnvrn->HolidayIndex > 0) {
1789 98 : CurDayType = state.dataEnvrn->HolidayIndex;
1790 : }
1791 630 : WriteTimeStampFormatData(state,
1792 105 : state.files.mtr,
1793 : ReportFreq::EachCall,
1794 105 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
1795 105 : state.dataGlobal->DayOfSimChr,
1796 105 : PrintTimeStamp && PrintTimeStampToSQL,
1797 105 : state.dataEnvrn->Month,
1798 105 : state.dataEnvrn->DayOfMonth,
1799 105 : state.dataGlobal->HourOfDay,
1800 : EndMinute,
1801 : StartMinute,
1802 105 : state.dataEnvrn->DSTIndicator,
1803 105 : Sched::dayTypeNames[CurDayType]);
1804 105 : if (rfMetersTS.dataFrameEnabled()) {
1805 105 : rfMetersTS.newRow(
1806 105 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, EndMinute, state.dataGlobal->CalendarYear);
1807 : }
1808 105 : PrintTimeStamp = false;
1809 105 : PrintTimeStampToSQL = false;
1810 : }
1811 :
1812 107 : if (PrintESOTimeStamp && !periodTS.RptFO && !periodTS.accRptFO) {
1813 99 : CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
1814 594 : WriteTimeStampFormatData(state,
1815 99 : state.files.eso,
1816 : ReportFreq::EachCall,
1817 99 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
1818 99 : state.dataGlobal->DayOfSimChr,
1819 99 : PrintTimeStamp && PrintESOTimeStamp && PrintTimeStampToSQL,
1820 99 : state.dataEnvrn->Month,
1821 99 : state.dataEnvrn->DayOfMonth,
1822 99 : state.dataGlobal->HourOfDay,
1823 : EndMinute,
1824 : StartMinute,
1825 99 : state.dataEnvrn->DSTIndicator,
1826 99 : Sched::dayTypeNames[CurDayType]);
1827 99 : PrintESOTimeStamp = false;
1828 : }
1829 :
1830 107 : if (periodTS.Rpt) {
1831 107 : periodTS.WriteReportData(state, ReportFreq::TimeStep);
1832 107 : rfMetersTS.pushVariableValue(periodTS.RptNum, periodTS.Value);
1833 : }
1834 :
1835 107 : if (periodTS.accRpt) {
1836 0 : WriteCumulativeReportMeterData(state, periodTS.accRptNum, periodTS.Value, periodTS.accRptFO);
1837 0 : rfMetersTS.pushVariableValue(periodTS.accRptNum, periodTS.Value);
1838 : }
1839 : }
1840 :
1841 717182 : for (auto *meter : op->meters) {
1842 698380 : meter->periods[(int)ReportFreq::TimeStep].Value = 0.0;
1843 18802 : }
1844 18802 : } // ReportTSMeters()
1845 :
1846 3539 : void ReportMeters(EnergyPlusData &state,
1847 : ReportFreq freq,
1848 : bool PrintTimeStampToSQL // Print Time Stamp to SQL file
1849 : )
1850 : {
1851 :
1852 : // SUBROUTINE INFORMATION:
1853 : // AUTHOR Linda Lawrie
1854 : // DATE WRITTEN January 2001
1855 :
1856 : // PURPOSE OF THIS SUBROUTINE:
1857 : // This subroutine reports on the meters that have been requested for
1858 : // reporting on each hour.
1859 :
1860 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1861 : bool PrintTimeStamp;
1862 : int CurDayType;
1863 3539 : auto &op = state.dataOutputProcessor;
1864 3539 : auto &rf = state.dataResultsFramework->resultsFramework;
1865 3539 : auto &rfMeters = rf->Meters[(int)freq];
1866 :
1867 3539 : assert(freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
1868 : freq == ReportFreq::Simulation);
1869 :
1870 3539 : if (!rfMeters.dataFrameEnabled()) {
1871 2597 : rf->initializeMeters(op->meters, freq);
1872 : }
1873 :
1874 3539 : PrintTimeStamp = true;
1875 142300 : for (auto *meter : op->meters) {
1876 138761 : auto &period = meter->periods[(int)freq];
1877 :
1878 138761 : if (freq == ReportFreq::Simulation) {
1879 5272 : meter->periodLastSM.Value = period.Value;
1880 5272 : meter->periodLastSM.MinVal = period.MinVal;
1881 5272 : meter->periodLastSM.MinValDate = period.MinValDate;
1882 5272 : meter->periodLastSM.MaxVal = period.MaxVal;
1883 5272 : meter->periodLastSM.MaxValDate = period.MaxValDate;
1884 : }
1885 :
1886 138761 : if (!period.Rpt && !period.accRpt) {
1887 135843 : continue;
1888 : }
1889 2918 : if (PrintTimeStamp) {
1890 977 : CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
1891 :
1892 977 : switch (freq) {
1893 :
1894 963 : case ReportFreq::Hour: {
1895 5778 : WriteTimeStampFormatData(state,
1896 963 : state.files.mtr,
1897 : freq,
1898 963 : op->freqStampReportNums[(int)freq],
1899 963 : state.dataGlobal->DayOfSimChr,
1900 963 : PrintTimeStamp && PrintTimeStampToSQL,
1901 963 : state.dataEnvrn->Month,
1902 963 : state.dataEnvrn->DayOfMonth,
1903 963 : state.dataGlobal->HourOfDay,
1904 : -1, // EndMinute
1905 : -1, // StartMinute
1906 963 : state.dataEnvrn->DSTIndicator,
1907 963 : Sched::dayTypeNames[CurDayType]);
1908 963 : } break;
1909 :
1910 3 : case ReportFreq::Day: {
1911 15 : WriteTimeStampFormatData(state,
1912 3 : state.files.mtr,
1913 : freq,
1914 3 : op->freqStampReportNums[(int)freq],
1915 3 : state.dataGlobal->DayOfSimChr,
1916 3 : PrintTimeStamp && PrintTimeStampToSQL,
1917 3 : state.dataEnvrn->Month,
1918 3 : state.dataEnvrn->DayOfMonth,
1919 : -1, // Hour
1920 : -1, // EndMinute
1921 : -1, // StartMinute
1922 3 : state.dataEnvrn->DSTIndicator,
1923 3 : Sched::dayTypeNames[CurDayType]);
1924 3 : } break;
1925 :
1926 5 : case ReportFreq::Month: {
1927 5 : WriteTimeStampFormatData(state,
1928 5 : state.files.mtr,
1929 : freq,
1930 5 : op->freqStampReportNums[(int)freq],
1931 5 : state.dataGlobal->DayOfSimChr,
1932 5 : PrintTimeStamp && PrintTimeStampToSQL,
1933 5 : state.dataEnvrn->Month);
1934 5 : } break;
1935 :
1936 1 : case ReportFreq::Year: {
1937 2 : WriteYearlyTimeStamp(state,
1938 1 : state.files.mtr,
1939 1 : op->freqStampReportNums[(int)freq],
1940 1 : state.dataGlobal->CalendarYearChr,
1941 1 : PrintTimeStamp && PrintTimeStampToSQL);
1942 1 : } break;
1943 :
1944 5 : case ReportFreq::Simulation: {
1945 5 : WriteTimeStampFormatData(state,
1946 5 : state.files.mtr,
1947 : freq,
1948 5 : op->freqStampReportNums[(int)freq],
1949 5 : state.dataGlobal->DayOfSimChr,
1950 5 : PrintTimeStamp && PrintTimeStampToSQL);
1951 5 : } break;
1952 :
1953 0 : default: {
1954 0 : } break;
1955 : } // switch (freq)
1956 :
1957 977 : if (rfMeters.dataFrameEnabled()) {
1958 977 : rfMeters.newRow(
1959 977 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
1960 : }
1961 977 : PrintTimeStamp = false;
1962 977 : PrintTimeStampToSQL = false;
1963 : }
1964 :
1965 2918 : if (period.Rpt) {
1966 2918 : period.WriteReportData(state, freq);
1967 2918 : rfMeters.pushVariableValue(period.RptNum, period.Value);
1968 2918 : period.Value = 0.0;
1969 :
1970 2918 : if (freq != ReportFreq::Hour) {
1971 34 : period.MinVal = MinSetValue;
1972 34 : period.MaxVal = MaxSetValue;
1973 : }
1974 : }
1975 :
1976 2918 : if (period.accRpt) {
1977 0 : WriteCumulativeReportMeterData(state, period.accRptNum, meter->periods[(int)ReportFreq::Simulation].Value, period.accRptFO);
1978 0 : rfMeters.pushVariableValue(period.accRptNum, meter->periods[(int)ReportFreq::Simulation].Value);
1979 : }
1980 3539 : } // for (meter)
1981 3539 : } // ReportMeters()
1982 :
1983 73 : void ReportForTabularReports(EnergyPlusData &state)
1984 : {
1985 :
1986 : // SUBROUTINE INFORMATION:
1987 : // AUTHOR Linda Lawrie
1988 : // DATE WRITTEN August 2013
1989 : // MODIFIED na
1990 : // RE-ENGINEERED na
1991 :
1992 : // PURPOSE OF THIS SUBROUTINE:
1993 : // This subroutine is called after all the simulation is done and before
1994 : // tabular reports in order to reduce the number of calls to the predefined routine
1995 : // for SM (Simulation period) meters, the value of the last calculation is stored
1996 : // in the data structure.
1997 :
1998 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1999 73 : auto const &op = state.dataOutputProcessor;
2000 :
2001 2908 : for (auto *meter : op->meters) {
2002 2835 : auto &period = meter->periodFinYrSM;
2003 :
2004 2835 : switch (meter->RT_forIPUnits) {
2005 1453 : case RT_IPUnits::Electricity: {
2006 2906 : OutputReportPredefined::PreDefTableEntry(
2007 1453 : state, state.dataOutRptPredefined->pdchEMelecannual, meter->Name, period.Value * Constant::convertJtoGJ);
2008 2906 : OutputReportPredefined::PreDefTableEntry(
2009 1453 : state, state.dataOutRptPredefined->pdchEMelecminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2010 2906 : OutputReportPredefined::PreDefTableEntry(
2011 4359 : state, state.dataOutRptPredefined->pdchEMelecminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2012 2906 : OutputReportPredefined::PreDefTableEntry(
2013 1453 : state, state.dataOutRptPredefined->pdchEMelecmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2014 2906 : OutputReportPredefined::PreDefTableEntry(
2015 4359 : state, state.dataOutRptPredefined->pdchEMelecmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2016 1453 : } break;
2017 :
2018 55 : case RT_IPUnits::Gas: {
2019 110 : OutputReportPredefined::PreDefTableEntry(
2020 55 : state, state.dataOutRptPredefined->pdchEMgasannual, meter->Name, period.Value * Constant::convertJtoGJ);
2021 110 : OutputReportPredefined::PreDefTableEntry(
2022 55 : state, state.dataOutRptPredefined->pdchEMgasminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2023 110 : OutputReportPredefined::PreDefTableEntry(
2024 165 : state, state.dataOutRptPredefined->pdchEMgasminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2025 110 : OutputReportPredefined::PreDefTableEntry(
2026 55 : state, state.dataOutRptPredefined->pdchEMgasmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2027 110 : OutputReportPredefined::PreDefTableEntry(
2028 165 : state, state.dataOutRptPredefined->pdchEMgasmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2029 55 : } break;
2030 :
2031 36 : case RT_IPUnits::Cooling: {
2032 72 : OutputReportPredefined::PreDefTableEntry(
2033 36 : state, state.dataOutRptPredefined->pdchEMcoolannual, meter->Name, period.Value * Constant::convertJtoGJ);
2034 72 : OutputReportPredefined::PreDefTableEntry(
2035 36 : state, state.dataOutRptPredefined->pdchEMcoolminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2036 72 : OutputReportPredefined::PreDefTableEntry(
2037 108 : state, state.dataOutRptPredefined->pdchEMcoolminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2038 72 : OutputReportPredefined::PreDefTableEntry(
2039 36 : state, state.dataOutRptPredefined->pdchEMcoolmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2040 72 : OutputReportPredefined::PreDefTableEntry(
2041 108 : state, state.dataOutRptPredefined->pdchEMcoolmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2042 36 : } break;
2043 :
2044 64 : case RT_IPUnits::Water: {
2045 64 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMwaterannual, meter->Name, period.Value);
2046 128 : OutputReportPredefined::PreDefTableEntry(
2047 64 : state, state.dataOutRptPredefined->pdchEMwaterminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2048 128 : OutputReportPredefined::PreDefTableEntry(
2049 192 : state, state.dataOutRptPredefined->pdchEMwaterminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2050 128 : OutputReportPredefined::PreDefTableEntry(
2051 64 : state, state.dataOutRptPredefined->pdchEMwatermaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2052 128 : OutputReportPredefined::PreDefTableEntry(
2053 192 : state, state.dataOutRptPredefined->pdchEMwatermaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2054 64 : } break;
2055 :
2056 146 : case RT_IPUnits::OtherKG: {
2057 146 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherKGannual, meter->Name, period.Value);
2058 146 : OutputReportPredefined::PreDefTableEntry(
2059 146 : state, state.dataOutRptPredefined->pdchEMotherKGminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2060 292 : OutputReportPredefined::PreDefTableEntry(
2061 438 : state, state.dataOutRptPredefined->pdchEMotherKGminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2062 146 : OutputReportPredefined::PreDefTableEntry(
2063 146 : state, state.dataOutRptPredefined->pdchEMotherKGmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2064 292 : OutputReportPredefined::PreDefTableEntry(
2065 438 : state, state.dataOutRptPredefined->pdchEMotherKGmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2066 146 : } break;
2067 :
2068 0 : case RT_IPUnits::OtherM3: {
2069 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherM3annual, meter->Name, period.Value, 3);
2070 0 : OutputReportPredefined::PreDefTableEntry(
2071 0 : state, state.dataOutRptPredefined->pdchEMotherM3minvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2072 0 : OutputReportPredefined::PreDefTableEntry(
2073 0 : state, state.dataOutRptPredefined->pdchEMotherM3minvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2074 0 : OutputReportPredefined::PreDefTableEntry(
2075 0 : state, state.dataOutRptPredefined->pdchEMotherM3maxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2076 0 : OutputReportPredefined::PreDefTableEntry(
2077 0 : state, state.dataOutRptPredefined->pdchEMotherM3maxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2078 0 : } break;
2079 :
2080 0 : case RT_IPUnits::OtherL: {
2081 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherLannual, meter->Name, period.Value, 3);
2082 0 : OutputReportPredefined::PreDefTableEntry(
2083 0 : state, state.dataOutRptPredefined->pdchEMotherLminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2084 0 : OutputReportPredefined::PreDefTableEntry(
2085 0 : state, state.dataOutRptPredefined->pdchEMotherLminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2086 0 : OutputReportPredefined::PreDefTableEntry(
2087 0 : state, state.dataOutRptPredefined->pdchEMotherLmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2088 0 : OutputReportPredefined::PreDefTableEntry(
2089 0 : state, state.dataOutRptPredefined->pdchEMotherLmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2090 0 : } break;
2091 :
2092 1081 : default: {
2093 2162 : OutputReportPredefined::PreDefTableEntry(
2094 1081 : state, state.dataOutRptPredefined->pdchEMotherJannual, meter->Name, period.Value * Constant::convertJtoGJ);
2095 2162 : OutputReportPredefined::PreDefTableEntry(
2096 1081 : state, state.dataOutRptPredefined->pdchEMotherJminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2097 2162 : OutputReportPredefined::PreDefTableEntry(
2098 3243 : state, state.dataOutRptPredefined->pdchEMotherJminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2099 2162 : OutputReportPredefined::PreDefTableEntry(
2100 1081 : state, state.dataOutRptPredefined->pdchEMotherJmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2101 2162 : OutputReportPredefined::PreDefTableEntry(
2102 3243 : state, state.dataOutRptPredefined->pdchEMotherJmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2103 1081 : } break;
2104 : } // switch
2105 73 : } // for (meter)
2106 73 : } // ReportForTabularReports()
2107 :
2108 5695 : std::string DateToStringWithMonth(int const codedDate) // word containing encoded month, day, hour, minute
2109 : {
2110 : // SUBROUTINE INFORMATION:
2111 : // AUTHOR Jason Glazer
2112 : // DATE WRITTEN August 2003
2113 : // MODIFIED na
2114 : // RE-ENGINEERED na
2115 :
2116 : // PURPOSE OF THIS SUBROUTINE:
2117 : // Convert the coded date format into a usable
2118 : // string
2119 :
2120 5695 : if (codedDate == 0) {
2121 2 : return "-";
2122 : }
2123 :
2124 : static constexpr std::string_view DateFmt("{:02}-{:3}-{:02}:{:02}");
2125 :
2126 : // ((month*100 + day)*100 + hour)*100 + minute
2127 : int Month; // month in integer format (1-12)
2128 : int Day; // day in integer format (1-31)
2129 : int Hour; // hour in integer format (1-24)
2130 : int Minute; // minute in integer format (0:59)
2131 :
2132 5694 : General::DecodeMonDayHrMin(codedDate, Month, Day, Hour, Minute);
2133 :
2134 5694 : if (Month < 1 || Month > 12) {
2135 1450 : return "-";
2136 : }
2137 4969 : if (Day < 1 || Day > 31) {
2138 6 : return "-";
2139 : }
2140 4966 : if (Hour < 1 || Hour > 24) {
2141 6 : return "-";
2142 : }
2143 4963 : if (Minute < 0 || Minute > 60) {
2144 0 : return "-";
2145 : }
2146 :
2147 4963 : --Hour;
2148 4963 : if (Minute == 60) {
2149 671 : ++Hour;
2150 671 : Minute = 0;
2151 : }
2152 :
2153 4963 : std::string monthName;
2154 4963 : switch (Month) {
2155 1406 : case 1:
2156 1406 : monthName = "JAN";
2157 1406 : break;
2158 1 : case 2:
2159 1 : monthName = "FEB";
2160 1 : break;
2161 1 : case 3:
2162 1 : monthName = "MAR";
2163 1 : break;
2164 1 : case 4:
2165 1 : monthName = "APR";
2166 1 : break;
2167 1 : case 5:
2168 1 : monthName = "MAY";
2169 1 : break;
2170 1 : case 6:
2171 1 : monthName = "JUN";
2172 1 : break;
2173 2262 : case 7:
2174 2262 : monthName = "JUL";
2175 2262 : break;
2176 1 : case 8:
2177 1 : monthName = "AUG";
2178 1 : break;
2179 1 : case 9:
2180 1 : monthName = "SEP";
2181 1 : break;
2182 1 : case 10:
2183 1 : monthName = "OCT";
2184 1 : break;
2185 1 : case 11:
2186 1 : monthName = "NOV";
2187 1 : break;
2188 1286 : case 12:
2189 1286 : monthName = "DEC";
2190 1286 : break;
2191 0 : default:
2192 0 : assert(false);
2193 : }
2194 :
2195 4963 : return format(DateFmt, Day, monthName, Hour, Minute);
2196 4963 : }
2197 :
2198 73776 : std::string OutVar::multiplierString() const
2199 : {
2200 73411 : return (ZoneMult == 1 && ZoneListMult == 1)
2201 73776 : ? ""
2202 293325 : : format(" * {} (Zone Multiplier = {}, Zone List Multiplier = {})", ZoneMult * ZoneListMult, ZoneMult, ZoneListMult);
2203 : }
2204 :
2205 99 : void ReportMeterDetails(EnergyPlusData &state)
2206 : {
2207 :
2208 : // SUBROUTINE INFORMATION:
2209 : // AUTHOR Linda Lawrie
2210 : // DATE WRITTEN January 2006
2211 :
2212 : // PURPOSE OF THIS SUBROUTINE:
2213 : // Writes the meter details report. This shows which variables are on
2214 : // meters as well as the meter contents.
2215 :
2216 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2217 99 : auto &op = state.dataOutputProcessor;
2218 :
2219 10807 : for (auto const *var : op->outVars) {
2220 :
2221 10708 : if (var->meterNums.empty()) {
2222 1112 : continue;
2223 : }
2224 :
2225 9596 : print(state.files.mtd,
2226 : "\n Meters for {},{} [{}]{}\n",
2227 9596 : var->ReportID,
2228 9596 : var->keyColonName,
2229 9596 : Constant::unitNames[(int)var->units],
2230 19192 : var->multiplierString());
2231 :
2232 73776 : for (int const meterNum : var->meterNums) {
2233 64180 : auto const *meter = op->meters[meterNum];
2234 :
2235 64180 : print(state.files.mtd,
2236 : " On{}Meter={} [{}]\n",
2237 128360 : (meter->type == MeterType::Normal) ? "" : "Custom",
2238 64180 : meter->Name,
2239 64180 : Constant::unitNames[(int)meter->units]);
2240 9596 : }
2241 99 : } // for (var)
2242 :
2243 3193 : for (auto const *meter : op->meters) {
2244 :
2245 3094 : print(state.files.mtd, "\n For Meter={} [{}]", meter->Name, Constant::unitNames[(int)meter->units]);
2246 3094 : if (meter->resource != Constant::eResource::Invalid) {
2247 3094 : print(state.files.mtd, ", ResourceType={}", Constant::eResourceNames[(int)meter->resource]);
2248 : }
2249 3094 : if (meter->endUseCat != EndUseCat::Invalid) {
2250 2003 : print(state.files.mtd, ", EndUse={}", endUseCatNames[(int)meter->endUseCat]);
2251 : }
2252 3094 : if (meter->group != Group::Invalid) {
2253 1302 : print(state.files.mtd, ", Group={}", groupNames[(int)meter->group]);
2254 : }
2255 3094 : print(state.files.mtd, ", contents are:\n");
2256 :
2257 3094 : if (meter->type == MeterType::Normal) {
2258 67274 : for (int srcVarNum : meter->srcVarNums) {
2259 64180 : auto const *var = op->outVars[srcVarNum];
2260 64180 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2261 3094 : }
2262 :
2263 0 : } else if (meter->type == MeterType::Custom) {
2264 0 : for (int srcVarNum : meter->srcVarNums) {
2265 0 : auto const *var = op->outVars[srcVarNum];
2266 0 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2267 0 : }
2268 :
2269 0 : } else if (meter->type == MeterType::CustomDec) {
2270 0 : print(state.files.mtd,
2271 : " Values for this meter will be Source Meter={}; but will be decremented by:\n",
2272 0 : op->meters[meter->decMeterNum]->Name);
2273 0 : for (int srcVarNum : meter->srcVarNums) {
2274 0 : auto const *var = op->outVars[srcVarNum];
2275 0 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2276 0 : }
2277 : }
2278 99 : } // for (meter)
2279 99 : } // ReportMeterDetails()
2280 :
2281 : // *****************************************************************************
2282 : // End of routines for Energy Meters implementation in EnergyPlus.
2283 : // *****************************************************************************
2284 :
2285 2480 : void WriteTimeStampFormatData(
2286 : EnergyPlusData &state,
2287 : InputOutputFile &outputFile,
2288 : ReportFreq const reportingInterval, // See Module Parameter Definitions for ReportEach, ReportTimeStep, ReportHourly, etc.
2289 : int const reportID, // The ID of the time stamp
2290 : std::string const &DayOfSimChr, // the number of days simulated so far
2291 : bool writeToSQL,
2292 : int const Month, // the month of the reporting interval
2293 : int const DayOfMonth, // The day of the reporting interval
2294 : int const Hour, // The hour of the reporting interval
2295 : Real64 const EndMinute, // The last minute in the reporting interval
2296 : Real64 const StartMinute, // The starting minute of the reporting interval
2297 : int const DST, // A flag indicating whether daylight savings time is observed
2298 : std::string_view const DayType // The day tied for the data (e.g., Monday)
2299 : )
2300 : {
2301 :
2302 : // FUNCTION INFORMATION:
2303 : // AUTHOR Greg Stark
2304 : // DATE WRITTEN July 2008
2305 : // MODIFIED na
2306 : // RE-ENGINEERED na
2307 :
2308 : // PURPOSE OF THIS FUNCTION:
2309 : // This function reports the timestamp data for the output processor
2310 : // Much of the code in this function was embedded in earlier versions of EnergyPlus
2311 : // and was moved to this location to simplify maintenance and to allow for data output
2312 : // to the SQL database
2313 :
2314 2510 : std::string reportStr = (reportID == -1) ? "" : std::to_string(reportID);
2315 :
2316 2480 : assert(reportStr.length() + DayOfSimChr.length() + (DayType.length()) + 26 < N_WriteTimeStampFormatData); // Check will fit in stamp size
2317 :
2318 2480 : if (!outputFile.good()) {
2319 0 : return;
2320 : }
2321 :
2322 2480 : auto &sql = state.dataSQLiteProcedures->sqlite;
2323 :
2324 2480 : switch (reportingInterval) {
2325 307 : case ReportFreq::EachCall:
2326 : case ReportFreq::TimeStep: {
2327 307 : assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && StartMinute != -1 && EndMinute != -1 && DST != -1 && !DayType.empty());
2328 307 : print<FormatSyntax::FMT>(outputFile,
2329 : "{},{},{:2d},{:2d},{:2d},{:2d},{:5.2f},{:5.2f},{}\n",
2330 : reportStr,
2331 : DayOfSimChr,
2332 : Month,
2333 : DayOfMonth,
2334 : DST,
2335 : Hour,
2336 : StartMinute,
2337 : EndMinute,
2338 : DayType);
2339 :
2340 307 : if (writeToSQL && sql) {
2341 398 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2342 : reportID,
2343 199 : state.dataGlobal->DayOfSim,
2344 199 : state.dataEnvrn->CurEnvirNum,
2345 199 : state.dataGlobal->CalendarYear,
2346 199 : state.dataEnvrn->CurrentYearIsLeapYear,
2347 : Month,
2348 : DayOfMonth,
2349 : Hour,
2350 : EndMinute,
2351 : StartMinute,
2352 : DST,
2353 : DayType,
2354 199 : state.dataGlobal->WarmupFlag);
2355 : }
2356 307 : } break;
2357 :
2358 2070 : case ReportFreq::Hour: {
2359 2070 : assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && DST != -1 && !DayType.empty());
2360 2070 : print<FormatSyntax::FMT>(outputFile,
2361 : "{},{},{:2d},{:2d},{:2d},{:2d},{:5.2f},{:5.2f},{}\n",
2362 : reportStr,
2363 : DayOfSimChr,
2364 : Month,
2365 : DayOfMonth,
2366 : DST,
2367 : Hour,
2368 0 : 0.0,
2369 2070 : 60.0,
2370 : DayType);
2371 2070 : if (writeToSQL && sql) {
2372 104 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2373 : reportID,
2374 52 : state.dataGlobal->DayOfSim,
2375 52 : state.dataEnvrn->CurEnvirNum,
2376 52 : state.dataGlobal->CalendarYear,
2377 52 : state.dataEnvrn->CurrentYearIsLeapYear,
2378 : Month,
2379 : DayOfMonth,
2380 : Hour,
2381 : -1, // EndMinute
2382 : -1, // StartMinute
2383 : DST,
2384 : DayType,
2385 52 : state.dataGlobal->WarmupFlag);
2386 : }
2387 2070 : } break;
2388 46 : case ReportFreq::Day: {
2389 46 : assert(Month != -1 && DayOfMonth != -1 && DST != -1 && !DayType.empty());
2390 46 : print<FormatSyntax::FMT>(outputFile, "{},{},{:2d},{:2d},{:2d},{}\n", reportStr, DayOfSimChr, Month, DayOfMonth, DST, DayType);
2391 46 : if (writeToSQL && sql) {
2392 8 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2393 : reportID,
2394 4 : state.dataGlobal->DayOfSim,
2395 4 : state.dataEnvrn->CurEnvirNum,
2396 4 : state.dataGlobal->CalendarYear,
2397 4 : state.dataEnvrn->CurrentYearIsLeapYear,
2398 : Month,
2399 : DayOfMonth,
2400 : -1, // Hour
2401 : -1, // EndMinute
2402 : -1, // StartMinute
2403 : DST,
2404 : DayType,
2405 4 : state.dataGlobal->WarmupFlag);
2406 : }
2407 46 : } break;
2408 :
2409 48 : case ReportFreq::Month: {
2410 48 : assert(Month != -1);
2411 48 : print<FormatSyntax::FMT>(outputFile, "{},{},{:2d}\n", reportStr, DayOfSimChr, Month);
2412 48 : if (writeToSQL && sql) {
2413 8 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2414 : reportID,
2415 4 : state.dataGlobal->DayOfSim,
2416 4 : state.dataEnvrn->CurEnvirNum,
2417 4 : state.dataGlobal->CalendarYear,
2418 4 : state.dataEnvrn->CurrentYearIsLeapYear,
2419 : Month);
2420 : }
2421 48 : } break;
2422 :
2423 8 : case ReportFreq::Simulation: {
2424 8 : print<FormatSyntax::FMT>(outputFile, "{},{}\n", reportStr, DayOfSimChr);
2425 8 : if (writeToSQL && sql) {
2426 8 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2427 : reportID,
2428 4 : state.dataGlobal->DayOfSim,
2429 4 : state.dataEnvrn->CurEnvirNum,
2430 4 : state.dataGlobal->CalendarYear,
2431 4 : state.dataEnvrn->CurrentYearIsLeapYear);
2432 : }
2433 8 : } break;
2434 1 : default: {
2435 1 : if (sql) {
2436 2 : sql->sqliteWriteMessage(
2437 2 : format<FormatSyntax::FMT>("Illegal reportingInterval passed to WriteTimeStampFormatData: {}", (int)reportingInterval));
2438 : }
2439 1 : } break;
2440 : } // switch (reportFreq)
2441 2480 : } // WriteTimeStampFormatData()
2442 :
2443 1 : void WriteYearlyTimeStamp(EnergyPlusData &state,
2444 : InputOutputFile &outputFile,
2445 : int reportID, // The ID of the time stamp
2446 : std::string const &yearOfSimChr, // the year of the simulation
2447 : bool writeToSQL)
2448 : {
2449 1 : print(outputFile, "{},{}\n", reportID, yearOfSimChr);
2450 1 : auto &sql = state.dataSQLiteProcedures->sqlite;
2451 1 : if (writeToSQL && sql) {
2452 1 : sql->createYearlyTimeIndexRecord(state.dataGlobal->CalendarYear, state.dataEnvrn->CurEnvirNum);
2453 : }
2454 1 : } // WriteYearlyTimeStamp()
2455 :
2456 5571 : void OutVar::writeReportDictionaryItem(EnergyPlusData &state)
2457 : {
2458 :
2459 : // SUBROUTINE INFORMATION:
2460 : // AUTHOR Greg Stark
2461 : // DATE WRITTEN August 2008
2462 : // MODIFIED April 2011; Linda Lawrie
2463 : // RE-ENGINEERED na
2464 :
2465 : // PURPOSE OF THIS SUBROUTINE:
2466 : // This subroutine writes the ESO data dictionary information to the output files
2467 : // and the SQL database
2468 :
2469 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2470 5571 : auto &rf = state.dataResultsFramework->resultsFramework;
2471 5571 : auto &sql = state.dataSQLiteProcedures->sqlite;
2472 :
2473 0 : std::string_view unitsString = (units == Constant::Units::customEMS && !unitNameCustomEMS.empty())
2474 5571 : ? unitNameCustomEMS
2475 5571 : : ((units == Constant::Units::Invalid) ? "" : Constant::unitNames[(int)units]);
2476 :
2477 11136 : std::string schedString = (sched != nullptr) ? sched->Name : "";
2478 :
2479 5571 : if (state.files.eso.good()) {
2480 5571 : print(state.files.eso,
2481 : "{},{},{},{} [{}]{}{}{}\n",
2482 5571 : ReportID,
2483 5571 : reportFreqArbitraryInts[(int)freq],
2484 5571 : key,
2485 5571 : name,
2486 : unitsString,
2487 5571 : reportingFrequencyNoticeStrings[(int)freq],
2488 11142 : !schedString.empty() ? "," : "",
2489 : schedString);
2490 : }
2491 :
2492 5571 : if (freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
2493 4456 : freq == ReportFreq::Simulation) {
2494 1140 : state.dataOutputProcessor->freqTrackingVariables[(int)freq] = true;
2495 : }
2496 :
2497 5571 : if (sql) {
2498 138 : sql->createSQLiteReportDictionaryRecord(ReportID, storeType, indexGroup, key, name, timeStepType, unitsString, freq, false, schedString);
2499 : }
2500 :
2501 : // add to ResultsFramework for output variable list, need to check RVI/MVI later
2502 5571 : rf->addReportVariable(key, name, unitsString, freq);
2503 :
2504 5571 : } // OutVar::WriteReportDictionaryItem()
2505 :
2506 127 : void WriteMeterDictionaryItem(EnergyPlusData &state,
2507 : ReportFreq const freq, // The reporting interval (e.g., hourly, daily)
2508 : StoreType const storeType,
2509 : int const reportID, // The reporting ID in for the variable
2510 : std::string const &indexGroup, // The reporting group for the variable
2511 : std::string const &meterName, // The variable's meter name
2512 : Constant::Units const units, // The variables units
2513 : bool const cumulativeMeterFlag, // A flag indicating cumulative data
2514 : bool const meterFileOnlyFlag // A flag indicating whether the data is to be written to standard output
2515 : )
2516 : {
2517 :
2518 : // SUBROUTINE INFORMATION:
2519 : // AUTHOR Greg Stark
2520 : // DATE WRITTEN August 2008
2521 : // MODIFIED April 2011; Linda Lawrie
2522 : // RE-ENGINEERED na
2523 :
2524 : // PURPOSE OF THIS SUBROUTINE:
2525 : // The subroutine writes meter data dictionary information to the output files
2526 : // and the SQL database. Much of the code here was embedded in other subroutines
2527 : // and was moved here for the purposes of ease of maintenance and to allow easy
2528 : // data reporting to the SQL database
2529 :
2530 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2531 127 : auto &rf = state.dataResultsFramework->resultsFramework;
2532 127 : auto &sql = state.dataSQLiteProcedures->sqlite;
2533 :
2534 127 : std::string FreqString = std::string(reportingFrequencyNoticeStrings[(int)freq]);
2535 127 : std::string FreqString2 = FreqString.substr(0, index(FreqString, '['));
2536 :
2537 127 : const auto print_meter = [&](EnergyPlusData &state, const int frequency) {
2538 164 : const auto out = [&](InputOutputFile &of) {
2539 164 : if (of.good()) {
2540 164 : if (cumulativeMeterFlag) {
2541 : static constexpr std::string_view fmt = "{},{},Cumulative {} [{}]{}\n";
2542 18 : print(of, fmt, reportID, 1, meterName, Constant::unitNames[(int)units], FreqString2);
2543 : } else {
2544 : static constexpr std::string_view fmt = "{},{},{} [{}]{}\n";
2545 146 : print(of, fmt, reportID, frequency, meterName, Constant::unitNames[(int)units], FreqString);
2546 : }
2547 : }
2548 164 : };
2549 :
2550 127 : out(state.files.mtr);
2551 127 : if (!meterFileOnlyFlag) {
2552 37 : out(state.files.eso);
2553 : }
2554 127 : };
2555 :
2556 127 : print_meter(state, reportFreqArbitraryInts[(int)freq]);
2557 :
2558 : static constexpr std::string_view keyedValueStringCum("Cumulative ");
2559 : static constexpr std::string_view keyedValueStringNon;
2560 127 : std::string_view const keyedValueString(cumulativeMeterFlag ? keyedValueStringCum : keyedValueStringNon);
2561 :
2562 127 : if (sql) {
2563 122 : sql->createSQLiteReportDictionaryRecord(
2564 61 : reportID, storeType, indexGroup, keyedValueString, meterName, TimeStepType::Zone, Constant::unitNames[(int)units], freq, true);
2565 : }
2566 :
2567 : // add to ResultsFramework for output variable list, need to check RVI/MVI later
2568 127 : rf->addReportMeter(meterName, Constant::unitNames[(int)units], freq);
2569 :
2570 127 : } // WriteMeterDictionaryItem()
2571 :
2572 12363 : void OutVar::writeOutput(EnergyPlusData &state,
2573 : ReportFreq const reportFreq // The report type or interval (e.g., hourly)
2574 : )
2575 : {
2576 :
2577 : // SUBROUTINE INFORMATION:
2578 : // AUTHOR Greg Stark
2579 : // DATE WRITTEN August 2008
2580 : // MODIFIED April 2011; Linda Lawrie, December 2017; Jason DeGraw
2581 : // RE-ENGINEERED na
2582 :
2583 : // PURPOSE OF THIS SUBROUTINE:
2584 : // This subroutine writes real report variable data to the output file and
2585 : // SQL database. Much of the code here was an included in earlier versions
2586 : // of the UpdateDataandReport subroutine. The code was moved to facilitate
2587 : // easier maintenance and writing of data to the SQL database.
2588 :
2589 12363 : if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) {
2590 0 : return;
2591 : }
2592 :
2593 12363 : if (!Report || freq != reportFreq || !Stored) {
2594 11237 : return;
2595 : }
2596 :
2597 1126 : if (NumStored > 0.0) {
2598 1126 : writeReportData(state);
2599 1126 : ++state.dataGlobal->StdOutputRecordCount;
2600 : }
2601 :
2602 1126 : StoreValue = 0.0;
2603 1126 : NumStored = 0.0;
2604 1126 : MinValue = MinSetValue;
2605 1126 : MaxValue = MaxSetValue;
2606 1126 : Stored = false;
2607 : } // OutVar::WriteOutput()
2608 :
2609 4 : void WriteCumulativeReportMeterData(EnergyPlusData &state,
2610 : int const reportID, // The variable's report ID
2611 : Real64 const repValue, // The variable's value
2612 : bool const meterOnlyFlag // A flag that indicates if the data should be written to standard output
2613 : )
2614 : {
2615 :
2616 : // SUBROUTINE INFORMATION:
2617 : // AUTHOR Greg Stark
2618 : // DATE WRITTEN July 2008
2619 : // MODIFIED na
2620 : // RE-ENGINEERED na
2621 :
2622 : // PURPOSE OF THIS SUBROUTINE:
2623 : // This subroutine writes the cumulative meter data to the output files and
2624 : // SQL database.
2625 :
2626 4 : std::string NumberOut; // Character for producing "number out"
2627 4 : auto &sql = state.dataSQLiteProcedures->sqlite;
2628 :
2629 4 : if (repValue == 0.0) {
2630 2 : NumberOut = "0.0";
2631 : } else {
2632 : char meterData[129];
2633 2 : dtoa(repValue, meterData);
2634 4 : NumberOut = std::string(meterData);
2635 : }
2636 :
2637 4 : if (sql) {
2638 4 : sql->createSQLiteReportDataRecord(reportID, repValue);
2639 : }
2640 :
2641 4 : if (state.files.mtr.good()) {
2642 4 : print(state.files.mtr, "{},{}\n", reportID, NumberOut);
2643 : }
2644 4 : ++state.dataGlobal->StdMeterRecordCount;
2645 :
2646 4 : if (!meterOnlyFlag) {
2647 2 : if (state.files.eso.good()) {
2648 2 : print(state.files.eso, "{},{}\n", reportID, NumberOut);
2649 : }
2650 2 : ++state.dataGlobal->StdOutputRecordCount;
2651 : }
2652 4 : } // WriteCumulativeReportMeterData()
2653 :
2654 3038 : void MeterPeriod::WriteReportData(EnergyPlusData &state, ReportFreq const freq)
2655 : {
2656 :
2657 : // SUBROUTINE INFORMATION:
2658 : // AUTHOR Greg Stark
2659 : // DATE WRITTEN July 2008
2660 :
2661 : // PURPOSE OF THIS SUBROUTINE:
2662 : // This subroutine writes for the non-cumulative meter data to the output files and
2663 : // SQL database.
2664 :
2665 3038 : auto &sql = state.dataSQLiteProcedures->sqlite;
2666 :
2667 3038 : std::string NumberOut;
2668 :
2669 3038 : if (Value == 0.0) {
2670 2366 : NumberOut = "0.0";
2671 : } else {
2672 : char tmp[128];
2673 672 : dtoa(Value, tmp);
2674 1344 : NumberOut = std::string(tmp);
2675 : }
2676 :
2677 3038 : if (sql) {
2678 153 : sql->createSQLiteReportDataRecord(RptNum, Value, freq, MinVal, MinValDate, MaxVal, MaxValDate, state.dataGlobal->MinutesInTimeStep);
2679 : }
2680 :
2681 3038 : if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
2682 2998 : if (state.files.mtr.good()) {
2683 2998 : print(state.files.mtr, "{},{}\n", RptNum, NumberOut);
2684 : }
2685 2998 : ++state.dataGlobal->StdMeterRecordCount;
2686 3111 : if (state.files.eso.good() && !RptFO) {
2687 113 : print(state.files.eso, "{},{}\n", RptNum, NumberOut);
2688 113 : ++state.dataGlobal->StdOutputRecordCount;
2689 : }
2690 : } else { // if ( ( reportingInterval == ReportDaily ) || ( reportingInterval == ReportMonthly ) || ( reportingInterval == ReportSim ) ) {
2691 : // // 2, 3, 4
2692 : // Append the min and max strings with date information
2693 : char minValString[128], maxValString[128];
2694 40 : dtoa(MinVal, minValString);
2695 40 : dtoa(MaxVal, maxValString);
2696 :
2697 40 : std::string minDateString = produceDateString(MinValDate, freq);
2698 40 : std::string maxDateString = produceDateString(MaxValDate, freq);
2699 :
2700 40 : if (state.files.mtr.good()) {
2701 40 : print(state.files.mtr, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
2702 : }
2703 :
2704 40 : ++state.dataGlobal->StdMeterRecordCount;
2705 40 : if (state.files.eso.good() && !RptFO) {
2706 9 : print(state.files.eso, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
2707 9 : ++state.dataGlobal->StdOutputRecordCount;
2708 : }
2709 40 : }
2710 3038 : } // MeterPeriod::WriteReportData()
2711 :
2712 32452 : void WriteNumericData(EnergyPlusData &state,
2713 : int const reportID, // The variable's reporting ID
2714 : Real64 const repValue // The variable's value
2715 : )
2716 : {
2717 : // SUBROUTINE INFORMATION:
2718 : // AUTHOR Mark Adams
2719 : // DATE WRITTEN May 2016
2720 : // MODIFIED na
2721 : // RE-ENGINEERED na
2722 :
2723 : // PURPOSE:
2724 : // This subroutine writes real data to the output files and
2725 : // SQL database.
2726 : // This is a refactor of WriteRealData.
2727 : //
2728 : // Much of the code here was an included in earlier versions
2729 : // of the UpdateDataandReport subroutine. The code was moved to facilitate
2730 : // easier maintenance and writing of data to the SQL database.
2731 32452 : auto &sql = state.dataSQLiteProcedures->sqlite;
2732 :
2733 32452 : if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) {
2734 0 : return;
2735 : }
2736 :
2737 32452 : if (sql) {
2738 3123 : sql->createSQLiteReportDataRecord(reportID, repValue);
2739 : }
2740 :
2741 32452 : if (state.files.eso.good()) {
2742 : char numericData[129];
2743 32452 : dtoa(repValue, numericData);
2744 32452 : print<FormatSyntax::FMT>(state.files.eso, "{},{}\n", reportID, numericData);
2745 : }
2746 : } // WriteNumericData()
2747 :
2748 5 : void WriteNumericData(EnergyPlusData &state,
2749 : int const reportID, // The variable's reporting ID
2750 : int32_t const repValue // The variable's value
2751 : )
2752 : {
2753 : // SUBROUTINE INFORMATION:
2754 : // AUTHOR Mark Adams
2755 : // DATE WRITTEN May 2016
2756 : // MODIFIED na
2757 : // RE-ENGINEERED na
2758 :
2759 : // PURPOSE:
2760 : // This subroutine writes real data to the output files and
2761 : // SQL database.
2762 : // This is a refactor of WriteIntegerData.
2763 : //
2764 : // Much of the code here was an included in earlier versions
2765 : // of the UpdateDataandReport subroutine. The code was moved to facilitate
2766 : // easier maintenance and writing of data to the SQL database.
2767 :
2768 : // i32toa(repValue, state.dataOutputProcessor->s_WriteNumericData);
2769 5 : auto &sql = state.dataSQLiteProcedures->sqlite;
2770 :
2771 5 : if (sql) {
2772 5 : sql->createSQLiteReportDataRecord(reportID, repValue);
2773 : }
2774 :
2775 5 : if (state.files.eso.good()) {
2776 5 : print<FormatSyntax::FMT>(state.files.eso, "{},{}\n", reportID, fmt::format_int(repValue).c_str());
2777 : }
2778 5 : } // WriteNumericData()
2779 :
2780 1153 : void OutVar::writeReportData(EnergyPlusData &state)
2781 : {
2782 :
2783 : // SUBROUTINE INFORMATION:
2784 :
2785 : // PURPOSE OF THIS SUBROUTINE:
2786 : // This subroutine writes averaged integer data to the output files and
2787 : // SQL database. It supports the WriteIntegerVariableOutput subroutine.
2788 : // Much of the code here was an included in earlier versions
2789 : // of the UpdateDataandReport subroutine. The code was moved to facilitate
2790 : // easier maintenance and writing of data to the SQL database.
2791 :
2792 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2793 1153 : auto &rf = state.dataResultsFramework->resultsFramework;
2794 1153 : auto &sql = state.dataSQLiteProcedures->sqlite;
2795 :
2796 1153 : Real64 repVal = (storeType == StoreType::Average) ? (StoreValue / NumStored) : StoreValue;
2797 :
2798 : // Append the min and max strings with date information
2799 1153 : if (rf->timeSeriesEnabled() &&
2800 0 : (freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year || freq == ReportFreq::Simulation)) {
2801 : // add to daily TS data store
2802 0 : rf->freqTSData[(int)freq].pushVariableValue(ReportID, repVal);
2803 : }
2804 :
2805 1153 : if (sql) {
2806 33 : sql->createSQLiteReportDataRecord(ReportID, repVal, freq, MinValue, minValueDate, MaxValue, maxValueDate);
2807 : }
2808 :
2809 1153 : if (state.files.eso.good()) {
2810 1153 : std::string NumberOut;
2811 1153 : if (varType == VariableType::Real) {
2812 : char tmp[128];
2813 1059 : dtoa(repVal, tmp);
2814 2118 : NumberOut = std::string(tmp);
2815 : } else {
2816 : // Can someone explain why we are printing integers as
2817 : // floats and why we are doing it differently than
2818 : // floats?
2819 176 : NumberOut = (repVal == 0.0) ? "0.0" : format("{:f}", repVal);
2820 : }
2821 :
2822 1153 : if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
2823 14 : print(state.files.eso, "{},{}\n", ReportID, NumberOut);
2824 : } else {
2825 : char minValString[128], maxValString[128];
2826 1139 : dtoa(MinValue, minValString);
2827 1139 : dtoa(MaxValue, maxValString);
2828 :
2829 1139 : std::string minDateString = produceDateString(minValueDate, freq);
2830 1139 : std::string maxDateString = produceDateString(maxValueDate, freq);
2831 :
2832 1139 : print(state.files.eso, "{},{},{},{},{},{}\n", ReportID, NumberOut, minValString, minDateString, maxValString, maxDateString);
2833 1139 : }
2834 1153 : }
2835 1153 : } // OutVar::WriteReportData()
2836 :
2837 11 : int DetermineIndexGroupKeyFromMeterName([[maybe_unused]] EnergyPlusData &state, std::string const &meterName) // the meter name
2838 : {
2839 :
2840 : // FUNCTION INFORMATION:
2841 : // AUTHOR Greg Stark
2842 : // DATE WRITTEN May 2009
2843 :
2844 : // PURPOSE OF THIS FUNCTION:
2845 : // This function attemps to guess determine how a meter variable should be
2846 : // grouped. It does this by parsing the meter name and then assigns a
2847 : // indexGroupKey based on the name
2848 :
2849 : // Facility indices are in the 100s
2850 11 : if (has(meterName, "Electricity:Facility")) {
2851 1 : return 100;
2852 10 : } else if (has(meterName, "NaturalGas:Facility")) {
2853 1 : return 101;
2854 9 : } else if (has(meterName, "DistricHeatingWater:Facility")) {
2855 1 : return 102;
2856 8 : } else if (has(meterName, "DistricCooling:Facility")) {
2857 1 : return 103;
2858 7 : } else if (has(meterName, "ElectricityNet:Facility")) {
2859 1 : return 104;
2860 :
2861 : // Building indices are in the 200s
2862 6 : } else if (has(meterName, "Electricity:Building")) {
2863 1 : return 201;
2864 5 : } else if (has(meterName, "NaturalGas:Building")) {
2865 1 : return 202;
2866 :
2867 : // HVAC indices are in the 300s
2868 4 : } else if (has(meterName, "Electricity:HVAC")) {
2869 1 : return 301;
2870 :
2871 : // InteriorLights:Electricity:Zone indices are in the 500s
2872 3 : } else if (has(meterName, "InteriorLights:Electricity:Zone")) {
2873 1 : return 501;
2874 :
2875 : // InteriorLights:Electricity indices are in the 400s
2876 2 : } else if (has(meterName, "InteriorLights:Electricity")) {
2877 1 : return 401;
2878 :
2879 : // Unknown items have negative indices
2880 : } else {
2881 1 : return -11;
2882 : }
2883 :
2884 : return -1;
2885 : } // DetermineIndexGroupKeyFromMeterName()
2886 :
2887 97 : std::string DetermineIndexGroupFromMeterGroup(Meter const *meter) // the meter
2888 : {
2889 :
2890 : // FUNCTION INFORMATION:
2891 : // AUTHOR Greg Stark
2892 : // DATE WRITTEN May 2009
2893 : // MODIFIED na
2894 : // RE-ENGINEERED na
2895 :
2896 : // PURPOSE OF THIS FUNCTION:
2897 : // This function attemps to determine how a meter variable should be
2898 : // grouped. It does this by parsing the meter group
2899 :
2900 : // Return value
2901 97 : std::string indexGroup;
2902 :
2903 97 : if (meter->group != Group::Invalid) {
2904 32 : indexGroup = groupNames[(int)meter->group];
2905 : } else {
2906 65 : indexGroup = "Facility";
2907 : }
2908 :
2909 97 : if (meter->resource != Constant::eResource::Invalid) {
2910 97 : indexGroup += format(":{}", Constant::eResourceNames[(int)meter->resource]);
2911 : }
2912 :
2913 97 : if (meter->endUseCat != EndUseCat::Invalid) {
2914 28 : indexGroup += format(":{}", endUseCatNames[(int)meter->endUseCat]);
2915 : }
2916 :
2917 97 : if (len(meter->EndUseSub) > 0) {
2918 0 : indexGroup += ":" + meter->EndUseSub;
2919 : }
2920 :
2921 97 : return indexGroup;
2922 0 : } // DetermineIndexGroupFromMeterGroup()
2923 :
2924 23 : void SetInternalVariableValue(EnergyPlusData &state,
2925 : OutputProcessor::VariableType const varType, // 1=integer, 2=real, 3=meter
2926 : int const keyVarIndex, // Array index
2927 : Real64 const SetRealVal, // real value to set, if type is real or meter
2928 : int const SetIntVal // integer value to set if type is integer
2929 : )
2930 : {
2931 :
2932 : // SUBROUTINE INFORMATION:
2933 : // AUTHOR B. Griffith
2934 : // DATE WRITTEN August 2012
2935 :
2936 : // PURPOSE OF THIS SUBROUTINE:
2937 : // This is a simple set routine for output pointers
2938 : // It is intended for special use to reinitializations those pointers used for EMS sensors
2939 :
2940 : // METHODOLOGY EMPLOYED:
2941 : // given a variable type and variable index,
2942 : // assign the pointers the values passed in.
2943 :
2944 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2945 23 : auto &op = state.dataOutputProcessor;
2946 :
2947 23 : if (varType == VariableType::Integer) {
2948 0 : OutVarInt *varInt = dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]);
2949 0 : assert(varInt != nullptr);
2950 0 : *varInt->Which = SetIntVal;
2951 23 : } else if (varType == VariableType::Real) {
2952 23 : OutVarReal *varReal = dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]);
2953 23 : assert(varReal != nullptr);
2954 23 : *varReal->Which = SetRealVal;
2955 0 : } else if (varType == VariableType::Meter) {
2956 0 : op->meters[keyVarIndex]->CurTSValue = SetRealVal;
2957 : }
2958 23 : } // SetInternalVariableValue()
2959 :
2960 : // returns the unit string for a DDVariableTypes item and custom string when customEMS is used
2961 5 : std::string unitStringFromDDitem(EnergyPlusData &state, int const ddNum // index provided for DDVariableTypes
2962 : )
2963 : {
2964 : // This function is here just for unit test purposes
2965 5 : DDOutVar *ddVar = state.dataOutputProcessor->ddOutVars[ddNum];
2966 5 : Constant::Units units = ddVar->units;
2967 5 : return format(" [{}]", units == Constant::Units::customEMS ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)units]);
2968 : } // unitStringFromDDitem()
2969 :
2970 : } // namespace OutputProcessor
2971 :
2972 : // TODO: Probably move these to a different location
2973 :
2974 168254 : void SetupOutputVariable(EnergyPlusData &state,
2975 : std::string_view const name, // String Name of variable (with units)
2976 : Constant::Units const units, // Actual units corresponding to the actual variable
2977 : Real64 &ActualVariable, // Actual Variable, used to set up pointer
2978 : OutputProcessor::TimeStepType timeStep, // Zone, HeatBalance=1, HVAC, System, Plant=2
2979 : OutputProcessor::StoreType store, // State, Average=1, NonState, Sum=2
2980 : std::string const &key, // Associated Key for this variable
2981 : Constant::eResource resource, // Meter Resource Type (Electricity, Gas, etc)
2982 : OutputProcessor::Group group, // Meter Super Group Key (Building, System, Plant)
2983 : OutputProcessor::EndUseCat endUseCat, // Meter End Use Key (Lights, Heating, Cooling, etc)
2984 : std::string_view const EndUseSub, // Meter End Use Sub Key (General Lights, Task Lights, etc)
2985 : std::string const &zone, // Meter Zone Key (zone name)
2986 : int const ZoneMult, // Zone Multiplier, defaults to 1
2987 : int const ZoneListMult, // Zone List Multiplier, defaults to 1
2988 : std::string const &spaceType, // Space type (applicable for Building group only)
2989 : int const indexGroupKey, // Group identifier for SQL output
2990 : std::string_view const customUnitName, // the custom name for the units from EMS definition of units
2991 : OutputProcessor::ReportFreq freq // Internal use -- causes reporting at this frequency
2992 : )
2993 : {
2994 :
2995 : // SUBROUTINE INFORMATION:
2996 : // AUTHOR Linda K. Lawrie
2997 : // DATE WRITTEN December 1998
2998 : // MODIFIED January 2001; Implement Meters
2999 : // August 2008; Implement SQL output
3000 : // RE-ENGINEERED na
3001 :
3002 : // PURPOSE OF THIS SUBROUTINE:
3003 : // This subroutine sets up the variable data structure that will be used
3004 : // to track values of the output variables of EnergyPlus.
3005 :
3006 : // METHODOLOGY EMPLOYED:
3007 : // Pointers (as pointers), pointers (as indices), and lots of other KEWL data stuff.
3008 :
3009 : using namespace OutputProcessor;
3010 :
3011 168254 : auto &op = state.dataOutputProcessor;
3012 :
3013 168254 : if (!op->OutputInitialized) {
3014 841 : InitializeOutput(state);
3015 : }
3016 :
3017 168254 : std::vector<int> reqVarNums;
3018 :
3019 : // Determine whether to Report or not
3020 168254 : CheckReportVariable(state, name, key, reqVarNums);
3021 168254 : if (reqVarNums.empty()) {
3022 162781 : reqVarNums.push_back(-1);
3023 : }
3024 :
3025 : // Is this redundant with CheckReportVariable?
3026 168254 : bool const ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
3027 :
3028 160011 : bool OnMeter = (resource != Constant::eResource::Invalid) || (endUseCat != EndUseCat::Invalid) || (!EndUseSub.empty()) ||
3029 328265 : (group != Group::Invalid) || (!zone.empty()) || (!spaceType.empty());
3030 :
3031 168254 : if (OnMeter && store == StoreType::Average) {
3032 0 : ShowSevereError(state, "Meters can only be \"Summed\" variables");
3033 0 : ShowContinueError(state, fmt::format("..reference variable={}:{}", key, name));
3034 0 : OnMeter = false;
3035 : }
3036 :
3037 168254 : int ddOutVarNum = AddDDOutVar(state, name, timeStep, store, VariableType::Real, units, customUnitName);
3038 168254 : auto *ddOutVar = op->ddOutVars[ddOutVarNum];
3039 :
3040 168254 : ++op->NumOfRVariable_Setup;
3041 :
3042 : // If we add any output variables here at all, the first one will be at this index
3043 168254 : int firstAddedOutVarNum = (int)op->outVars.size();
3044 :
3045 168254 : op->NumTotalRVariable += reqVarNums.size();
3046 :
3047 168254 : if (!OnMeter && !ThisOneOnTheList) {
3048 158693 : return;
3049 : }
3050 :
3051 9561 : if (store == StoreType::Sum) {
3052 8401 : ++op->NumOfRVariable_Sum;
3053 : }
3054 9561 : if (OnMeter) {
3055 8243 : ++op->NumOfRVariable_Meter;
3056 : }
3057 :
3058 19146 : for (int reqVarNum : reqVarNums) {
3059 :
3060 9585 : ++op->NumOfRVariable;
3061 :
3062 9585 : OutVarReal *var = new OutVarReal;
3063 9585 : op->outVars.push_back(var);
3064 :
3065 : // Link this keyed variable to the dictionary entry
3066 9585 : ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
3067 9585 : var->ddVarNum = ddOutVarNum;
3068 :
3069 9585 : var->varType = VariableType::Real;
3070 9585 : var->timeStepType = timeStep;
3071 9585 : var->storeType = store;
3072 9585 : var->name = name;
3073 9585 : var->nameUC = Util::makeUPPER(var->name);
3074 9585 : var->key = key;
3075 9585 : var->keyUC = Util::makeUPPER(key);
3076 9585 : var->keyColonName = fmt::format("{}:{}", key, name);
3077 9585 : var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
3078 9585 : var->units = units;
3079 9585 : if (units == Constant::Units::customEMS) {
3080 0 : var->unitNameCustomEMS = customUnitName;
3081 : }
3082 9585 : var->freq = freq;
3083 9585 : var->sched = nullptr;
3084 9585 : var->ReportID = ++op->ReportNumberCounter;
3085 9585 : var->Which = &ActualVariable;
3086 9585 : var->ZoneMult = ZoneMult;
3087 9585 : var->ZoneListMult = ZoneListMult;
3088 9585 : var->indexGroupKey = indexGroupKey;
3089 9585 : var->indexGroup = timeStepTypeNames[(int)var->timeStepType];
3090 :
3091 : // This is only done for the first variable in the list. It
3092 : // could be moved out of this loop entirely but then some
3093 : // numberings in unit tests would not line up
3094 9585 : if (OnMeter) {
3095 8243 : AttachMeters(state, units, resource, endUseCat, EndUseSub, group, zone, spaceType, firstAddedOutVarNum);
3096 8243 : OnMeter = false;
3097 : }
3098 :
3099 : // This is a dummy variable that is not being reported, it is only being used to feed a particular meter.
3100 9585 : if (reqVarNum == -1) {
3101 4088 : continue;
3102 : }
3103 :
3104 5497 : var->Report = true;
3105 :
3106 : // freq != ReportFreq::Hour
3107 5497 : if (freq == ReportFreq::Hour) {
3108 5497 : var->freq = op->reqVars[reqVarNum]->freq;
3109 5497 : var->sched = op->reqVars[reqVarNum]->sched;
3110 : }
3111 :
3112 5497 : var->writeReportDictionaryItem(state);
3113 9561 : }
3114 :
3115 168254 : } // SetupOutputVariable()
3116 :
3117 8115 : void SetupOutputVariable(EnergyPlusData &state,
3118 : std::string_view const name, // String Name of variable
3119 : Constant::Units const units, // Actual units corresponding to the actual variable
3120 : int &ActualVariable, // Actual Variable, used to set up pointer
3121 : OutputProcessor::TimeStepType timeStepType, // Zone, HeatBalance=1, HVAC, System, Plant=2
3122 : OutputProcessor::StoreType storeType, // State, Average=1, NonState, Sum=2
3123 : std::string const &key, // Associated Key for this variable
3124 : [[maybe_unused]] int const indexGroupKey, // Group identifier for SQL output
3125 : OutputProcessor::ReportFreq freq // Internal use -- causes reporting at this freqency
3126 : )
3127 : {
3128 :
3129 : // SUBROUTINE INFORMATION:
3130 : // AUTHOR Linda K. Lawrie
3131 : // DATE WRITTEN December 1998
3132 : // MODIFIED August 2008; Added SQL output capability
3133 : // RE-ENGINEERED na
3134 :
3135 : // PURPOSE OF THIS SUBROUTINE:
3136 : // This subroutine sets up the variable data structure that will be used
3137 : // to track values of the output variables of EnergyPlus.
3138 :
3139 : // METHODOLOGY EMPLOYED:
3140 : // Pointers (as pointers), pointers (as indices), and lots of other KEWL data stuff <-- LOL
3141 :
3142 : // Using/Aliasing
3143 : using namespace OutputProcessor;
3144 :
3145 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3146 8115 : auto &op = state.dataOutputProcessor;
3147 :
3148 8115 : if (!op->OutputInitialized) {
3149 25 : InitializeOutput(state);
3150 : }
3151 :
3152 : // Determine whether to Report or not
3153 8115 : std::vector<int> reqVarNums;
3154 8115 : CheckReportVariable(state, name, key, reqVarNums);
3155 8115 : if (reqVarNums.empty()) {
3156 8065 : reqVarNums.push_back(-1);
3157 : }
3158 :
3159 : // DataOutputs::OutputVariablesForSimulation is case-insentitive
3160 8115 : int ddOutVarNum = AddDDOutVar(state, name, timeStepType, storeType, VariableType::Integer, units);
3161 8115 : auto *ddOutVar = op->ddOutVars[ddOutVarNum];
3162 :
3163 8115 : ++op->NumOfIVariable_Setup;
3164 :
3165 8115 : op->NumTotalIVariable += (!reqVarNums.empty()) ? reqVarNums.size() : 1;
3166 8115 : bool ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
3167 8115 : if (!ThisOneOnTheList) {
3168 8065 : return;
3169 : }
3170 :
3171 50 : if (storeType == StoreType::Sum) {
3172 0 : ++op->NumOfIVariable_Sum;
3173 : }
3174 :
3175 100 : for (int reqVarNum : reqVarNums) {
3176 :
3177 50 : ++op->NumOfIVariable;
3178 :
3179 50 : OutVarInt *var = new OutVarInt;
3180 50 : op->outVars.push_back(var);
3181 : // Add to ddVar key list
3182 50 : ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
3183 :
3184 50 : var->varType = VariableType::Integer;
3185 50 : var->timeStepType = timeStepType;
3186 50 : var->storeType = storeType;
3187 50 : var->name = name;
3188 50 : var->nameUC = Util::makeUPPER(var->name);
3189 50 : var->key = key;
3190 50 : var->keyUC = Util::makeUPPER(key);
3191 50 : var->keyColonName = fmt::format("{}:{}", key, name);
3192 50 : var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
3193 50 : var->units = units;
3194 50 : var->ReportID = ++op->ReportNumberCounter;
3195 50 : var->Which = &ActualVariable;
3196 50 : var->indexGroupKey = -1;
3197 :
3198 50 : if (reqVarNum == -1) {
3199 0 : continue;
3200 : }
3201 :
3202 50 : var->Report = true;
3203 :
3204 50 : if (freq != ReportFreq::Hour) {
3205 0 : var->freq = freq;
3206 0 : var->sched = nullptr;
3207 : } else {
3208 50 : var->freq = op->reqVars[reqVarNum]->freq;
3209 50 : var->sched = op->reqVars[reqVarNum]->sched;
3210 : }
3211 :
3212 50 : var->writeReportDictionaryItem(state);
3213 50 : }
3214 8115 : } // SetOutputVariable()
3215 :
3216 41163 : void UpdateDataandReport(EnergyPlusData &state, OutputProcessor::TimeStepType const t_TimeStepTypeKey) // What kind of data to update (Zone, HVAC)
3217 : {
3218 :
3219 : // SUBROUTINE INFORMATION:
3220 : // AUTHOR Linda K. Lawrie
3221 : // DATE WRITTEN December 1998
3222 : // MODIFIED January 2001; Resolution integrated at the Zone TimeStep intervals
3223 : // MODIFIED August 2008; Added SQL output capability
3224 : // RE-ENGINEERED na
3225 :
3226 : // PURPOSE OF THIS SUBROUTINE:
3227 : // This subroutine writes the actual report variable (for user requested
3228 : // Report Variables) strings to the standard output file.
3229 :
3230 : // Using/Aliasing
3231 : using namespace OutputProcessor;
3232 : using General::EncodeMonDayHrMin;
3233 :
3234 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3235 41163 : bool TimePrint(true); // True if the time needs to be printed
3236 41163 : bool EndTimeStepFlag(false); // True when it's the end of the Zone Time Step
3237 41163 : auto &op = state.dataOutputProcessor;
3238 41163 : auto &rf = state.dataResultsFramework->resultsFramework;
3239 :
3240 41163 : if (t_TimeStepTypeKey != TimeStepType::Zone && t_TimeStepTypeKey != TimeStepType::System) {
3241 0 : ShowFatalError(state, "Invalid reporting requested -- UpdateDataAndReport");
3242 : }
3243 :
3244 : // Basic record keeping and report out if "detailed"
3245 41163 : Real64 StartMinute = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // StartMinute for UpdateData call
3246 41163 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute += (*op->TimeValue[(int)t_TimeStepTypeKey].TimeStep) * 60.0;
3247 63526 : if (t_TimeStepTypeKey == TimeStepType::System &&
3248 22363 : (op->TimeValue[(int)TimeStepType::System].CurMinute == op->TimeValue[(int)TimeStepType::Zone].CurMinute)) {
3249 0 : EndTimeStepFlag = true;
3250 41163 : } else if (t_TimeStepTypeKey == TimeStepType::Zone) {
3251 18800 : EndTimeStepFlag = true;
3252 : } else {
3253 22363 : EndTimeStepFlag = false;
3254 : }
3255 41163 : Real64 MinuteNow = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // What minute it is now
3256 :
3257 : int MDHM; // Month,Day,Hour,Minute
3258 41163 : EncodeMonDayHrMin(MDHM, state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, int(MinuteNow));
3259 41163 : TimePrint = true;
3260 :
3261 41163 : Real64 rxTime = (MinuteNow - StartMinute) /
3262 41163 : double(state.dataGlobal->MinutesInTimeStep); // (MinuteNow-StartMinute)/REAL(MinutesPerTimeStep,r64) - for execution time
3263 :
3264 41163 : if (rf->timeSeriesEnabled()) {
3265 : // R and I data frames for TimeStepType::TimeStepZone
3266 0 : if (!rf->detailedTSData[(int)t_TimeStepTypeKey].variablesScanned()) {
3267 0 : rf->initializeTSDataFrame(ReportFreq::EachCall, op->outVars, t_TimeStepTypeKey);
3268 : }
3269 : }
3270 :
3271 41163 : if (rf->timeSeriesEnabled()) {
3272 0 : rf->detailedTSData[(int)t_TimeStepTypeKey].newRow(state.dataEnvrn->Month,
3273 0 : state.dataEnvrn->DayOfMonth,
3274 0 : state.dataGlobal->HourOfDay,
3275 0 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
3276 0 : state.dataGlobal->CalendarYear);
3277 : }
3278 :
3279 : // Main "Record Keeping" Loops for R and I variables
3280 2415198 : for (auto *var : op->outVars) {
3281 2374035 : if (var->timeStepType != t_TimeStepTypeKey) {
3282 1166040 : continue;
3283 : }
3284 :
3285 1207995 : Real64 value = (var->varType == VariableType::Real) ? *(dynamic_cast<OutVarReal *>(var))->Which : *(dynamic_cast<OutVarInt *>(var))->Which;
3286 :
3287 1207995 : var->Stored = true;
3288 :
3289 1207995 : if (var->storeType == StoreType::Average) {
3290 818046 : Real64 CurVal = value * rxTime;
3291 : // TODO: Is this correct? Integer logic is different
3292 818046 : if (var->varType == VariableType::Real) {
3293 798846 : if (value > var->MaxValue) {
3294 86714 : var->MaxValue = value;
3295 86714 : var->maxValueDate = MDHM;
3296 : }
3297 798846 : if (value < var->MinValue) {
3298 205900 : var->MinValue = value;
3299 205900 : var->minValueDate = MDHM;
3300 : }
3301 : } else { // var->varType == VariableType::Integer
3302 19200 : if (CurVal > var->MaxValue) {
3303 84 : var->MaxValue = CurVal;
3304 84 : var->maxValueDate = MDHM;
3305 : }
3306 19200 : if (CurVal < var->MinValue) {
3307 86 : var->MinValue = CurVal;
3308 86 : var->minValueDate = MDHM;
3309 : }
3310 : }
3311 818046 : var->TSValue += CurVal;
3312 818046 : var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
3313 : } else {
3314 389949 : if (value > var->MaxValue) {
3315 16019 : var->MaxValue = value;
3316 16019 : var->maxValueDate = MDHM;
3317 : }
3318 389949 : if (value < var->MinValue) {
3319 5304 : var->MinValue = value;
3320 5304 : var->minValueDate = MDHM;
3321 : }
3322 389949 : var->TSValue += value;
3323 389949 : var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
3324 : }
3325 :
3326 : // End of "record keeping" Report if applicable
3327 1207995 : if (!var->Report) {
3328 355917 : continue;
3329 : }
3330 :
3331 852078 : if (var->sched != nullptr && var->sched->getCurrentVal() == 0.0) {
3332 0 : continue;
3333 : }
3334 :
3335 852078 : var->tsStored = true;
3336 852078 : if (!var->thisTSStored) {
3337 851582 : ++var->thisTSCount;
3338 851582 : var->thisTSStored = true;
3339 : }
3340 :
3341 852078 : if (var->freq != ReportFreq::EachCall) {
3342 850827 : continue;
3343 : }
3344 :
3345 1251 : if (TimePrint) {
3346 98 : if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
3347 0 : std::abs(op->LEndMin - op->TimeValue[(int)t_TimeStepTypeKey].CurMinute) > 0.001) {
3348 98 : int CurDayType = state.dataEnvrn->DayOfWeek;
3349 98 : if (state.dataEnvrn->HolidayIndex > 0) {
3350 96 : CurDayType = state.dataEnvrn->HolidayIndex;
3351 : }
3352 588 : WriteTimeStampFormatData(state,
3353 98 : state.files.eso,
3354 : ReportFreq::EachCall,
3355 98 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3356 98 : state.dataGlobal->DayOfSimChr,
3357 : true,
3358 98 : state.dataEnvrn->Month,
3359 98 : state.dataEnvrn->DayOfMonth,
3360 98 : state.dataGlobal->HourOfDay,
3361 98 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
3362 : StartMinute,
3363 98 : state.dataEnvrn->DSTIndicator,
3364 98 : Sched::dayTypeNames[CurDayType]);
3365 98 : op->LHourP = state.dataGlobal->HourOfDay;
3366 98 : op->LStartMin = StartMinute;
3367 98 : op->LEndMin = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute;
3368 : }
3369 98 : TimePrint = false;
3370 : }
3371 :
3372 1251 : WriteNumericData(state, var->ReportID, value);
3373 1251 : ++state.dataGlobal->StdOutputRecordCount;
3374 :
3375 1251 : if (rf->timeSeriesEnabled()) {
3376 0 : rf->detailedTSData[(int)t_TimeStepTypeKey].pushVariableValue(var->ReportID, value);
3377 : }
3378 41163 : } // for (var)
3379 :
3380 41163 : if (t_TimeStepTypeKey == TimeStepType::System) {
3381 41022 : return; // All other stuff happens at the "zone" time step call to this routine.
3382 : }
3383 :
3384 : // TimeStep Block (Report on Zone TimeStep)
3385 :
3386 18800 : if (EndTimeStepFlag) {
3387 18800 : if (rf->timeSeriesEnabled()) {
3388 0 : if (!rf->freqTSData[(int)ReportFreq::TimeStep].variablesScanned()) {
3389 0 : rf->initializeTSDataFrame(ReportFreq::TimeStep, op->outVars);
3390 : }
3391 0 : rf->freqTSData[(int)ReportFreq::TimeStep].newRow(state.dataEnvrn->Month,
3392 0 : state.dataEnvrn->DayOfMonth,
3393 0 : state.dataGlobal->HourOfDay,
3394 0 : op->TimeValue[(int)TimeStepType::Zone].CurMinute,
3395 0 : state.dataGlobal->CalendarYear);
3396 : }
3397 :
3398 : // Update meters on the TimeStep (Zone)
3399 18800 : if (op->meterValues.capacity() > 0) {
3400 630392 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
3401 614088 : auto *meter = op->meters[iMeter];
3402 614088 : if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
3403 1959822 : for (int srcVarNum : meter->srcVarNums) {
3404 1345734 : auto *var = op->outVars[srcVarNum];
3405 : // Separate the Zone variables from the HVAC variables using TimeStepType
3406 1345734 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3407 0 : continue;
3408 : }
3409 : // Add to the total all of the appropriate variables, make sure to use var->TSValue and not *var->Which
3410 1345734 : op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
3411 614088 : }
3412 614088 : } else if (meter->type == MeterType::CustomDec) {
3413 0 : auto *decMeter = op->meters[meter->decMeterNum];
3414 0 : for (int srcVarNum : decMeter->srcVarNums) {
3415 0 : auto *var = op->outVars[srcVarNum];
3416 0 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3417 0 : continue;
3418 : }
3419 0 : op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
3420 0 : }
3421 0 : for (int srcVarNum : meter->srcVarNums) {
3422 0 : auto *var = op->outVars[srcVarNum];
3423 0 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3424 0 : continue;
3425 : }
3426 0 : op->meterValues[iMeter] -= var->TSValue * var->ZoneMult * var->ZoneListMult;
3427 0 : }
3428 : } else {
3429 0 : assert(false);
3430 : }
3431 : } // for (iMeter)
3432 : } // if (op->meterValues.capacity() > 0)
3433 :
3434 1190099 : for (auto *var : op->outVars) {
3435 1171299 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3436 0 : continue;
3437 : }
3438 :
3439 1171299 : bool ReportNow = true;
3440 1171299 : if (var->sched != nullptr) {
3441 0 : ReportNow = (var->sched->getCurrentVal() != 0.0); // SetReportNow(RVar%SchedPtr)
3442 : }
3443 1171299 : if (!ReportNow || !var->Report) {
3444 319333 : var->TSValue = 0.0;
3445 : }
3446 : // IF (RVar%StoreType == AveragedVar) THEN
3447 : // RVar%Value=RVar%Value+RVar%TSValue/NumOfTimeStepInHour
3448 : // ELSE
3449 1171299 : var->Value += var->TSValue;
3450 : // ENDIF
3451 :
3452 1171299 : if (!ReportNow || !var->Report) {
3453 319333 : continue;
3454 : }
3455 :
3456 851966 : if (var->freq == ReportFreq::TimeStep) {
3457 8403 : if (TimePrint) {
3458 5 : if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
3459 1 : std::abs(op->LEndMin - op->TimeValue[(int)var->timeStepType].CurMinute) > 0.001) {
3460 3 : int CurDayType = state.dataEnvrn->DayOfWeek;
3461 3 : if (state.dataEnvrn->HolidayIndex > 0) {
3462 0 : CurDayType = state.dataEnvrn->HolidayIndex;
3463 : }
3464 18 : WriteTimeStampFormatData(state,
3465 3 : state.files.eso,
3466 : ReportFreq::EachCall,
3467 3 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3468 3 : state.dataGlobal->DayOfSimChr,
3469 : true,
3470 3 : state.dataEnvrn->Month,
3471 3 : state.dataEnvrn->DayOfMonth,
3472 3 : state.dataGlobal->HourOfDay,
3473 3 : op->TimeValue[(int)var->timeStepType].CurMinute,
3474 : StartMinute,
3475 3 : state.dataEnvrn->DSTIndicator,
3476 3 : Sched::dayTypeNames[CurDayType]);
3477 3 : op->LHourP = state.dataGlobal->HourOfDay;
3478 3 : op->LStartMin = StartMinute;
3479 3 : op->LEndMin = op->TimeValue[(int)var->timeStepType].CurMinute;
3480 : }
3481 4 : TimePrint = false;
3482 : } // if (TimePrint)
3483 :
3484 8403 : WriteNumericData(state, var->ReportID, var->TSValue);
3485 8403 : ++state.dataGlobal->StdOutputRecordCount;
3486 :
3487 8403 : if (rf->timeSeriesEnabled()) {
3488 0 : rf->freqTSData[(int)ReportFreq::TimeStep].pushVariableValue(var->ReportID, var->TSValue);
3489 : }
3490 : }
3491 851966 : var->TSValue = 0.0;
3492 851966 : var->thisTSStored = false;
3493 18800 : } // for (var)
3494 :
3495 18800 : UpdateMeters(state, MDHM);
3496 :
3497 18800 : ReportTSMeters(state, StartMinute, op->TimeValue[(int)TimeStepType::Zone].CurMinute, TimePrint, TimePrint);
3498 :
3499 : } // TimeStep Block
3500 :
3501 : // Hour Block
3502 18800 : if (state.dataGlobal->EndHourFlag) {
3503 3111 : if (op->freqTrackingVariables[(int)ReportFreq::Hour]) {
3504 1106 : int CurDayType = state.dataEnvrn->DayOfWeek;
3505 1106 : if (state.dataEnvrn->HolidayIndex > 0) {
3506 1104 : CurDayType = state.dataEnvrn->HolidayIndex;
3507 : }
3508 5530 : WriteTimeStampFormatData(state,
3509 1106 : state.files.eso,
3510 : ReportFreq::Hour,
3511 1106 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3512 1106 : state.dataGlobal->DayOfSimChr,
3513 : true,
3514 1106 : state.dataEnvrn->Month,
3515 1106 : state.dataEnvrn->DayOfMonth,
3516 1106 : state.dataGlobal->HourOfDay,
3517 : -1, // EndMinute
3518 : -1, // startMinute
3519 1106 : state.dataEnvrn->DSTIndicator,
3520 1106 : Sched::dayTypeNames[CurDayType]);
3521 1106 : TimePrint = false;
3522 : }
3523 :
3524 3111 : if (rf->timeSeriesEnabled()) {
3525 0 : if (!rf->freqTSData[(int)ReportFreq::Hour].variablesScanned()) {
3526 0 : rf->initializeTSDataFrame(ReportFreq::Hour, op->outVars);
3527 : }
3528 0 : rf->freqTSData[(int)ReportFreq::Hour].newRow(
3529 0 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3530 : }
3531 :
3532 3111 : op->TimeValue[(int)TimeStepType::Zone].CurMinute = 0.0;
3533 3111 : op->TimeValue[(int)TimeStepType::System].CurMinute = 0.0;
3534 :
3535 108929 : for (auto *var : op->outVars) {
3536 :
3537 105818 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3538 0 : continue;
3539 : }
3540 :
3541 : // ReportNow=.TRUE.
3542 : // IF (RVar%SchedPtr > 0) &
3543 : // ReportNow=(GetCurrentScheduleValue(state, RVar%SchedPtr) /= 0.0) !SetReportNow(RVar%SchedPtr)
3544 :
3545 : // IF (ReportNow) THEN
3546 105818 : if (var->tsStored) {
3547 58355 : if (var->storeType == StoreType::Average) {
3548 45923 : var->Value /= double(var->thisTSCount);
3549 : }
3550 58355 : if (var->Report && var->freq == ReportFreq::Hour && var->Stored) {
3551 22754 : WriteNumericData(state, var->ReportID, var->Value);
3552 22754 : ++state.dataGlobal->StdOutputRecordCount;
3553 22754 : var->Stored = false;
3554 : // add time series value for hourly to data store
3555 22754 : if (rf->timeSeriesEnabled()) {
3556 0 : rf->freqTSData[(int)ReportFreq::Hour].pushVariableValue(var->ReportID, var->Value);
3557 : }
3558 : }
3559 58355 : var->StoreValue += var->Value;
3560 58355 : ++var->NumStored;
3561 : }
3562 105818 : var->tsStored = false;
3563 105818 : var->thisTSStored = false;
3564 105818 : var->thisTSCount = 0;
3565 105818 : var->Value = 0.0;
3566 3111 : } // for (var)
3567 :
3568 3111 : ReportMeters(state, ReportFreq::Hour, TimePrint);
3569 : } // Hour Block
3570 :
3571 18800 : if (!state.dataGlobal->EndHourFlag) {
3572 15689 : return;
3573 : }
3574 :
3575 : // Day Block
3576 3111 : if (state.dataGlobal->EndDayFlag) {
3577 141 : if (op->freqTrackingVariables[(int)ReportFreq::Day]) {
3578 42 : int CurDayType = state.dataEnvrn->DayOfWeek;
3579 42 : if (state.dataEnvrn->HolidayIndex > 0) {
3580 40 : CurDayType = state.dataEnvrn->HolidayIndex;
3581 : }
3582 168 : WriteTimeStampFormatData(state,
3583 42 : state.files.eso,
3584 : ReportFreq::Day,
3585 42 : op->freqStampReportNums[(int)ReportFreq::Day],
3586 42 : state.dataGlobal->DayOfSimChr,
3587 : true,
3588 42 : state.dataEnvrn->Month,
3589 42 : state.dataEnvrn->DayOfMonth,
3590 : -1, // Hour
3591 : -1, // EndMinute
3592 : -1, // StartMinute
3593 42 : state.dataEnvrn->DSTIndicator,
3594 42 : Sched::dayTypeNames[CurDayType]);
3595 42 : TimePrint = false;
3596 : }
3597 141 : if (rf->timeSeriesEnabled()) {
3598 0 : if (!rf->freqTSData[(int)ReportFreq::Day].variablesScanned()) {
3599 0 : rf->initializeTSDataFrame(ReportFreq::Day, op->outVars);
3600 : }
3601 0 : rf->freqTSData[(int)ReportFreq::Day].newRow(
3602 0 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3603 : }
3604 :
3605 141 : op->NumHoursInMonth += 24;
3606 4262 : for (auto *var : op->outVars) {
3607 4121 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3608 0 : continue;
3609 : }
3610 4121 : var->writeOutput(state, ReportFreq::Day);
3611 141 : }
3612 :
3613 141 : ReportMeters(state, ReportFreq::Day, TimePrint);
3614 :
3615 : } // Day Block
3616 :
3617 : // Only continue if EndDayFlag is set
3618 3111 : if (!state.dataGlobal->EndDayFlag) {
3619 2970 : return;
3620 : }
3621 :
3622 : // Month Block
3623 141 : if (state.dataEnvrn->EndMonthFlag || state.dataGlobal->EndEnvrnFlag) {
3624 141 : if (op->freqTrackingVariables[(int)ReportFreq::Month]) {
3625 42 : WriteTimeStampFormatData(state,
3626 42 : state.files.eso,
3627 : ReportFreq::Month,
3628 42 : op->freqStampReportNums[(int)ReportFreq::Month],
3629 42 : state.dataGlobal->DayOfSimChr,
3630 : true,
3631 42 : state.dataEnvrn->Month);
3632 42 : TimePrint = false;
3633 : }
3634 :
3635 141 : if (rf->timeSeriesEnabled()) {
3636 0 : if (!rf->freqTSData[(int)ReportFreq::Month].variablesScanned()) {
3637 0 : rf->initializeTSDataFrame(ReportFreq::Month, op->outVars);
3638 : }
3639 0 : rf->freqTSData[(int)ReportFreq::Month].newRow(
3640 0 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3641 : }
3642 :
3643 141 : op->NumHoursInSim += op->NumHoursInMonth;
3644 141 : state.dataEnvrn->EndMonthFlag = false;
3645 4262 : for (auto *var : op->outVars) {
3646 4121 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3647 0 : continue;
3648 : }
3649 4121 : var->writeOutput(state, ReportFreq::Month);
3650 141 : }
3651 :
3652 141 : ReportMeters(state, ReportFreq::Month, TimePrint);
3653 :
3654 141 : op->NumHoursInMonth = 0;
3655 : } // Month Block
3656 :
3657 : // Sim/Environment Block
3658 141 : if (state.dataGlobal->EndEnvrnFlag) {
3659 141 : if (op->freqTrackingVariables[(int)ReportFreq::Simulation]) {
3660 2 : WriteTimeStampFormatData(state,
3661 2 : state.files.eso,
3662 : ReportFreq::Simulation,
3663 2 : op->freqStampReportNums[(int)ReportFreq::Simulation],
3664 2 : state.dataGlobal->DayOfSimChr,
3665 : true);
3666 2 : TimePrint = false;
3667 : }
3668 :
3669 141 : if (rf->timeSeriesEnabled()) {
3670 0 : if (!rf->freqTSData[(int)ReportFreq::Simulation].variablesScanned()) {
3671 0 : rf->initializeTSDataFrame(ReportFreq::Simulation, op->outVars);
3672 : }
3673 0 : rf->freqTSData[(int)ReportFreq::Simulation].newRow(
3674 0 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3675 : }
3676 :
3677 4262 : for (auto *var : op->outVars) {
3678 4121 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3679 0 : continue;
3680 : }
3681 4121 : var->writeOutput(state, ReportFreq::Simulation);
3682 141 : }
3683 :
3684 141 : ReportMeters(state, ReportFreq::Simulation, TimePrint);
3685 :
3686 141 : op->NumHoursInSim = 0;
3687 : }
3688 :
3689 : // Yearly Block
3690 141 : if (state.dataEnvrn->EndYearFlag) {
3691 0 : if (op->freqTrackingVariables[(int)ReportFreq::Year]) {
3692 0 : WriteYearlyTimeStamp(state, state.files.eso, op->freqStampReportNums[(int)ReportFreq::Year], state.dataGlobal->CalendarYearChr, true);
3693 0 : TimePrint = false;
3694 : }
3695 0 : if (rf->timeSeriesEnabled()) {
3696 0 : if (!rf->freqTSData[(int)ReportFreq::Year].variablesScanned()) {
3697 0 : rf->initializeTSDataFrame(ReportFreq::Year, op->outVars);
3698 : }
3699 0 : rf->freqTSData[(int)ReportFreq::Year].newRow(
3700 0 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3701 : }
3702 :
3703 0 : for (auto *var : op->outVars) {
3704 0 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3705 0 : continue;
3706 : }
3707 0 : var->writeOutput(state, ReportFreq::Year);
3708 0 : }
3709 :
3710 0 : ReportMeters(state, ReportFreq::Year, TimePrint);
3711 :
3712 0 : state.dataGlobal->CalendarYear += 1;
3713 0 : state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
3714 : }
3715 : } // UpdateDataandReport()
3716 :
3717 26 : void GenOutputVariablesAuditReport(EnergyPlusData &state)
3718 : {
3719 :
3720 : // SUBROUTINE INFORMATION:
3721 : // AUTHOR Linda Lawrie
3722 : // DATE WRITTEN February 2000
3723 :
3724 : // PURPOSE OF THIS SUBROUTINE:
3725 : // This subroutine reports (to the .err file) any report variables
3726 : // which were requested but not "setup" during the run. These will
3727 : // either be items that were not used in the IDF file or misspellings
3728 : // of report variable names.
3729 :
3730 : // METHODOLOGY EMPLOYED:
3731 : // Use flagged data structure in OutputProcessor.
3732 :
3733 : // Using/Aliasing
3734 : using namespace OutputProcessor;
3735 :
3736 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3737 26 : auto &op = state.dataOutputProcessor;
3738 :
3739 : static constexpr std::array<std::string_view, (int)ReportFreq::Num> localReportFreqNames = {
3740 : "Detailed", // EachCall // For some reason, this is "Detailed" here and "Each Call" in other places
3741 : "TimeStep", // TimeStep
3742 : "Hourly", // Hourly
3743 : "Daily", // Daily
3744 : "Monthly", // Monthly
3745 : "RunPeriod", // Simulation
3746 : "Annual" // Yearly
3747 : };
3748 :
3749 499 : for (auto *reqVar : op->reqVars) {
3750 473 : if (reqVar->Used) {
3751 471 : continue;
3752 : }
3753 2 : if (reqVar->key.empty()) {
3754 2 : reqVar->key = "*";
3755 : }
3756 2 : if (has(reqVar->name, "OPAQUE SURFACE INSIDE FACE CONDUCTION") && !state.dataGlobal->DisplayAdvancedReportVariables &&
3757 0 : !state.dataOutputProcessor->OpaqSurfWarned) {
3758 0 : ShowWarningMessage(state, R"(Variables containing "Opaque Surface Inside Face Conduction" are now "advanced" variables.)");
3759 0 : ShowContinueError(state, "You must enter the \"Output:Diagnostics,DisplayAdvancedReportVariables;\" statement to view.");
3760 0 : ShowContinueError(state, "First, though, read cautionary statements in the \"InputOutputReference\" document.");
3761 0 : state.dataOutputProcessor->OpaqSurfWarned = true;
3762 : }
3763 2 : if (!state.dataOutputProcessor->Rept) {
3764 2 : ShowWarningMessage(state, "The following Report Variables were requested but not generated -- check.rdd file");
3765 2 : ShowContinueError(state, "Either the IDF did not contain these elements, the variable name is misspelled,");
3766 2 : ShowContinueError(state,
3767 : "or the requested variable is an advanced output which requires Output : Diagnostics, DisplayAdvancedReportVariables;");
3768 1 : state.dataOutputProcessor->Rept = true;
3769 : }
3770 2 : ShowMessage(state, format("Key={}, VarName={}, Frequency={}", reqVar->key, reqVar->name, localReportFreqNames[(int)reqVar->freq]));
3771 26 : }
3772 26 : } // GenOutputVariablesAuditReport()
3773 :
3774 99 : void UpdateMeterReporting(EnergyPlusData &state)
3775 : {
3776 :
3777 : // SUBROUTINE INFORMATION:
3778 : // AUTHOR Linda Lawrie
3779 : // DATE WRITTEN January 2001
3780 : // MODIFIED February 2007 -- add cumulative meter reporting
3781 : // January 2012 -- add predefined tabular meter reporting
3782 : // RE-ENGINEERED na
3783 :
3784 : // PURPOSE OF THIS SUBROUTINE:
3785 : // This subroutine is called at the end of the first HVAC iteration and
3786 : // sets up the reporting for the Energy Meters. It also may show a fatal error
3787 : // if errors occurred during initial SetupOutputVariable processing. It "gets"
3788 : // the Report Meter input:
3789 : // Report Meter,
3790 : // \memo Meters requested here show up on eplusout.eso and eplusout.mtr
3791 : // A1 , \field Meter_Name
3792 : // \required-field
3793 : // \note Form is EnergyUseType:..., e.g. Electricity:* for all Electricity meters
3794 : // \note or EndUse:..., e.g. InteriorLights:* for all interior lights
3795 : // \note Report MeterFileOnly puts results on the eplusout.mtr file only
3796 : // A2 ; \field Reporting_Frequency
3797 : // \type choice
3798 : // \key timestep
3799 : // \note timestep refers to the zone timestep/timestep in hour value
3800 : // \note runperiod, environment, and annual are the same
3801 : // \key hourly
3802 : // \key daily
3803 : // \key monthly
3804 : // \key runperiod
3805 : // \key environment
3806 : // \key annual
3807 : // \note runperiod, environment, and annual are synonymous
3808 : // Report MeterFileOnly,
3809 : // \memo same reporting as Report Meter -- goes to eplusout.mtr only
3810 : // A1 , \field Meter_Name
3811 : // \required-field
3812 : // \note Form is EnergyUseType:..., e.g. Electricity:* for all Electricity meters
3813 : // \note or EndUse:..., e.g. InteriorLights:* for all interior lights
3814 : // \note Report MeterFileOnly puts results on the eplusout.mtr file only
3815 : // A2 ; \field Reporting_Frequency
3816 : // \type choice
3817 : // \key timestep
3818 : // \note timestep refers to the zone timestep/timestep in hour value
3819 : // \note runperiod, environment, and annual are the same
3820 : // \key hourly
3821 : // \key daily
3822 : // \key monthly
3823 : // \key runperiod
3824 : // \key environment
3825 : // \key annual
3826 : // \note runperiod, environment, and annual are synonymous
3827 :
3828 : // Using/Aliasing
3829 : using namespace OutputProcessor;
3830 :
3831 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3832 : int Loop;
3833 99 : Array1D_string Alphas(2);
3834 99 : Array1D<Real64> Numbers(1);
3835 : int NumAlpha;
3836 : int NumNumbers;
3837 : int IOStat;
3838 : int NumReqMeters;
3839 : int NumReqMeterFOs;
3840 :
3841 99 : bool ErrorsFound(false); // If errors detected in input
3842 99 : auto const &op = state.dataOutputProcessor;
3843 99 : auto &ipsc = state.dataIPShortCut;
3844 :
3845 99 : GetCustomMeterInput(state, ErrorsFound);
3846 99 : if (ErrorsFound) {
3847 0 : op->ErrorsLogged = true;
3848 : }
3849 :
3850 : // Helper lambda to locate a meter index from its name. Returns a negative value if not found
3851 : auto setupMeterFromMeterName = // (AUTO_OK_LAMBDA)
3852 97 : [&state](std::string &name, ReportFreq freq, bool MeterFileOnlyIndicator, bool CumulativeIndicator) -> bool {
3853 97 : bool result = false;
3854 :
3855 97 : size_t varnameLen = index(name, '[');
3856 97 : if (varnameLen != std::string::npos) {
3857 0 : name.erase(varnameLen);
3858 : }
3859 :
3860 97 : auto const &op = state.dataOutputProcessor;
3861 :
3862 97 : std::string::size_type wildCardPosition = index(name, '*');
3863 :
3864 97 : if (wildCardPosition == std::string::npos) {
3865 96 : if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
3866 96 : SetInitialMeterReportingAndOutputNames(state, found->second, MeterFileOnlyIndicator, freq, CumulativeIndicator);
3867 96 : result = true;
3868 96 : }
3869 : } else { // Wildcard input
3870 1 : std::string nameSubstr = name.substr(0, wildCardPosition);
3871 20 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
3872 19 : if (Util::SameString(op->meters[iMeter]->Name.substr(0, wildCardPosition), nameSubstr)) {
3873 5 : SetInitialMeterReportingAndOutputNames(state, iMeter, MeterFileOnlyIndicator, freq, CumulativeIndicator);
3874 5 : result = true;
3875 : }
3876 : }
3877 1 : }
3878 :
3879 97 : return result;
3880 99 : };
3881 :
3882 99 : ipsc->cCurrentModuleObject = "Output:Meter";
3883 99 : NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3884 :
3885 122 : for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
3886 :
3887 69 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3888 23 : ipsc->cCurrentModuleObject,
3889 : Loop,
3890 : Alphas,
3891 : NumAlpha,
3892 : Numbers,
3893 : NumNumbers,
3894 : IOStat,
3895 23 : ipsc->lNumericFieldBlanks,
3896 23 : ipsc->lAlphaFieldBlanks,
3897 23 : ipsc->cAlphaFieldNames,
3898 23 : ipsc->cNumericFieldNames);
3899 :
3900 23 : bool meterFileOnlyIndicator = false;
3901 23 : bool cumulativeIndicator = false;
3902 23 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3903 :
3904 23 : if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
3905 0 : ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
3906 : }
3907 : }
3908 :
3909 99 : ipsc->cCurrentModuleObject = "Output:Meter:MeterFileOnly";
3910 99 : NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3911 173 : for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
3912 :
3913 222 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3914 74 : ipsc->cCurrentModuleObject,
3915 : Loop,
3916 : Alphas,
3917 : NumAlpha,
3918 : Numbers,
3919 : NumNumbers,
3920 : IOStat,
3921 74 : ipsc->lNumericFieldBlanks,
3922 74 : ipsc->lAlphaFieldBlanks,
3923 74 : ipsc->cAlphaFieldNames,
3924 74 : ipsc->cNumericFieldNames);
3925 :
3926 74 : bool meterFileOnlyIndicator = true;
3927 74 : bool cumulativeIndicator = false;
3928 74 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3929 74 : if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
3930 0 : ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
3931 : }
3932 : }
3933 :
3934 99 : ipsc->cCurrentModuleObject = "Output:Meter:Cumulative";
3935 99 : NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3936 :
3937 99 : for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
3938 :
3939 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3940 0 : ipsc->cCurrentModuleObject,
3941 : Loop,
3942 : Alphas,
3943 : NumAlpha,
3944 : Numbers,
3945 : NumNumbers,
3946 : IOStat,
3947 0 : ipsc->lNumericFieldBlanks,
3948 0 : ipsc->lAlphaFieldBlanks,
3949 0 : ipsc->cAlphaFieldNames,
3950 0 : ipsc->cNumericFieldNames);
3951 :
3952 0 : bool meterFileOnlyIndicator = false;
3953 0 : bool cumulativeIndicator = true;
3954 0 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3955 0 : if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
3956 0 : ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
3957 : }
3958 : }
3959 :
3960 99 : ipsc->cCurrentModuleObject = "Output:Meter:Cumulative:MeterFileOnly";
3961 99 : NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3962 99 : for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
3963 :
3964 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3965 0 : ipsc->cCurrentModuleObject,
3966 : Loop,
3967 : Alphas,
3968 : NumAlpha,
3969 : Numbers,
3970 : NumNumbers,
3971 : IOStat,
3972 0 : ipsc->lNumericFieldBlanks,
3973 0 : ipsc->lAlphaFieldBlanks,
3974 0 : ipsc->cAlphaFieldNames,
3975 0 : ipsc->cNumericFieldNames);
3976 :
3977 0 : bool meterFileOnlyIndicator = true;
3978 0 : bool cumulativeIndicator = true;
3979 0 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3980 0 : if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
3981 0 : ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
3982 : }
3983 : }
3984 :
3985 99 : ReportMeterDetails(state);
3986 :
3987 99 : if (op->ErrorsLogged) {
3988 0 : ShowFatalError(state, "UpdateMeterReporting: Previous Meter Specification errors cause program termination.");
3989 : }
3990 :
3991 99 : op->meterValues.resize(op->meters.size(), 0.0);
3992 99 : std::fill(op->meterValues.begin(), op->meterValues.end(), 0.0);
3993 99 : } // UpdateMeterReporting()
3994 :
3995 105 : void SetInitialMeterReportingAndOutputNames(EnergyPlusData &state,
3996 : int const WhichMeter, // Which meter number
3997 : bool const MeterFileOnlyIndicator, // true if this is a meter file only reporting
3998 : OutputProcessor::ReportFreq freq, // at what frequency is the meter reported
3999 : bool const CumulativeIndicator // true if this is a Cumulative meter reporting
4000 : )
4001 : {
4002 :
4003 : // SUBROUTINE INFORMATION:
4004 : // AUTHOR Linda Lawrie
4005 : // DATE WRITTEN February 2007
4006 :
4007 : // PURPOSE OF THIS SUBROUTINE:
4008 : // Set values and output initial names to output files.
4009 :
4010 : // Using/Aliasing
4011 : using namespace OutputProcessor;
4012 :
4013 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4014 105 : auto &op = state.dataOutputProcessor;
4015 :
4016 105 : auto *meter = op->meters[WhichMeter];
4017 105 : auto &period = meter->periods[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq];
4018 105 : if (!CumulativeIndicator) {
4019 103 : if (MeterFileOnlyIndicator && period.Rpt) {
4020 4 : ShowWarningError(state,
4021 4 : format(R"(Output:Meter:MeterFileOnly requested for "{}" ({}), already on "Output:Meter". Will report to both {} and {})",
4022 2 : meter->Name,
4023 2 : reportFreqNames[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq],
4024 4 : state.files.eso.filePath.filename(),
4025 4 : state.files.mtr.filePath.filename()));
4026 : }
4027 103 : if (!period.Rpt) {
4028 97 : period.Rpt = true;
4029 97 : if (MeterFileOnlyIndicator) {
4030 78 : period.RptFO = true;
4031 : } else {
4032 19 : op->freqTrackingVariables[(int)freq] = true;
4033 : }
4034 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4035 97 : meter->indexGroup = DetermineIndexGroupFromMeterGroup(meter);
4036 97 : WriteMeterDictionaryItem(
4037 97 : state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, MeterFileOnlyIndicator);
4038 : }
4039 : } else { // !CumulativeIndicator
4040 2 : if (MeterFileOnlyIndicator && period.accRpt) {
4041 4 : ShowWarningError(state,
4042 4 : format("Output:Meter:MeterFileOnly requested for \"Cumulative {}\" (TimeStep), already on \"Output:Meter\". "
4043 : "Will report to both {} and {}",
4044 2 : meter->Name,
4045 4 : state.files.eso.filePath.filename(),
4046 4 : state.files.mtr.filePath.filename()));
4047 : }
4048 :
4049 2 : if (!period.accRpt) {
4050 0 : period.accRpt = true;
4051 0 : if (MeterFileOnlyIndicator) {
4052 0 : period.accRptFO = true;
4053 : }
4054 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4055 0 : meter->indexGroup = DetermineIndexGroupFromMeterGroup(op->meters[WhichMeter]);
4056 0 : WriteMeterDictionaryItem(
4057 0 : state, freq, StoreType::Sum, period.accRptNum, meter->indexGroup, meter->Name, meter->units, true, MeterFileOnlyIndicator);
4058 : }
4059 : } // if (CumulativeIndicator)
4060 105 : } // SetInitialMeterReportingAndOutputNames()
4061 :
4062 16645 : int GetMeterIndex(EnergyPlusData const &state, std::string const &name)
4063 : {
4064 :
4065 : // FUNCTION INFORMATION:
4066 : // AUTHOR Linda K. Lawrie
4067 : // DATE WRITTEN August 2002
4068 :
4069 : // PURPOSE OF THIS FUNCTION:
4070 : // This function returns a index to the meter "number" (aka assigned report number)
4071 : // for the meter name. If none active for this run, a zero is returned. This is used later to
4072 : // obtain a meter "value".
4073 :
4074 16645 : auto const &op = state.dataOutputProcessor;
4075 :
4076 16645 : auto found = op->meterMap.find(name);
4077 33290 : return (found != op->meterMap.end()) ? found->second : -1;
4078 16645 : } // GetMeterIndex()
4079 :
4080 0 : Constant::eResource GetMeterResourceType(EnergyPlusData const &state, int const MeterNumber) // Which Meter Number (from GetMeterIndex)
4081 : {
4082 :
4083 : // FUNCTION INFORMATION:
4084 : // AUTHOR Linda K. Lawrie
4085 : // DATE WRITTEN August 2002
4086 :
4087 : // Using/Aliasing
4088 : using namespace OutputProcessor;
4089 :
4090 0 : return (MeterNumber != -1) ? state.dataOutputProcessor->meters[MeterNumber]->resource : Constant::eResource::Invalid;
4091 : } // GetMeterResourceType()
4092 :
4093 675 : Real64 GetCurrentMeterValue(EnergyPlusData const &state, int const MeterNumber) // Which Meter Number (from GetMeterIndex)
4094 : {
4095 :
4096 : // FUNCTION INFORMATION:
4097 : // AUTHOR Linda K. Lawrie
4098 : // DATE WRITTEN August 2002
4099 :
4100 : // PURPOSE OF THIS FUNCTION:
4101 : // This function returns the current meter value (timestep) for the meter number indicated.
4102 :
4103 675 : return (MeterNumber != -1) ? state.dataOutputProcessor->meters[MeterNumber]->CurTSValue : 0.0;
4104 : } // GetCurrentMeterValue()
4105 :
4106 19820052 : Real64 GetInstantMeterValue(EnergyPlusData &state,
4107 : int const meterNum, // Which Meter Number (from GetMeterIndex)
4108 : OutputProcessor::TimeStepType const timeStepType // Whether this is zone of HVAC
4109 : )
4110 : {
4111 :
4112 : // FUNCTION INFORMATION:
4113 : // AUTHOR Richard Liesen
4114 : // DATE WRITTEN February 2003
4115 :
4116 : // PURPOSE OF THIS FUNCTION:
4117 : // This function returns the Instantaneous meter value (timestep) for the meter number indicated
4118 : // using TimeStepType to differentiate between Zone and HVAC values.
4119 :
4120 : // Using/Aliasing
4121 : using namespace OutputProcessor;
4122 :
4123 19820052 : Real64 InstantMeterValue = 0.0;
4124 :
4125 19820052 : if (meterNum == -1) {
4126 15940530 : return InstantMeterValue;
4127 : }
4128 :
4129 3879522 : auto const &op = state.dataOutputProcessor;
4130 3879522 : auto *meter = op->meters[meterNum];
4131 :
4132 3879522 : if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
4133 29343776 : for (int srcVarNum : meter->srcVarNums) {
4134 25464254 : auto *var = op->outVars[srcVarNum];
4135 : // Separate the Zone variables from the HVAC variables using TimeStepType
4136 25464254 : if (var->timeStepType != timeStepType) {
4137 12728065 : continue;
4138 : }
4139 :
4140 12736189 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4141 12736189 : assert(rVar != nullptr);
4142 : // Add to the total all of the appropriate variables
4143 12736189 : InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4144 3879522 : }
4145 :
4146 3879522 : } else if (meter->type == MeterType::CustomDec) {
4147 0 : auto *decMeter = op->meters[meter->decMeterNum];
4148 0 : for (int srcVarNum : decMeter->srcVarNums) {
4149 0 : auto *var = op->outVars[srcVarNum];
4150 0 : if (var->timeStepType != timeStepType) {
4151 0 : continue;
4152 : }
4153 0 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4154 0 : assert(rVar != nullptr);
4155 0 : InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4156 0 : }
4157 0 : for (int srcVarNum : meter->srcVarNums) {
4158 0 : auto *var = op->outVars[srcVarNum];
4159 0 : if (var->timeStepType != timeStepType) {
4160 0 : continue;
4161 : }
4162 0 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4163 0 : assert(rVar != nullptr);
4164 0 : InstantMeterValue -= (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4165 0 : }
4166 : } else {
4167 0 : assert(false);
4168 : }
4169 :
4170 3879522 : return InstantMeterValue;
4171 : } // GetInstantMeterValue()
4172 :
4173 273303 : Real64 GetInternalVariableValue(EnergyPlusData &state,
4174 : OutputProcessor::VariableType const varType, // 1=integer, 2=real, 3=meter
4175 : int const keyVarIndex // Array index
4176 : )
4177 : {
4178 : // FUNCTION INFORMATION:
4179 : // AUTHOR Linda K. Lawrie
4180 : // DATE WRITTEN December 2000
4181 : // MODIFIED August 2003, M. J. Witte
4182 :
4183 : // PURPOSE OF THIS FUNCTION:
4184 : // This function returns the current value of the Internal Variable assigned to
4185 : // the varType and keyVarIndex. Values may be accessed for REAL(r64) and integer
4186 : // report variables and meter variables. The variable type (varType) may be
4187 : // determined by calling subroutine and GetVariableKeyCountandType. The
4188 : // index (keyVarIndex) may be determined by calling subroutine GetVariableKeys.
4189 :
4190 : // METHODOLOGY EMPLOYED:
4191 : // Uses Internal OutputProcessor data structure to return value.
4192 :
4193 : // Using/Aliasing
4194 : using namespace OutputProcessor;
4195 :
4196 : // Return value
4197 : Real64 resultVal; // value returned
4198 :
4199 273303 : auto const &op = state.dataOutputProcessor;
4200 :
4201 : // Select based on variable type: integer, real, or meter
4202 273303 : if (varType == VariableType::Invalid) { // Variable not a found variable
4203 48 : resultVal = 0.0;
4204 273255 : } else if (varType == VariableType::Integer || varType == VariableType::Real) {
4205 273250 : if (keyVarIndex < 0 || keyVarIndex >= (int)op->outVars.size()) {
4206 0 : ShowFatalError(state, "GetInternalVariableValue: passed variable index beyond range of array.");
4207 0 : ShowContinueError(state, format("Index = {} Number of variables = {}", keyVarIndex, op->outVars.size()));
4208 : }
4209 :
4210 : // must use %Which, %Value is always zero if variable is not a requested report variable
4211 546500 : resultVal = (varType == VariableType::Integer) ? (double)*(dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]))->Which
4212 273250 : : (double)*(dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]))->Which;
4213 5 : } else if (varType == VariableType::Meter) {
4214 5 : resultVal = GetCurrentMeterValue(state, keyVarIndex);
4215 0 : } else if (varType == VariableType::Schedule) {
4216 0 : resultVal = state.dataSched->schedules[keyVarIndex]->getCurrentVal();
4217 : } else {
4218 0 : resultVal = 0.0;
4219 : }
4220 :
4221 273303 : return resultVal;
4222 : } // GetInternalVariableValue()
4223 :
4224 0 : Real64 GetInternalVariableValueExternalInterface(EnergyPlusData &state,
4225 : OutputProcessor::VariableType const varType, // 1=integer, 2=REAL(r64), 3=meter
4226 : int const keyVarIndex // Array index
4227 : )
4228 : {
4229 : // FUNCTION INFORMATION:
4230 : // AUTHOR Thierry S. Nouidui
4231 : // DATE WRITTEN August 2011
4232 :
4233 : // PURPOSE OF THIS FUNCTION:
4234 : // This function returns the last zone-timestep value of the Internal Variable assigned to
4235 : // the varType and keyVarIndex. Values may be accessed for REAL(r64) and integer
4236 : // report variables and meter variables. The variable type (varType) may be
4237 : // determined by calling subroutine and GetVariableKeyCountandType. The
4238 : // index (keyVarIndex) may be determined by calling subroutine GetVariableKeys.
4239 :
4240 : // METHODOLOGY EMPLOYED:
4241 : // Uses Internal OutputProcessor data structure to return value.
4242 :
4243 : // Using/Aliasing
4244 : using namespace OutputProcessor;
4245 :
4246 : // Return value
4247 : Real64 resultVal; // value returned
4248 :
4249 0 : auto const &op = state.dataOutputProcessor;
4250 :
4251 : // Select based on variable type: integer, REAL(r64), or meter
4252 0 : if (varType == VariableType::Invalid) { // Variable not a found variable
4253 0 : resultVal = 0.0;
4254 0 : } else if (varType == VariableType::Integer || varType == VariableType::Real) {
4255 0 : if (keyVarIndex < 0 || keyVarIndex >= (int)op->outVars.size()) {
4256 0 : ShowFatalError(state, "GetInternalVariableValueExternalInterface: passed index beyond range of array.");
4257 : }
4258 :
4259 0 : resultVal = (double)op->outVars[keyVarIndex]->EITSValue;
4260 0 : } else if (varType == VariableType::Meter) {
4261 0 : resultVal = GetCurrentMeterValue(state, keyVarIndex);
4262 0 : } else if (varType == VariableType::Schedule) {
4263 0 : resultVal = state.dataSched->schedules[keyVarIndex]->getCurrentVal();
4264 : } else {
4265 0 : resultVal = 0.0;
4266 : }
4267 :
4268 0 : return resultVal;
4269 : } // GetInternalVariableValueExternalInterface()
4270 :
4271 415 : int GetNumMeteredVariables(EnergyPlusData const &state,
4272 : [[maybe_unused]] std::string const &ComponentType, // Given Component Type
4273 : std::string const &ComponentName // Given Component Name (user defined)
4274 : )
4275 : {
4276 :
4277 : // FUNCTION INFORMATION:
4278 : // AUTHOR Linda Lawrie
4279 : // DATE WRITTEN May 2005
4280 :
4281 : // PURPOSE OF THIS FUNCTION:
4282 : // This function counts the number of metered variables associated with the
4283 : // given ComponentType/Name. This resultant number would then be used to
4284 : // allocate arrays for a call the GetMeteredVariables routine.
4285 :
4286 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
4287 415 : int NumVariables = 0;
4288 415 : auto const &op = state.dataOutputProcessor;
4289 :
4290 14420 : for (auto *var : op->outVars) {
4291 : // Pos=INDEX(RVariableTypes(Loop)%VarName,':')
4292 : // IF (ComponentName /= RVariableTypes(Loop)%VarNameUC(1:Pos-1)) CYCLE
4293 14005 : if (var->varType != OutputProcessor::VariableType::Real) {
4294 10 : continue;
4295 : }
4296 13995 : if (ComponentName != var->keyUC) {
4297 13053 : continue;
4298 : }
4299 942 : if (!var->meterNums.empty()) {
4300 474 : ++NumVariables;
4301 : }
4302 415 : }
4303 415 : return NumVariables;
4304 : } // GetNumMeteredVariables()
4305 :
4306 198 : int GetMeteredVariables(EnergyPlusData &state,
4307 : std::string const &ComponentName, // Given Component Name (user defined)
4308 : Array1D<OutputProcessor::MeteredVar> &meteredVars // Variable Types (1=integer, 2=real, 3=meter)
4309 : )
4310 : {
4311 :
4312 : // SUBROUTINE INFORMATION:
4313 : // AUTHOR Linda Lawrie
4314 : // DATE WRITTEN May 2005
4315 : // MODIFIED Jason DeGraw 2/12/2020, de-optionalized
4316 : // RE-ENGINEERED na
4317 :
4318 : // PURPOSE OF THIS SUBROUTINE:
4319 : // This routine gets the variable names and other associated information
4320 : // for metered variables associated with the given ComponentType/Name.
4321 :
4322 : // Using/Aliasing
4323 : using namespace OutputProcessor;
4324 :
4325 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4326 : int NumVariables;
4327 198 : auto &op = state.dataOutputProcessor;
4328 :
4329 198 : NumVariables = 0;
4330 :
4331 7144 : for (int iVar = 0; iVar < (int)op->outVars.size(); ++iVar) {
4332 : // Pos=INDEX(RVariableTypes(Loop)%VarName,':')
4333 : // IF (ComponentName /= RVariableTypes(Loop)%VarNameUC(1:Pos-1)) CYCLE
4334 :
4335 6946 : auto *var = op->outVars[iVar];
4336 6946 : if (var->varType != VariableType::Real) {
4337 9 : continue;
4338 : }
4339 6937 : if (ComponentName != var->keyUC) {
4340 5996 : continue;
4341 : }
4342 941 : if (var->meterNums.empty()) {
4343 468 : continue;
4344 : }
4345 :
4346 473 : auto &meteredVar = meteredVars(++NumVariables);
4347 :
4348 473 : meteredVar.num = iVar;
4349 473 : meteredVar.varType = VariableType::Real;
4350 473 : meteredVar.timeStepType = var->timeStepType;
4351 473 : meteredVar.units = var->units;
4352 :
4353 473 : meteredVar.resource = op->meters[var->meterNums[0]]->resource;
4354 473 : meteredVar.name = var->keyColonNameUC;
4355 :
4356 473 : bool foundEndUse = false;
4357 473 : bool foundGroup = false;
4358 1419 : for (int meterNum : var->meterNums) {
4359 1417 : auto *meter = op->meters[meterNum];
4360 1417 : if (!foundEndUse && meter->endUseCat != EndUseCat::Invalid) {
4361 471 : meteredVar.endUseCat = meter->endUseCat;
4362 471 : foundEndUse = true;
4363 : }
4364 :
4365 1417 : if (!foundGroup && meter->group != Group::Invalid) {
4366 473 : meteredVar.group = meter->group;
4367 473 : foundGroup = true;
4368 : }
4369 :
4370 1417 : if (foundEndUse && foundGroup) {
4371 471 : break;
4372 : }
4373 473 : }
4374 :
4375 473 : meteredVar.rptNum = var->ReportID;
4376 : }
4377 198 : return NumVariables;
4378 : } // GetMeteredVariables()
4379 :
4380 2325 : void GetVariableKeyCountandType(EnergyPlusData &state,
4381 : std::string const &name, // Standard variable name
4382 : int &numKeys, // Number of keys found
4383 : OutputProcessor::VariableType &varType,
4384 : OutputProcessor::StoreType &storeType, // Variable is Averaged=1 or Summed=2
4385 : OutputProcessor::TimeStepType &timeStepType, // Variable time step is Zone=1 or HVAC=2
4386 : Constant::Units &units // Units enumeration
4387 : )
4388 : {
4389 :
4390 : // SUBROUTINE INFORMATION:
4391 : // AUTHOR Michael J. Witte
4392 : // DATE WRITTEN August 2003
4393 :
4394 : // PURPOSE OF THIS SUBROUTINE:
4395 : // This subroutine returns the variable TYPE (Real, integer, meter, schedule, etc.)
4396 : // (varType) whether it is an averaged or summed variable (varAvgSum),
4397 : // whether it is a zone or HVAC time step (varStepType),
4398 : // and the number of keynames for a given report variable or report meter name
4399 : // (varName). The variable type (varType) and number of keys (numKeys) are
4400 : // used when calling subroutine GetVariableKeys to obtain a list of the
4401 : // keynames for a particular variable and a corresponding list of indexes.
4402 :
4403 : // METHODOLOGY EMPLOYED:
4404 : // Uses Internal OutputProcessor data structure to search for varName
4405 : // in each of the three output data arrays:
4406 : // RVariableTypes - real report variables
4407 : // IVariableTypes - integer report variables
4408 : // EnergyMeters - report meters (via GetMeterIndex function)
4409 : // Schedules - specific schedule values
4410 : // When the variable is found, the variable type (varType) is set and the
4411 : // number of associated keys is counted.
4412 :
4413 : using namespace OutputProcessor;
4414 :
4415 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4416 2325 : auto const &op = state.dataOutputProcessor;
4417 :
4418 2325 : varType = VariableType::Invalid;
4419 2325 : numKeys = 0;
4420 2325 : storeType = StoreType::Average;
4421 2325 : timeStepType = TimeStepType::Zone;
4422 2325 : units = Constant::Units::None; // Why is this None and not Invalid?
4423 :
4424 2325 : std::string nameUC = Util::makeUPPER(name);
4425 :
4426 : // Search Variable List First
4427 2325 : if (auto found = op->ddOutVarMap.find(nameUC); found != op->ddOutVarMap.end()) {
4428 38 : auto const *ddOutVar = op->ddOutVars[found->second];
4429 38 : varType = ddOutVar->variableType;
4430 38 : storeType = ddOutVar->storeType;
4431 38 : timeStepType = ddOutVar->timeStepType;
4432 38 : units = ddOutVar->units;
4433 38 : numKeys = ddOutVar->keyOutVarNums.size();
4434 :
4435 2287 : } else if (auto found2 = op->meterMap.find(nameUC); found2 != op->meterMap.end()) {
4436 : // Search Meters if not found in integers or reals
4437 : // Use the GetMeterIndex function
4438 : // Meters do not have keys, so only one will be found
4439 68 : int meterNum = found2->second;
4440 68 : numKeys = 1;
4441 68 : varType = VariableType::Meter;
4442 68 : units = op->meters[meterNum]->units;
4443 68 : storeType = StoreType::Sum;
4444 68 : timeStepType = TimeStepType::Zone;
4445 :
4446 : } else {
4447 : // Search schedules if not found in integers, reals, or meters
4448 : // Schedules do not have keys, so only one will be found
4449 2219 : auto *sched = Sched::GetSchedule(state, nameUC);
4450 2219 : if (sched != nullptr) {
4451 2 : numKeys = 1;
4452 2 : varType = VariableType::Schedule;
4453 2 : if (sched->schedTypeNum != Sched::SchedNum_Invalid) {
4454 1 : std::string const &schedTypeName = state.dataSched->scheduleTypes[sched->schedTypeNum]->Name;
4455 1 : units = static_cast<Constant::Units>(getEnumValue(Constant::unitNamesUC, schedTypeName));
4456 : }
4457 2 : storeType = StoreType::Average;
4458 2 : timeStepType = TimeStepType::Zone;
4459 : }
4460 4612 : }
4461 2325 : } // GetVariableKeyCountandType()
4462 :
4463 116 : void GetVariableKeys(EnergyPlusData &state,
4464 : std::string const &varName, // Standard variable name
4465 : OutputProcessor::VariableType const varType, // 1=integer, 2=real, 3=meter
4466 : Array1D_string &keyNames,
4467 : Array1D_int &keyOutVarNums // Array index for
4468 :
4469 : )
4470 : {
4471 :
4472 : // SUBROUTINE INFORMATION:
4473 : // AUTHOR Michael J. Witte
4474 : // DATE WRITTEN August 2003
4475 :
4476 : // PURPOSE OF THIS SUBROUTINE:
4477 : // This subroutine returns a list of keynames and indexes associated
4478 : // with a particular report variable or report meter name (varName).
4479 : // This routine assumes that the variable TYPE (Real, integer, meter, etc.)
4480 : // may be determined by calling GetVariableKeyCountandType. The variable type
4481 : // and index can then be used with function GetInternalVariableValue to
4482 : // to retrieve the current value of a particular variable/keyname combination.
4483 :
4484 : // METHODOLOGY EMPLOYED:
4485 : // Uses Internal OutputProcessor data structure to search for varName
4486 : // and build list of keynames and indexes. The indexes are the array index
4487 : // in the data array for the
4488 :
4489 : // Using/Aliasing
4490 : using namespace OutputProcessor;
4491 :
4492 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4493 116 : std::string nameUC = Util::makeUPPER(varName);
4494 :
4495 : // Select based on variable type: integer, real, or meter
4496 116 : if (varType == VariableType::Integer || varType == VariableType::Real) {
4497 38 : auto const &op = state.dataOutputProcessor;
4498 38 : auto found = op->ddOutVarMap.find(nameUC);
4499 38 : if (found == op->ddOutVarMap.end()) {
4500 0 : return;
4501 : }
4502 :
4503 38 : auto const *ddOutVar = op->ddOutVars[found->second];
4504 :
4505 38 : if (ddOutVar->keyOutVarNums.size() > size(keyOutVarNums)) {
4506 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4507 : }
4508 :
4509 38 : int iKey = 0;
4510 108 : for (int keyOutVarNum : ddOutVar->keyOutVarNums) {
4511 70 : ++iKey;
4512 70 : keyOutVarNums(iKey) = keyOutVarNum;
4513 70 : keyNames(iKey) = op->outVars[keyOutVarNum]->keyUC;
4514 38 : }
4515 :
4516 154 : } else if (varType == VariableType::Meter) { // Meter
4517 :
4518 68 : if (size(keyOutVarNums) == 0) {
4519 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4520 : }
4521 68 : keyOutVarNums(1) = GetMeterIndex(state, varName);
4522 68 : keyNames(1) = "Meter";
4523 :
4524 10 : } else if (varType == VariableType::Schedule) { // Schedule
4525 :
4526 2 : if (size(keyOutVarNums) == 0) {
4527 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4528 : }
4529 2 : keyOutVarNums(1) = Sched::GetScheduleNum(state, varName);
4530 2 : keyNames(1) = "Environment";
4531 : } else {
4532 : // do nothing
4533 : }
4534 116 : } // GetVariableKeys()
4535 :
4536 977 : bool ReportingThisVariable(EnergyPlusData &state, std::string const &RepVarName)
4537 : {
4538 :
4539 : // FUNCTION INFORMATION:
4540 : // AUTHOR Linda Lawrie
4541 : // DATE WRITTEN October 2008
4542 :
4543 : // PURPOSE OF THIS FUNCTION:
4544 : // This function scans the report variables and reports back
4545 : // if user has requested this variable be reported.
4546 : using namespace OutputProcessor;
4547 :
4548 977 : auto const &op = state.dataOutputProcessor;
4549 :
4550 977 : std::string name = Util::makeUPPER(RepVarName);
4551 :
4552 6028 : for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
4553 5051 : if (op->reqVars[iReqVar]->name == name) {
4554 0 : return true;
4555 : }
4556 : }
4557 :
4558 977 : if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
4559 146 : auto const *meter = op->meters[found->second];
4560 1022 : for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
4561 876 : if (iFreq == (int)ReportFreq::Year) {
4562 146 : continue;
4563 : }
4564 730 : auto const &period = meter->periods[iFreq];
4565 730 : if (period.Rpt || period.RptFO || period.accRpt || period.accRptFO) {
4566 0 : return true;
4567 : }
4568 : }
4569 977 : }
4570 :
4571 977 : return false;
4572 977 : } // ReportingThisVariable()
4573 :
4574 0 : void InitPollutionMeterReporting(EnergyPlusData &state, OutputProcessor::ReportFreq freq)
4575 : {
4576 :
4577 : // SUBROUTINE INFORMATION:Richard Liesen
4578 : // DATE WRITTEN July 2002
4579 :
4580 : // PURPOSE OF THIS SUBROUTINE:
4581 : // This subroutine is called at the end of the first HVAC iteration and
4582 : // sets up the reporting for the Pollution Meters.
4583 : // ReportPollutionOutput,
4584 : // A1 ; \field Reporting_Frequency
4585 : // \type choice
4586 : // \key timestep
4587 : // \key hourly
4588 : // \key daily
4589 : // \key monthly
4590 : // \key runperiod
4591 : // METHODOLOGY EMPLOYED:
4592 : // The program tries to setup all of the following meters if the Pollution Report is initiated.
4593 : // Electricity:Facility [J]
4594 : // Diesel:Facility [J]
4595 : // DistrictCooling:Facility [J]
4596 : // DistrictHeating:Facility [J]
4597 : // Gas:Facility [J]
4598 : // GASOLINE:Facility [J]
4599 : // COAL:Facility [J]
4600 : // FuelOilNo1:Facility [J]
4601 : // FuelOilNo2:Facility [J]
4602 : // Propane:Facility [J]
4603 : // ElectricityProduced:Facility [J]
4604 : // Pollutant:CO2
4605 : // Pollutant:CO
4606 : // Pollutant:CH4
4607 : // Pollutant:NOx
4608 : // Pollutant:N2O
4609 : // Pollutant:SO2
4610 : // Pollutant:PM
4611 : // Pollutant:PM10
4612 : // Pollutant:PM2.5
4613 : // Pollutant:NH3
4614 : // Pollutant:NMVOC
4615 : // Pollutant:Hg
4616 : // Pollutant:Pb
4617 : // Pollutant:WaterEnvironmentalFactors
4618 : // Pollutant:Nuclear High
4619 : // Pollutant:Nuclear Low
4620 : // Pollutant:Carbon Equivalent
4621 :
4622 : // Using/Aliasing
4623 : using namespace OutputProcessor;
4624 :
4625 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4626 :
4627 0 : auto &op = state.dataOutputProcessor;
4628 :
4629 0 : for (int iResource = 0; iResource < (int)Constant::eResource::Num; ++iResource) {
4630 0 : std::string meterName = format("{}:Facility", Constant::eResourceNames[iResource]);
4631 0 : std::string meterNameUC = Util::makeUPPER(meterName);
4632 :
4633 0 : auto found = op->meterMap.find(meterNameUC);
4634 0 : if (found == op->meterMap.end()) {
4635 0 : continue;
4636 : }
4637 :
4638 0 : auto *meter = op->meters[found->second];
4639 0 : auto &period = meter->periods[(int)freq];
4640 :
4641 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4642 0 : meter->indexGroup = DetermineIndexGroupFromMeterGroup(meter);
4643 :
4644 : // All of the specified meters are checked and the headers printed to the meter file if this
4645 : // has not been done previously
4646 0 : if (!period.Rpt) {
4647 0 : period.Rpt = true;
4648 0 : WriteMeterDictionaryItem(state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, false);
4649 0 : op->freqTrackingVariables[(int)freq] = true;
4650 : }
4651 0 : }
4652 0 : } // InitPollutionMeterReporting()
4653 :
4654 73 : void ProduceRDDMDD(EnergyPlusData &state)
4655 : {
4656 :
4657 : // SUBROUTINE INFORMATION:
4658 : // AUTHOR Linda Lawrie
4659 : // DATE WRITTEN March 2009
4660 :
4661 : // PURPOSE OF THIS SUBROUTINE:
4662 : // provide a single call for writing out the Report Data Dictionary and Meter Data Dictionary.
4663 :
4664 : // Using/Aliasing
4665 : using namespace OutputProcessor;
4666 : using General::ScanForReports;
4667 :
4668 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4669 73 : std::string VarOption1;
4670 73 : std::string VarOption2;
4671 : bool DoReport;
4672 : bool SortByName;
4673 :
4674 73 : auto const &op = state.dataOutputProcessor;
4675 73 : auto const &rf = state.dataResultsFramework->resultsFramework;
4676 :
4677 : // See if Report Variables should be turned on
4678 73 : SortByName = false;
4679 219 : ScanForReports(state, "VariableDictionary", DoReport, _, VarOption1, VarOption2);
4680 : // IF (.not. DoReport) RETURN
4681 :
4682 73 : if (DoReport) {
4683 26 : op->ProduceReportVDD = ReportVDD::Yes;
4684 52 : if (VarOption1 == std::string("IDF")) {
4685 26 : op->ProduceReportVDD = ReportVDD::IDF;
4686 : }
4687 26 : if (!VarOption2.empty()) {
4688 2 : if (Util::SameString(VarOption2, "Name") || Util::SameString(VarOption2, "AscendingName")) {
4689 0 : SortByName = true;
4690 : }
4691 : }
4692 : }
4693 :
4694 146 : state.files.rdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.rdd);
4695 146 : state.files.mdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.mdd);
4696 73 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4697 0 : print(state.files.rdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4698 0 : print(state.files.rdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
4699 :
4700 0 : print(state.files.mdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4701 0 : print(state.files.mdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
4702 73 : } else if (op->ProduceReportVDD == ReportVDD::IDF) {
4703 26 : print(state.files.rdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4704 26 : print(state.files.rdd, "! Output:Variable Objects (applicable to this run){}", '\n');
4705 :
4706 26 : print(state.files.mdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4707 26 : print(state.files.mdd, "! Output:Meter Objects (applicable to this run){}", '\n');
4708 : }
4709 :
4710 73 : if (op->ProduceReportVDD == ReportVDD::Yes || op->ProduceReportVDD == ReportVDD::IDF) {
4711 :
4712 26 : auto miVar = op->ddOutVarMap.begin();
4713 26 : int aiVar = 0;
4714 : for (;;) {
4715 8801 : int iVar = -1;
4716 : // Too complicated to do this logic in the for loop header
4717 8801 : if (SortByName) {
4718 0 : if (miVar == op->ddOutVarMap.end()) {
4719 0 : break;
4720 : }
4721 0 : iVar = miVar->second;
4722 0 : ++miVar;
4723 : } else {
4724 8801 : if (aiVar == (int)op->ddOutVars.size()) {
4725 26 : break;
4726 : }
4727 8775 : iVar = aiVar;
4728 8775 : ++aiVar;
4729 : }
4730 :
4731 8775 : auto *ddVar = op->ddOutVars[iVar];
4732 :
4733 8775 : if (ddVar->ReportedOnDDFile) {
4734 0 : continue;
4735 : }
4736 :
4737 : static constexpr std::array<std::string_view, (int)TimeStepType::Num> timeStepNamesLocal = {"Zone", "HVAC"};
4738 8775 : std::string_view timeStepName = timeStepNamesLocal[(int)ddVar->timeStepType];
4739 8775 : std::string_view storeTypeName = storeTypeNames[(int)ddVar->storeType];
4740 8775 : std::string_view varName = ddVar->name;
4741 : std::string_view unitName =
4742 8775 : (ddVar->units == Constant::Units::customEMS) ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)ddVar->units];
4743 8775 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4744 0 : print(state.files.rdd, "{},{},{} [{}]\n", timeStepName, storeTypeName, varName, unitName);
4745 0 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4746 : } else {
4747 8775 : print(state.files.rdd, "Output:Variable,*,{},hourly; !- {} {} [{}]\n", varName, timeStepName, storeTypeName, unitName);
4748 8775 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4749 : }
4750 :
4751 8775 : ddVar->ReportedOnDDFile = true;
4752 8775 : if (SortByName) {
4753 0 : while (ddVar->Next != -1) {
4754 0 : ddVar = op->ddOutVars[ddVar->Next];
4755 :
4756 0 : timeStepName = timeStepTypeNames[(int)ddVar->timeStepType];
4757 0 : storeTypeName = storeTypeNames[(int)ddVar->storeType];
4758 0 : varName = ddVar->name;
4759 :
4760 0 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4761 0 : print(state.files.rdd, "{},{},{} [{}]\n", timeStepName, storeTypeName, varName, unitName);
4762 0 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4763 : } else {
4764 0 : print(state.files.rdd, "Output:Variable,*,{},hourly; !- {} {} [{}]\n", varName, timeStepName, storeTypeName, unitName);
4765 0 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4766 : }
4767 0 : ddVar->ReportedOnDDFile = true;
4768 : } // while (ddVar->Next != 0)
4769 : } // if (SortByName)
4770 8775 : } // for (aiVar, miVar)
4771 26 : } // if (produceReportVDD)
4772 73 : state.files.rdd.close();
4773 :
4774 73 : auto miMeter = op->meterMap.begin();
4775 2908 : for (int aiMeter = 0; aiMeter < (int)op->meters.size() && miMeter != op->meterMap.end(); ++aiMeter, ++miMeter) {
4776 2835 : int iMeter = (SortByName) ? miMeter->second : aiMeter;
4777 2835 : auto *meter = op->meters[iMeter];
4778 2835 : std::string_view unitName = Constant::unitNames[(int)meter->units];
4779 2835 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4780 0 : print(state.files.mdd, "Zone,Meter,{} [{}]\n", meter->Name, unitName);
4781 0 : rf->MDD.push_back(format("Zone,Meter,{} [{}]", meter->Name, unitName));
4782 2835 : } else if (op->ProduceReportVDD == ReportVDD::IDF) {
4783 814 : print(state.files.mdd, "Output:Meter,{},hourly; !- [{}]\n", meter->Name, unitName);
4784 814 : rf->MDD.push_back(format("Output:Meter,{} [{}]", meter->Name, unitName));
4785 814 : print(state.files.mdd, "Output:Meter:Cumulative,{},hourly; !- [{}]\n", meter->Name, unitName);
4786 814 : rf->MDD.push_back(format("Output:Meter:Cumulative,{} [{}]", meter->Name, unitName));
4787 : }
4788 : }
4789 73 : state.files.mdd.close();
4790 73 : } // ProduceRDDMDD()
4791 :
4792 176382 : int AddDDOutVar(EnergyPlusData const &state,
4793 : std::string_view const name, // Variable Name
4794 : OutputProcessor::TimeStepType const timeStepType,
4795 : OutputProcessor::StoreType const storeType,
4796 : OutputProcessor::VariableType const variableType,
4797 : Constant::Units const units,
4798 : std::string_view const customUnitName // the custom name for the units from EMS definition of units
4799 : )
4800 : {
4801 :
4802 : // SUBROUTINE INFORMATION:
4803 : // AUTHOR Linda Lawrie
4804 : // DATE WRITTEN August 2010
4805 :
4806 : // PURPOSE OF THIS SUBROUTINE:
4807 : // This routine maintains a unique list of Output Variables for the
4808 : // Variable Dictionary output.
4809 :
4810 : // Using/Aliasing
4811 : using namespace OutputProcessor;
4812 :
4813 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4814 176382 : auto const &op = state.dataOutputProcessor;
4815 :
4816 176382 : std::string nameUC = Util::makeUPPER(name);
4817 :
4818 176382 : auto found = op->ddOutVarMap.find(nameUC);
4819 176382 : if (found == op->ddOutVarMap.end()) {
4820 59301 : auto *ddVar = new DDOutVar();
4821 59301 : op->ddOutVars.push_back(ddVar);
4822 : // Add to map
4823 59301 : op->ddOutVarMap.insert_or_assign(nameUC, op->ddOutVars.size() - 1);
4824 :
4825 59301 : ddVar->timeStepType = timeStepType;
4826 59301 : ddVar->storeType = storeType;
4827 59301 : ddVar->variableType = variableType;
4828 59301 : ddVar->name = name;
4829 59301 : ddVar->units = units;
4830 59301 : if (!customUnitName.empty() && units == Constant::Units::customEMS) {
4831 2 : ddVar->unitNameCustomEMS = customUnitName;
4832 : }
4833 59301 : return op->ddOutVars.size() - 1;
4834 :
4835 117081 : } else if (units == op->ddOutVars[found->second]->units) {
4836 117078 : return found->second;
4837 :
4838 : } else { // not the same as first units
4839 3 : int dup2 = -1; // for duplicate variable name
4840 3 : auto *ddVarDup = op->ddOutVars[found->second];
4841 3 : while (ddVarDup->Next != -1) {
4842 2 : if (units != op->ddOutVars[ddVarDup->Next]->units) {
4843 0 : ddVarDup = op->ddOutVars[ddVarDup->Next];
4844 0 : continue;
4845 : }
4846 2 : dup2 = ddVarDup->Next;
4847 2 : break;
4848 : }
4849 3 : if (dup2 == -1) {
4850 1 : DDOutVar *ddVar2 = new DDOutVar();
4851 1 : op->ddOutVars.push_back(ddVar2);
4852 : // Don't add this one to the map. Leave the map pointing to the first one
4853 1 : ddVar2->timeStepType = timeStepType;
4854 1 : ddVar2->storeType = storeType;
4855 1 : ddVar2->variableType = variableType;
4856 1 : ddVar2->name = name;
4857 1 : ddVar2->units = units;
4858 1 : if (!customUnitName.empty() && units == Constant::Units::customEMS) {
4859 0 : ddVar2->unitNameCustomEMS = customUnitName;
4860 : }
4861 1 : ddVarDup->Next = op->ddOutVars.size() - 1;
4862 :
4863 1 : return op->ddOutVars.size() - 1;
4864 : } else {
4865 2 : return dup2;
4866 : } // if (dup2 == 0)
4867 : } // if (unitsForVar)
4868 176382 : } // AddDDOutVar()
4869 :
4870 26 : int initErrorFile(EnergyPlusData &state)
4871 : {
4872 26 : state.files.err_stream = std::make_unique<std::ofstream>(state.files.outputErrFilePath);
4873 26 : if (state.files.err_stream->bad()) {
4874 0 : DisplayString(state, fmt::format("ERROR: Could not open file {} for output (write).", state.files.outputErrFilePath));
4875 0 : return EXIT_FAILURE;
4876 : }
4877 26 : return EXIT_SUCCESS;
4878 : } // initErrorFile()
4879 :
4880 : } // namespace EnergyPlus
|