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 48134 : 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 48134 : Real64 constexpr FracToMin(60.0);
136 48134 : return ((state.dataGlobal->CurrentTime + state.dataHVACGlobal->SysTimeElapsed) - int(state.dataGlobal->CurrentTime)) * FracToMin;
137 : }
138 :
139 803 : 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 803 : auto &op = state.dataOutputProcessor;
149 :
150 : // Initialize end use category names - the indices must match up with endUseNames in OutputReportTabular
151 803 : op->EndUseCategory.allocate((int)Constant::EndUse::Num);
152 803 : op->EndUseCategory((int)Constant::EndUse::Heating + 1).Name = "Heating";
153 803 : op->EndUseCategory((int)Constant::EndUse::Cooling + 1).Name = "Cooling";
154 803 : op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).Name = "InteriorLights";
155 803 : op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).Name = "ExteriorLights";
156 803 : op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).Name = "InteriorEquipment";
157 803 : op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).Name = "ExteriorEquipment";
158 803 : op->EndUseCategory((int)Constant::EndUse::Fans + 1).Name = "Fans";
159 803 : op->EndUseCategory((int)Constant::EndUse::Pumps + 1).Name = "Pumps";
160 803 : op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).Name = "HeatRejection";
161 803 : op->EndUseCategory((int)Constant::EndUse::Humidification + 1).Name = "Humidifier";
162 803 : op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).Name = "HeatRecovery";
163 803 : op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).Name = "WaterSystems";
164 803 : op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).Name = "Refrigeration";
165 803 : 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 803 : op->EndUseCategory((int)Constant::EndUse::Heating + 1).DisplayName = "Heating";
169 803 : op->EndUseCategory((int)Constant::EndUse::Cooling + 1).DisplayName = "Cooling";
170 803 : op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).DisplayName = "Interior Lighting";
171 803 : op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).DisplayName = "Exterior Lighting";
172 803 : op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).DisplayName = "Interior Equipment";
173 803 : op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).DisplayName = "Exterior Equipment";
174 803 : op->EndUseCategory((int)Constant::EndUse::Fans + 1).DisplayName = "Fans";
175 803 : op->EndUseCategory((int)Constant::EndUse::Pumps + 1).DisplayName = "Pumps";
176 803 : op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).DisplayName = "Heat Rejection";
177 803 : op->EndUseCategory((int)Constant::EndUse::Humidification + 1).DisplayName = "Humidification";
178 803 : op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).DisplayName = "Heat Recovery";
179 803 : op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).DisplayName = "Water Systems";
180 803 : op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).DisplayName = "Refrigeration";
181 803 : op->EndUseCategory((int)Constant::EndUse::Cogeneration + 1).DisplayName = "Generators";
182 :
183 803 : op->OutputInitialized = true;
184 :
185 803 : op->TimeStepZoneSec = double(state.dataGlobal->MinutesInTimeStep) * 60.0;
186 :
187 1606 : state.files.mtd.ensure_open(state, "InitializeMeters", state.files.outputControl.mtd);
188 803 : } // InitializeOutput()
189 :
190 42627 : 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 42627 : auto &op = state.dataOutputProcessor;
201 :
202 42627 : Constant::EndUse endUse = endUseCat2endUse[(int)endUseCat];
203 42627 : if (endUse == Constant::EndUse::Invalid) {
204 0 : ShowSevereError(state, format("Nonexistent end use passed to addEndUseSpaceType={}", endUseCatNames[(int)endUseCat]));
205 0 : return;
206 : }
207 :
208 42627 : auto &endUseCategory = op->EndUseCategory((int)endUse + 1);
209 :
210 48618 : for (int EndUseSubNum = 1; EndUseSubNum <= endUseCategory.NumSubcategories; ++EndUseSubNum) {
211 42158 : if (Util::SameString(endUseCategory.SubcategoryName(EndUseSubNum), endUseSubName)) {
212 36167 : return; // Subcategory already exists, no further action required
213 : }
214 : }
215 :
216 : // Add the subcategory by reallocating the array
217 6460 : endUseCategory.SubcategoryName.redimension(++endUseCategory.NumSubcategories);
218 6460 : endUseCategory.SubcategoryName(endUseCategory.NumSubcategories) = endUseSubName;
219 :
220 6460 : if (endUseCategory.NumSubcategories > op->MaxNumSubcategories) {
221 706 : op->MaxNumSubcategories = endUseCategory.NumSubcategories;
222 : }
223 : } // addEndUseSubcategory()
224 :
225 9195 : void addEndUseSpaceType(EnergyPlusData &state, OutputProcessor::EndUseCat sovEndUseCat, std::string_view const EndUseSpaceTypeName)
226 : {
227 9195 : auto &op = state.dataOutputProcessor;
228 :
229 9195 : Constant::EndUse endUse = endUseCat2endUse[(int)sovEndUseCat];
230 :
231 9195 : 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 9195 : auto &endUseCat = op->EndUseCategory((int)endUse + 1);
237 :
238 9243 : for (int endUseSpTypeNum = 1; endUseSpTypeNum <= endUseCat.numSpaceTypes; ++endUseSpTypeNum) {
239 7892 : if (Util::SameString(endUseCat.spaceTypeName(endUseSpTypeNum), EndUseSpaceTypeName)) {
240 7844 : return; // SpaceType already exists, no further action required
241 : }
242 : }
243 :
244 : // Add the space type by reallocating the array
245 1351 : endUseCat.spaceTypeName.redimension(++endUseCat.numSpaceTypes);
246 1351 : endUseCat.spaceTypeName(endUseCat.numSpaceTypes) = EndUseSpaceTypeName;
247 :
248 1351 : if (endUseCat.numSpaceTypes > op->maxNumEndUseSpaceTypes) {
249 12 : op->maxNumEndUseSpaceTypes = endUseCat.numSpaceTypes;
250 : }
251 : } // addEndUseSpaceType()
252 :
253 1606 : 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 1606 : if (state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep != nullptr) {
276 0 : ShowFatalError(state, format("SetupTimePointers was already called for {}", timeStepTypeNames[(int)timeStep]));
277 : }
278 1606 : state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep = &TimeStep;
279 1606 : }
280 :
281 6528855 : 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 6528855 : GetReportVariableInput(state);
312 :
313 6528855 : auto const &op = state.dataOutputProcessor;
314 :
315 220922645 : for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
316 214393790 : auto *reqVar = op->reqVars[iReqVar];
317 :
318 214393790 : if (!Util::SameString(reqVar->name, Name)) {
319 213267253 : continue;
320 : }
321 :
322 2190455 : if (!reqVar->key.empty() && !(reqVar->is_simple_string && Util::SameString(reqVar->key, Key)) &&
323 2190455 : !(!reqVar->is_simple_string && RE2::FullMatch(std::string{Key}, *(reqVar->case_insensitive_pattern)))) {
324 1063918 : continue;
325 : }
326 :
327 : // A match. Make sure doesn't duplicate
328 62619 : reqVar->Used = true;
329 62619 : 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 64866 : for (int iReqVar2 : reqVarList) {
332 3561 : if (op->reqVars[iReqVar2]->freq == reqVar->freq && op->reqVars[iReqVar2]->sched == reqVar->sched) {
333 1314 : Dup = true;
334 1314 : break;
335 : }
336 62619 : }
337 :
338 62619 : if (!Dup) {
339 61305 : reqVarList.push_back(iReqVar);
340 : }
341 : }
342 6528855 : }
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 28571 : 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 28571 : 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 28571 : const std::string FreqStringUpper = Util::makeUPPER(FreqString);
400 28571 : std::string::size_type const LenString = min(len(FreqString), static_cast<std::string::size_type>(4u));
401 :
402 28571 : if (LenString < 4u) {
403 0 : return freq;
404 : }
405 :
406 28571 : std::string const FreqStringTrim(FreqStringUpper.substr(0, LenString));
407 186696 : for (unsigned Loop = 0; Loop < FreqValues.size(); ++Loop) {
408 93348 : if (FreqStringTrim == PossibleFreqs[Loop]) {
409 28571 : 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 28571 : freq = std::max(FreqValues[Loop], state.dataOutputProcessor->minimumReportFreq);
414 28571 : break;
415 : }
416 : }
417 28571 : return freq;
418 28571 : }
419 :
420 6528855 : 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 6528855 : constexpr std::string_view routineName = "GetReportVariableInput";
450 :
451 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
452 : int NumAlpha;
453 : int NumNumbers;
454 : int IOStat;
455 6528855 : bool ErrorsFound(false); // If errors detected in input
456 6528855 : std::string cCurrentModuleObject;
457 6528855 : Array1D_string cAlphaArgs(4);
458 6528855 : Array1D_string cAlphaFieldNames(4);
459 6528855 : Array1D_bool lAlphaBlanks(4);
460 6528855 : Array1D<Real64> rNumericArgs(1);
461 6528855 : Array1D_string cNumericFieldNames(1);
462 6528855 : Array1D_bool lNumericBlanks(1);
463 6528855 : auto &op = state.dataOutputProcessor;
464 :
465 : // Bail out if the input has already been read in
466 6528855 : if (!op->GetOutputInputFlag) {
467 6528052 : return;
468 : }
469 803 : op->GetOutputInputFlag = false;
470 :
471 : // First check environment variable to see of possible override for minimum reporting frequency
472 803 : 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 803 : cCurrentModuleObject = "Output:Variable";
482 803 : int numReqVariables = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
483 :
484 22660 : for (int Loop = 1; Loop <= numReqVariables; ++Loop) {
485 :
486 21857 : 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 21857 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
500 :
501 : // Check for duplicates?
502 21857 : ReqVar *reqVar = new ReqVar();
503 21857 : op->reqVars.push_back(reqVar);
504 :
505 21857 : reqVar->key = cAlphaArgs(1);
506 21857 : if (reqVar->key == "*") {
507 15665 : reqVar->key = std::string();
508 : }
509 :
510 21857 : bool is_simple_string = !DataOutputs::isKeyRegexLike(reqVar->key);
511 21857 : reqVar->is_simple_string = is_simple_string;
512 21857 : if (!is_simple_string) {
513 0 : reqVar->case_insensitive_pattern = std::make_shared<RE2>("(?i)" + reqVar->key);
514 : }
515 :
516 21857 : std::string::size_type const lbpos = index(cAlphaArgs(2), '['); // Remove Units designation if user put it in
517 21857 : 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 21857 : reqVar->name = cAlphaArgs(2);
523 :
524 21857 : reqVar->freq = determineFrequency(state, Util::makeUPPER(cAlphaArgs(3)));
525 21857 : if (reqVar->freq == ReportFreq::Invalid) {
526 0 : ShowSevereInvalidKey(state, eoh, cAlphaFieldNames(3), cAlphaArgs(3));
527 0 : ErrorsFound = true;
528 : }
529 :
530 : // Schedule information
531 21857 : if (lAlphaBlanks(4)) {
532 21222 : reqVar->sched = nullptr;
533 635 : } 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 21857 : reqVar->Used = false;
539 : }
540 :
541 803 : if (ErrorsFound) {
542 0 : ShowFatalError(state, format("GetReportVariableInput:{}: errors in input.", cCurrentModuleObject));
543 : }
544 45697167 : }
545 :
546 104602 : 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 104602 : General::DecodeMonDayHrMin(date, Mon, Day, Hour, Minute);
570 :
571 104602 : switch (freq) {
572 20386 : case ReportFreq::Day:
573 20386 : return format("{:2},{:2}", Hour, Minute);
574 70760 : case ReportFreq::Month:
575 70760 : return format("{:2},{:2},{:2}", Day, Hour, Minute);
576 13456 : case ReportFreq::Year:
577 : case ReportFreq::Simulation:
578 13456 : 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 782 : 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 782 : constexpr std::string_view routineName = "GetCustomMeterInput";
660 :
661 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
662 782 : auto &op = state.dataOutputProcessor;
663 782 : auto &ip = state.dataInputProcessing->inputProcessor;
664 782 : auto &ipsc = state.dataIPShortCut;
665 :
666 : int NumAlpha;
667 : int NumNumbers;
668 : int IOStat;
669 782 : Array1D_string NamesOfKeys; // Specific key name
670 782 : Array1D_int IndexesForKeyVar; // Array index
671 :
672 782 : Array1D_int VarsOnSourceMeter;
673 :
674 782 : bool BigErrorsFound = false;
675 :
676 782 : int numCustomMeters = 0, numCustomDecMeters = 0;
677 782 : std::vector<std::string> customMeterNames;
678 782 : std::vector<std::string> customDecMeterNames;
679 2346 : if (auto const found = ip->epJSON.find("Meter:Custom"); found != ip->epJSON.end()) {
680 133 : for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomMeters) {
681 91 : customMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
682 42 : }
683 782 : }
684 :
685 2346 : if (auto const found = ip->epJSON.find("Meter:CustomDecrement"); found != ip->epJSON.end()) {
686 74 : for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomDecMeters) {
687 37 : customDecMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
688 37 : }
689 782 : }
690 :
691 782 : ipsc->cCurrentModuleObject = "Meter:Custom";
692 873 : for (int Loop = 1; Loop <= numCustomMeters; ++Loop) {
693 182 : ip->getObjectItem(state,
694 91 : ipsc->cCurrentModuleObject,
695 : Loop,
696 91 : ipsc->cAlphaArgs,
697 : NumAlpha,
698 91 : ipsc->rNumericArgs,
699 : NumNumbers,
700 : IOStat,
701 91 : ipsc->lNumericFieldBlanks,
702 91 : ipsc->lAlphaFieldBlanks,
703 91 : ipsc->cAlphaFieldNames,
704 91 : ipsc->cNumericFieldNames);
705 :
706 91 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
707 :
708 91 : std::string meterName = ipsc->cAlphaArgs(1);
709 91 : std::string::size_type lbrackPos = index(meterName, '[');
710 91 : if (lbrackPos != std::string::npos) {
711 0 : meterName.erase(lbrackPos);
712 : }
713 :
714 91 : std::string meterNameUC = Util::makeUPPER(meterName);
715 :
716 : // Check for duplicate name
717 91 : 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 91 : static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
726 91 : 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 91 : 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 91 : bool foundBadSrc = false;
740 91 : bool itemsAssigned = false;
741 :
742 457 : for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
743 366 : if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
744 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
745 0 : foundBadSrc = true;
746 0 : break;
747 : }
748 :
749 366 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
750 366 : lbrackPos = index(meterOrVarNameUC, '[');
751 366 : if (lbrackPos != std::string::npos) {
752 0 : meterOrVarNameUC.erase(lbrackPos);
753 : }
754 :
755 : // A custom meter cannot reference another custom meter
756 366 : if (std::find(customMeterNames.begin(), customMeterNames.end(), meterOrVarNameUC) != customMeterNames.end()) {
757 0 : ShowWarningError(state,
758 0 : format(R"(Meter:Custom="{}", contains a reference to another Meter:Custom in field: {}="{}".)",
759 0 : ipsc->cAlphaArgs(1),
760 0 : ipsc->cAlphaFieldNames(fldIndex + 1),
761 0 : ipsc->cAlphaArgs(fldIndex + 1)));
762 0 : foundBadSrc = true;
763 0 : break;
764 : }
765 :
766 : // A custom meter cannot reference another customDec meter
767 366 : 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 366 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
778 45 : int srcMeterNum = foundSrcMeter->second;
779 45 : auto *srcMeter = op->meters[srcMeterNum];
780 45 : assert(srcMeter->type == MeterType::Normal);
781 :
782 : // If it's the first meter, it gets to set the units
783 45 : if (units == Constant::Units::Invalid) {
784 44 : units = srcMeter->units;
785 44 : itemsAssigned = true;
786 1 : } 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 321 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
803 320 : int srcDDVarNum = foundSrcDDVar->second;
804 320 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
805 :
806 : // Has to be a summed variable
807 320 : 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 320 : if (units == Constant::Units::Invalid) {
824 46 : units = srcDDVar->units;
825 : // Otherwise it has to match the existing units
826 274 : } 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 320 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
838 : // Have already checked for mismatching units between meter and source variable and assigned units
839 320 : if (KeyIsStar) {
840 11 : if (srcDDVar->keyOutVarNums.empty()) {
841 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
842 0 : foundBadSrc = true;
843 0 : break;
844 : }
845 :
846 11 : itemsAssigned = true;
847 : } else { // Key is not "*"
848 309 : bool foundKey = false;
849 3337 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
850 3337 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
851 309 : foundKey = true;
852 309 : itemsAssigned = true;
853 309 : break;
854 : }
855 309 : }
856 309 : 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 2 : ShowWarningError(state,
867 4 : format(R"(Meter:Custom="{}", invalid {}="{}".)",
868 1 : ipsc->cAlphaArgs(1),
869 1 : ipsc->cAlphaFieldNames(fldIndex + 1),
870 1 : ipsc->cAlphaArgs(fldIndex + 1)));
871 3 : ShowContinueError(state, "...will not be shown with the Meter results.");
872 : // Not setting the foundBadSrc flag here.
873 687 : }
874 :
875 366 : } // for (fldIndex)
876 :
877 : // Somehow, this meter is not linked to any variables either directly or via another meter
878 91 : if (!itemsAssigned) {
879 1 : ShowWarningError(state, format("Meter:Custom=\"{}\", no items assigned ", ipsc->cAlphaArgs(1)));
880 2 : 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 1 : continue;
883 : }
884 :
885 : // One of the sources is bad
886 90 : if (foundBadSrc) {
887 0 : continue;
888 : }
889 :
890 90 : auto *meter = new Meter(meterName);
891 90 : meter->type = MeterType::Custom;
892 90 : meter->resource = resource;
893 90 : meter->units = units;
894 90 : bool errFlag = false;
895 90 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
896 90 : if (errFlag) {
897 1 : ShowContinueError(state, format("..on {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
898 3 : ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
899 : }
900 :
901 : // This meter is good
902 90 : int meterNum = op->meters.size();
903 90 : op->meters.push_back(meter);
904 90 : op->meterMap.insert_or_assign(meterNameUC, meterNum);
905 :
906 90 : for (ReportFreq reportFreq :
907 720 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
908 540 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
909 : }
910 :
911 90 : for (ReportFreq reportFreq :
912 720 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
913 540 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
914 : }
915 :
916 : // Do the loop again, this time without error checking
917 455 : for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
918 : // No need to check for empty fields
919 365 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
920 365 : lbrackPos = index(meterOrVarNameUC, '[');
921 365 : if (lbrackPos != std::string::npos) {
922 0 : meterOrVarNameUC.erase(lbrackPos);
923 : }
924 :
925 : // No need to check for custom source meters
926 365 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
927 45 : int srcMeterNum = foundSrcMeter->second;
928 45 : auto *srcMeter = op->meters[srcMeterNum];
929 45 : 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 45 : 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 45 : meter->srcMeterNums.push_back(srcMeterNum);
946 45 : srcMeter->dstMeterNums.push_back(meterNum);
947 :
948 386 : for (int srcVarNum : srcMeter->srcVarNums) {
949 341 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
950 341 : meter->srcVarNums.push_back(srcVarNum);
951 341 : op->outVars[srcVarNum]->meterNums.push_back(meterNum);
952 : }
953 45 : }
954 :
955 : // It's a variable
956 320 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
957 320 : int srcDDVarNum = foundSrcDDVar->second;
958 320 : 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 320 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
964 : // Have already checked for mismatching units between meter and source variable and assigned units
965 320 : if (KeyIsStar) {
966 : // No need to check for empty keys
967 22 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
968 11 : 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 11 : meter->srcVarNums.push_back(keyOutVarNum);
976 11 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
977 : }
978 11 : }
979 : } else { // Key is not "*"
980 3337 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
981 3337 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
982 309 : 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 309 : meter->srcVarNums.push_back(keyOutVarNum);
989 309 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
990 : }
991 309 : break;
992 : }
993 309 : }
994 : } // if (keyIsStar)
995 685 : } // if (meter or variable)
996 :
997 365 : } // for (fldIndex)
998 92 : } // for (Loop)
999 :
1000 782 : ipsc->cCurrentModuleObject = "Meter:CustomDecrement";
1001 819 : for (int Loop = 1; Loop <= numCustomDecMeters; ++Loop) {
1002 74 : ip->getObjectItem(state,
1003 37 : ipsc->cCurrentModuleObject,
1004 : Loop,
1005 37 : ipsc->cAlphaArgs,
1006 : NumAlpha,
1007 37 : ipsc->rNumericArgs,
1008 : NumNumbers,
1009 : IOStat,
1010 37 : ipsc->lNumericFieldBlanks,
1011 37 : ipsc->lAlphaFieldBlanks,
1012 37 : ipsc->cAlphaFieldNames,
1013 37 : ipsc->cNumericFieldNames);
1014 :
1015 37 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
1016 :
1017 37 : std::string meterName = ipsc->cAlphaArgs(1);
1018 37 : std::string::size_type lbrackPos = index(meterName, '[');
1019 37 : if (lbrackPos != std::string::npos) {
1020 0 : meterName.erase(lbrackPos);
1021 : }
1022 37 : std::string meterNameUC = Util::makeUPPER(meterName);
1023 :
1024 : // Search for duplicate name
1025 37 : 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 37 : static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
1034 37 : 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 37 : bool itemsAssigned = false;
1041 :
1042 37 : std::string decMeterName = ipsc->cAlphaArgs(3);
1043 37 : lbrackPos = index(decMeterName, '[');
1044 37 : if (lbrackPos != std::string::npos) {
1045 0 : decMeterName.erase(lbrackPos);
1046 : }
1047 37 : std::string decMeterNameUC = Util::makeUPPER(decMeterName);
1048 :
1049 : // DecMeter cannot be a Meter:Custom
1050 37 : 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 37 : auto foundDecMeter = op->meterMap.find(decMeterName);
1061 37 : if (foundDecMeter == op->meterMap.end()) {
1062 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), decMeterName);
1063 0 : ErrorsFound = true;
1064 0 : continue;
1065 : }
1066 :
1067 37 : int decMeterNum = foundDecMeter->second;
1068 37 : auto *decMeter = op->meters[decMeterNum];
1069 37 : assert(decMeter->type == MeterType::Normal);
1070 :
1071 37 : Constant::Units units = decMeter->units;
1072 :
1073 37 : 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 37 : bool foundBadSrc = false;
1081 :
1082 74 : for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
1083 37 : if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
1084 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
1085 0 : foundBadSrc = true;
1086 0 : break;
1087 : }
1088 :
1089 37 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
1090 37 : lbrackPos = index(meterOrVarNameUC, '[');
1091 37 : if (lbrackPos != std::string::npos) {
1092 0 : meterOrVarNameUC.erase(lbrackPos);
1093 : }
1094 :
1095 : // A custom meter cannot reference another custom meter
1096 37 : 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 37 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
1107 35 : int srcMeterNum = foundSrcMeter->second;
1108 35 : auto *srcMeter = op->meters[srcMeterNum];
1109 35 : assert(srcMeter->type == MeterType::Normal || srcMeter->type == MeterType::Custom);
1110 :
1111 : // If it's the first meter, it gets to set the units
1112 35 : if (units == Constant::Units::Invalid) {
1113 0 : units = srcMeter->units;
1114 0 : itemsAssigned = true;
1115 35 : } 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 2 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
1132 2 : int srcDDVarNum = foundSrcDDVar->second;
1133 2 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
1134 :
1135 : // Has to be a summed variable
1136 2 : 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 2 : if (units == Constant::Units::Invalid) {
1153 0 : units = srcDDVar->units;
1154 : // Otherwise it has to match the existing units
1155 2 : } 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 2 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
1167 : // Have already checked for mismatching units between meter and source variable and assigned units
1168 2 : 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 2 : bool foundKey = false;
1178 97 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1179 97 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
1180 2 : foundKey = true;
1181 2 : itemsAssigned = true;
1182 2 : break;
1183 : }
1184 2 : }
1185 2 : 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 39 : }
1204 :
1205 37 : } // for (fldIndex)
1206 :
1207 : // Somehow, this meter is not linked to any variables either directly or via another meter
1208 37 : 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 37 : if (foundBadSrc) {
1217 0 : continue;
1218 : }
1219 :
1220 37 : auto *meter = new Meter(meterName);
1221 37 : meter->type = MeterType::CustomDec;
1222 37 : meter->resource = resource;
1223 37 : meter->units = units;
1224 37 : bool errFlag = false;
1225 37 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
1226 37 : 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 37 : meter->decMeterNum = decMeterNum;
1232 :
1233 : // This meter is good
1234 37 : int meterNum = op->meters.size();
1235 37 : op->meters.push_back(meter);
1236 37 : op->meterMap.insert_or_assign(meterNameUC, meterNum);
1237 :
1238 37 : for (ReportFreq reportFreq :
1239 296 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1240 222 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
1241 : }
1242 :
1243 37 : for (ReportFreq reportFreq :
1244 296 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1245 222 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
1246 : }
1247 :
1248 : // Links meter to dec meter and its output variable and vice versa
1249 37 : meter->srcMeterNums.push_back(meter->decMeterNum);
1250 37 : 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 74 : for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
1261 : // No need to check for empty fields
1262 37 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
1263 37 : lbrackPos = index(meterOrVarNameUC, '[');
1264 37 : if (lbrackPos != std::string::npos) {
1265 0 : meterOrVarNameUC.erase(lbrackPos);
1266 : }
1267 :
1268 : // No need to check for custom source meters
1269 37 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
1270 35 : int srcMeterNum = foundSrcMeter->second;
1271 35 : auto *srcMeter = op->meters[srcMeterNum];
1272 35 : 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 35 : 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 35 : meter->srcMeterNums.push_back(srcMeterNum);
1289 35 : srcMeter->dstMeterNums.push_back(meterNum);
1290 :
1291 212 : for (int srcVarNum : srcMeter->srcVarNums) {
1292 177 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
1293 177 : meter->srcVarNums.push_back(srcVarNum);
1294 177 : op->outVars[srcVarNum]->meterNums.push_back(meterNum);
1295 : }
1296 35 : }
1297 :
1298 : // It's a variable
1299 2 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
1300 2 : int srcDDVarNum = foundSrcDDVar->second;
1301 2 : 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 2 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
1307 : // Have already checked for mismatching units between meter and source variable and assigned units
1308 2 : 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 97 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1324 97 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
1325 2 : 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 2 : meter->srcVarNums.push_back(keyOutVarNum);
1332 2 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
1333 : }
1334 2 : break;
1335 : }
1336 2 : }
1337 : } // if (keyIsStar)
1338 39 : } // if (meter or variable)
1339 :
1340 37 : } // for (fldIndex)
1341 37 : }
1342 :
1343 782 : if (BigErrorsFound) {
1344 0 : ErrorsFound = true;
1345 : }
1346 782 : }
1347 :
1348 308144 : 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 308144 : auto &op = state.dataOutputProcessor;
1369 :
1370 308144 : int meterNum = -1;
1371 308144 : Meter *meter = nullptr;
1372 :
1373 308144 : std::string nameUC = Util::makeUPPER(Name);
1374 :
1375 308144 : if (auto found = op->meterMap.find(nameUC); found != op->meterMap.end()) {
1376 209693 : meterNum = found->second;
1377 209693 : meter = op->meters[meterNum];
1378 : } else {
1379 :
1380 98451 : meterNum = op->meters.size();
1381 98451 : meter = new Meter(Name);
1382 98451 : op->meters.push_back(meter);
1383 98451 : op->meterMap.insert_or_assign(nameUC, meterNum);
1384 :
1385 98451 : meter->type = MeterType::Normal;
1386 98451 : meter->resource = resource;
1387 98451 : meter->endUseCat = endUseCat;
1388 98451 : meter->EndUseSub = EndUseSub;
1389 98451 : meter->group = group;
1390 98451 : meter->units = units;
1391 98451 : meter->CurTSValue = 0.0;
1392 :
1393 98451 : for (ReportFreq reportFreq :
1394 787608 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1395 590706 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
1396 : }
1397 :
1398 98451 : for (ReportFreq reportFreq :
1399 787608 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1400 590706 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
1401 : }
1402 :
1403 98451 : if (meter->resource != Constant::eResource::Invalid) {
1404 98451 : bool errFlag = false;
1405 98451 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, units, errFlag);
1406 98451 : if (errFlag) {
1407 0 : ShowContinueError(state, format("..on Meter=\"{}\".", Name));
1408 0 : ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
1409 : }
1410 : }
1411 308144 : }
1412 :
1413 : // outVarNum == -1 is only true in unit tests
1414 308144 : if (outVarNum != -1) {
1415 308144 : OutVar *var = op->outVars[outVarNum];
1416 308144 : var->meterNums.push_back(meterNum);
1417 308144 : meter->srcVarNums.push_back(outVarNum);
1418 : }
1419 :
1420 308144 : return meterNum;
1421 308144 : }
1422 :
1423 60894 : 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 60894 : std::string_view resourceName = Constant::eResourceNames[(int)resource];
1444 :
1445 60894 : std::string endUseSub = standardizeEndUseSub(endUseCat, EndUseSub);
1446 :
1447 60894 : if (!endUseSub.empty()) {
1448 42627 : addEndUseSubcategory(state, endUseCat, endUseSub);
1449 : }
1450 :
1451 60894 : if (!SpaceType.empty()) {
1452 9195 : addEndUseSpaceType(state, endUseCat, SpaceType);
1453 : }
1454 :
1455 60894 : std::string meterName = format("{}:Facility", resourceName);
1456 60894 : AddMeter(state, meterName, units, resource, EndUseCat::Invalid, "", Group::Invalid, outVarNum);
1457 :
1458 60894 : if (group != Group::Invalid) {
1459 54715 : std::string groupMeterName = format("{}:{}", resourceName, groupNames[(int)group]);
1460 54715 : AddMeter(state, groupMeterName, units, resource, EndUseCat::Invalid, "", group, outVarNum);
1461 :
1462 54715 : if (group == Group::Building) {
1463 20831 : if (!ZoneName.empty()) {
1464 20486 : std::string zoneMeterName = format("{}:Zone:{}", resourceName, ZoneName);
1465 20486 : AddMeter(state, zoneMeterName, units, resource, EndUseCat::Invalid, "", Group::Zone, outVarNum);
1466 20486 : }
1467 20831 : if (!SpaceType.empty()) {
1468 9195 : std::string spaceMeterName = format("{}:SpaceType:{}", resourceName, SpaceType);
1469 9195 : AddMeter(state, spaceMeterName, units, resource, EndUseCat::Invalid, "", Group::SpaceType, outVarNum);
1470 9195 : }
1471 : } // if (Group == "Building")
1472 54715 : }
1473 :
1474 : //!! Following if we do EndUse by ResourceType
1475 60894 : if (endUseCat != EndUseCat::Invalid) {
1476 60865 : std::string_view endUseCatName = endUseCatNames[(int)endUseCat];
1477 60865 : std::string enduseMeterName = format("{}:{}", endUseCatName, resourceName);
1478 60865 : AddMeter(state, enduseMeterName, units, resource, endUseCat, "", Group::Invalid, outVarNum);
1479 :
1480 60865 : if (group == Group::Building) { // Match to Zone and Space
1481 20825 : if (!ZoneName.empty()) {
1482 20486 : std::string enduseZoneMeterName = format("{}:{}:Zone:{}", endUseCatName, resourceName, ZoneName);
1483 20486 : AddMeter(state, enduseZoneMeterName, units, resource, endUseCat, "", Group::Zone, outVarNum);
1484 20486 : }
1485 20825 : if (!SpaceType.empty()) {
1486 9195 : std::string enduseSpaceMeterName = format("{}:{}:SpaceType:{}", endUseCatName, resourceName, SpaceType);
1487 9195 : AddMeter(state, enduseSpaceMeterName, units, resource, endUseCat, "", Group::SpaceType, outVarNum);
1488 9195 : }
1489 : }
1490 :
1491 : // End-Use Subcategories
1492 60865 : if (!endUseSub.empty()) {
1493 42627 : std::string subEnduseMeterName = format("{}:{}:{}", endUseSub, endUseCatNames[(int)endUseCat], resourceName);
1494 42627 : AddMeter(state, subEnduseMeterName, units, resource, endUseCat, endUseSub, Group::Invalid, outVarNum);
1495 :
1496 42627 : if (group == Group::Building) { // Match to Zone and Space
1497 20825 : if (!ZoneName.empty()) {
1498 20486 : std::string subEnduseZoneMeterName = format("{}:{}:{}:Zone:{}", endUseSub, endUseCatName, resourceName, ZoneName);
1499 20486 : AddMeter(state, subEnduseZoneMeterName, units, resource, endUseCat, endUseSub, Group::Zone, outVarNum);
1500 20486 : }
1501 20825 : if (!SpaceType.empty()) {
1502 9195 : std::string subEnduseSpaceMeterName = format("{}:{}:{}:SpaceType:{}", endUseSub, endUseCatName, resourceName, SpaceType);
1503 9195 : AddMeter(state, subEnduseSpaceMeterName, units, resource, endUseCat, endUseSub, Group::SpaceType, outVarNum);
1504 9195 : }
1505 : } // if (sovGroup == Building)
1506 42627 : } // if (!endUseSub.empty())
1507 60865 : } // if (sovEndUseCat != Invalid)
1508 60894 : } // AttachMeters()
1509 :
1510 60894 : std::string standardizeEndUseSub(EndUseCat endUseCat, std::string_view endUseSubName)
1511 : {
1512 60894 : if (!endUseSubName.empty()) {
1513 39456 : return std::string(endUseSubName);
1514 41166 : } else if (endUseCat == EndUseCat::Invalid) {
1515 58 : return "";
1516 41137 : } else if (endUseCat2endUse[(int)endUseCat] != Constant::EndUse::Invalid) {
1517 45798 : return "General";
1518 : } else {
1519 36476 : return "";
1520 : }
1521 : }
1522 :
1523 98578 : 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 98578 : switch (resource) {
1555 46084 : 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 46084 : IPUnits = RT_IPUnits::Electricity;
1561 46084 : } break;
1562 2759 : case Constant::eResource::NaturalGas: {
1563 2759 : IPUnits = RT_IPUnits::Gas;
1564 2759 : } break;
1565 3438 : 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 3438 : IPUnits = RT_IPUnits::Water;
1571 3438 : } break;
1572 2018 : case Constant::eResource::DistrictCooling:
1573 : case Constant::eResource::PlantLoopCoolingDemand: {
1574 2018 : IPUnits = RT_IPUnits::Cooling;
1575 2018 : } break;
1576 44279 : default: {
1577 44279 : if (units == Constant::Units::m3) {
1578 284 : IPUnits = RT_IPUnits::OtherM3;
1579 43995 : } else if (units == Constant::Units::kg) {
1580 5540 : IPUnits = RT_IPUnits::OtherKG;
1581 38455 : } else if (units == Constant::Units::L) {
1582 284 : IPUnits = RT_IPUnits::OtherL;
1583 : } else {
1584 38171 : IPUnits = RT_IPUnits::OtherJ;
1585 : }
1586 44279 : } break;
1587 : } // switch
1588 :
1589 : // write(outputfiledebug,*) 'resourcetype=',TRIM(resourcetype)
1590 : // write(outputfiledebug,*) 'ipunits type=',CodeForIPUnits
1591 98578 : if (units != Constant::Units::kg && units != Constant::Units::J && units != Constant::Units::m3 && units != Constant::Units::L) {
1592 2 : ShowWarningMessage(
1593 2 : state, format("DetermineMeterIPUnits: Meter units not recognized for IP Units conversion=[{}].", Constant::unitNames[(int)units]));
1594 1 : ErrorsFound = true;
1595 : }
1596 98578 : return IPUnits;
1597 : }
1598 :
1599 491160 : 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 491160 : if (state.dataGlobal->WarmupFlag) {
1611 3456 : return;
1612 : }
1613 :
1614 487704 : auto &op = state.dataOutputProcessor;
1615 :
1616 487704 : if (op->meters.size() == 0 || op->meterValues.size() == 0) {
1617 0 : return;
1618 : }
1619 :
1620 49590960 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
1621 49103256 : auto *meter = op->meters[iMeter];
1622 49103256 : if (meter->type != MeterType::CustomDec && meter->type != MeterType::CustomDiff) {
1623 49026360 : meter->periods[(int)ReportFreq::TimeStep].Value += op->meterValues[iMeter];
1624 : // Is this correct? What is going on here?
1625 : } else {
1626 76896 : 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 49103256 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1632 49103256 : meter->periods[(int)ReportFreq::Hour].Value += TSValue;
1633 49103256 : meter->periods[(int)ReportFreq::Day].Value += TSValue;
1634 49103256 : meter->periods[(int)ReportFreq::Month].Value += TSValue;
1635 49103256 : meter->periods[(int)ReportFreq::Year].Value += TSValue;
1636 49103256 : meter->periods[(int)ReportFreq::Simulation].Value += TSValue;
1637 49103256 : meter->periodFinYrSM.Value += TSValue;
1638 : } // for (iMeter)
1639 :
1640 : // Set Max
1641 49590960 : for (auto *meter : op->meters) {
1642 49103256 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1643 49103256 : Real64 TSValueComp = TSValue; // - 0.00001;
1644 :
1645 : // Todo - HRMinVal, HRMaxVal not used
1646 49103256 : auto &periodDY = meter->periods[(int)ReportFreq::Day];
1647 49103256 : if (TSValueComp <= periodDY.MaxVal) {
1648 47336382 : continue;
1649 : }
1650 1766874 : periodDY.MaxVal = TSValue;
1651 1766874 : periodDY.MaxValDate = TimeStamp;
1652 :
1653 1766874 : auto &periodMN = meter->periods[(int)ReportFreq::Month];
1654 1766874 : if (TSValueComp <= periodMN.MaxVal) {
1655 136 : continue;
1656 : }
1657 1766738 : periodMN.MaxVal = TSValue;
1658 1766738 : periodMN.MaxValDate = TimeStamp;
1659 :
1660 1766738 : auto &periodYR = meter->periods[(int)ReportFreq::Year];
1661 1766738 : if (TSValueComp > periodYR.MaxVal) {
1662 1766003 : periodYR.MaxVal = TSValue;
1663 1766003 : periodYR.MaxValDate = TimeStamp;
1664 : }
1665 :
1666 1766738 : auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
1667 1766738 : if (TSValueComp > periodSM.MaxVal) {
1668 1766591 : periodSM.MaxVal = TSValue;
1669 1766591 : periodSM.MaxValDate = TimeStamp;
1670 : }
1671 :
1672 1766738 : if (TSValueComp > meter->periodFinYrSM.MaxVal) {
1673 1766003 : meter->periodFinYrSM.MaxVal = TSValue;
1674 1766003 : meter->periodFinYrSM.MaxValDate = TimeStamp;
1675 : }
1676 487704 : } // for (meter)
1677 :
1678 : // Set Min
1679 49590960 : for (auto *meter : op->meters) {
1680 49103256 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1681 49103256 : Real64 TSValueComp = TSValue; // + 0.00001;
1682 :
1683 49103256 : auto &periodDY = meter->periods[(int)ReportFreq::Day];
1684 49103256 : if (TSValueComp >= periodDY.MinVal) {
1685 48442900 : continue;
1686 : }
1687 :
1688 660356 : periodDY.MinVal = TSValue;
1689 660356 : periodDY.MinValDate = TimeStamp;
1690 :
1691 660356 : auto &periodMN = meter->periods[(int)ReportFreq::Month];
1692 660356 : if (TSValueComp >= periodMN.MinVal) {
1693 36 : continue;
1694 : }
1695 :
1696 660320 : periodMN.MinVal = TSValue;
1697 660320 : periodMN.MinValDate = TimeStamp;
1698 :
1699 660320 : auto &periodYR = meter->periods[(int)ReportFreq::Year];
1700 660320 : if (TSValueComp < periodYR.MinVal) {
1701 660201 : periodYR.MinVal = TSValue;
1702 660201 : periodYR.MinValDate = TimeStamp;
1703 : }
1704 :
1705 660320 : auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
1706 660320 : if (TSValueComp < periodSM.MinVal) {
1707 660303 : periodSM.MinVal = TSValue;
1708 660303 : periodSM.MinValDate = TimeStamp;
1709 : }
1710 :
1711 660320 : if (TSValueComp < meter->periodFinYrSM.MinVal) {
1712 660201 : meter->periodFinYrSM.MinVal = TSValue;
1713 660201 : meter->periodFinYrSM.MinValDate = TimeStamp;
1714 : }
1715 487704 : } // for (meter)
1716 :
1717 49590960 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
1718 49103256 : 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 491160 : 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 491160 : auto &op = state.dataOutputProcessor;
1771 491160 : auto &rf = state.dataResultsFramework->resultsFramework;
1772 491160 : auto &rfMetersTS = rf->Meters[(int)ReportFreq::TimeStep];
1773 :
1774 491160 : if (!rfMetersTS.dataFrameEnabled()) {
1775 485030 : rf->initializeMeters(op->meters, ReportFreq::TimeStep);
1776 : }
1777 :
1778 491160 : PrintTimeStamp = true;
1779 49781040 : for (int Loop = 0; Loop < (int)op->meters.size(); ++Loop) {
1780 49289880 : auto *meter = op->meters[Loop];
1781 49289880 : auto &periodTS = meter->periods[(int)ReportFreq::TimeStep];
1782 49289880 : meter->CurTSValue = periodTS.Value;
1783 49289880 : if (!periodTS.Rpt && !periodTS.accRpt) {
1784 49263480 : continue;
1785 : }
1786 26400 : if (PrintTimeStamp) {
1787 6144 : CurDayType = state.dataEnvrn->DayOfWeek;
1788 6144 : if (state.dataEnvrn->HolidayIndex > 0) {
1789 6144 : CurDayType = state.dataEnvrn->HolidayIndex;
1790 : }
1791 36864 : WriteTimeStampFormatData(state,
1792 6144 : state.files.mtr,
1793 : ReportFreq::EachCall,
1794 6144 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
1795 6144 : state.dataGlobal->DayOfSimChr,
1796 6144 : PrintTimeStamp && PrintTimeStampToSQL,
1797 6144 : state.dataEnvrn->Month,
1798 6144 : state.dataEnvrn->DayOfMonth,
1799 6144 : state.dataGlobal->HourOfDay,
1800 : EndMinute,
1801 : StartMinute,
1802 6144 : state.dataEnvrn->DSTIndicator,
1803 6144 : Sched::dayTypeNames[CurDayType]);
1804 6144 : if (rfMetersTS.dataFrameEnabled()) {
1805 6144 : rfMetersTS.newRow(
1806 6144 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, EndMinute, state.dataGlobal->CalendarYear);
1807 : }
1808 6144 : PrintTimeStamp = false;
1809 6144 : PrintTimeStampToSQL = false;
1810 : }
1811 :
1812 26400 : if (PrintESOTimeStamp && !periodTS.RptFO && !periodTS.accRptFO) {
1813 0 : CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
1814 0 : WriteTimeStampFormatData(state,
1815 0 : state.files.eso,
1816 : ReportFreq::EachCall,
1817 0 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
1818 0 : state.dataGlobal->DayOfSimChr,
1819 0 : PrintTimeStamp && PrintESOTimeStamp && PrintTimeStampToSQL,
1820 0 : state.dataEnvrn->Month,
1821 0 : state.dataEnvrn->DayOfMonth,
1822 0 : state.dataGlobal->HourOfDay,
1823 : EndMinute,
1824 : StartMinute,
1825 0 : state.dataEnvrn->DSTIndicator,
1826 0 : Sched::dayTypeNames[CurDayType]);
1827 0 : PrintESOTimeStamp = false;
1828 : }
1829 :
1830 26400 : if (periodTS.Rpt) {
1831 26400 : periodTS.WriteReportData(state, ReportFreq::TimeStep);
1832 26400 : rfMetersTS.pushVariableValue(periodTS.RptNum, periodTS.Value);
1833 : }
1834 :
1835 26400 : 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 49781040 : for (auto *meter : op->meters) {
1842 49289880 : meter->periods[(int)ReportFreq::TimeStep].Value = 0.0;
1843 491160 : }
1844 491160 : } // ReportTSMeters()
1845 :
1846 110368 : 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 110368 : auto &op = state.dataOutputProcessor;
1864 110368 : auto &rf = state.dataResultsFramework->resultsFramework;
1865 110368 : auto &rfMeters = rf->Meters[(int)freq];
1866 :
1867 110368 : assert(freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
1868 : freq == ReportFreq::Simulation);
1869 :
1870 110368 : if (!rfMeters.dataFrameEnabled()) {
1871 101684 : rf->initializeMeters(op->meters, freq);
1872 : }
1873 :
1874 110368 : PrintTimeStamp = true;
1875 11570994 : for (auto *meter : op->meters) {
1876 11460626 : auto &period = meter->periods[(int)freq];
1877 :
1878 11460626 : if (freq == ReportFreq::Simulation) {
1879 205148 : meter->periodLastSM.Value = period.Value;
1880 205148 : meter->periodLastSM.MinVal = period.MinVal;
1881 205148 : meter->periodLastSM.MinValDate = period.MinValDate;
1882 205148 : meter->periodLastSM.MaxVal = period.MaxVal;
1883 205148 : meter->periodLastSM.MaxValDate = period.MaxValDate;
1884 : }
1885 :
1886 11460626 : if (!period.Rpt && !period.accRpt) {
1887 11407459 : continue;
1888 : }
1889 53167 : if (PrintTimeStamp) {
1890 9913 : CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
1891 :
1892 9913 : switch (freq) {
1893 :
1894 7512 : case ReportFreq::Hour: {
1895 45072 : WriteTimeStampFormatData(state,
1896 7512 : state.files.mtr,
1897 : freq,
1898 7512 : op->freqStampReportNums[(int)freq],
1899 7512 : state.dataGlobal->DayOfSimChr,
1900 7512 : PrintTimeStamp && PrintTimeStampToSQL,
1901 7512 : state.dataEnvrn->Month,
1902 7512 : state.dataEnvrn->DayOfMonth,
1903 7512 : state.dataGlobal->HourOfDay,
1904 : -1, // EndMinute
1905 : -1, // StartMinute
1906 7512 : state.dataEnvrn->DSTIndicator,
1907 7512 : Sched::dayTypeNames[CurDayType]);
1908 7512 : } break;
1909 :
1910 48 : case ReportFreq::Day: {
1911 240 : WriteTimeStampFormatData(state,
1912 48 : state.files.mtr,
1913 : freq,
1914 48 : op->freqStampReportNums[(int)freq],
1915 48 : state.dataGlobal->DayOfSimChr,
1916 48 : PrintTimeStamp && PrintTimeStampToSQL,
1917 48 : state.dataEnvrn->Month,
1918 48 : state.dataEnvrn->DayOfMonth,
1919 : -1, // Hour
1920 : -1, // EndMinute
1921 : -1, // StartMinute
1922 48 : state.dataEnvrn->DSTIndicator,
1923 48 : Sched::dayTypeNames[CurDayType]);
1924 48 : } break;
1925 :
1926 1268 : case ReportFreq::Month: {
1927 1268 : WriteTimeStampFormatData(state,
1928 1268 : state.files.mtr,
1929 : freq,
1930 1268 : op->freqStampReportNums[(int)freq],
1931 1268 : state.dataGlobal->DayOfSimChr,
1932 1268 : PrintTimeStamp && PrintTimeStampToSQL,
1933 1268 : state.dataEnvrn->Month);
1934 1268 : } break;
1935 :
1936 0 : case ReportFreq::Year: {
1937 0 : WriteYearlyTimeStamp(state,
1938 0 : state.files.mtr,
1939 0 : op->freqStampReportNums[(int)freq],
1940 0 : state.dataGlobal->CalendarYearChr,
1941 0 : PrintTimeStamp && PrintTimeStampToSQL);
1942 0 : } break;
1943 :
1944 1085 : case ReportFreq::Simulation: {
1945 1085 : WriteTimeStampFormatData(state,
1946 1085 : state.files.mtr,
1947 : freq,
1948 1085 : op->freqStampReportNums[(int)freq],
1949 1085 : state.dataGlobal->DayOfSimChr,
1950 1085 : PrintTimeStamp && PrintTimeStampToSQL);
1951 1085 : } break;
1952 :
1953 0 : default: {
1954 0 : } break;
1955 : } // switch (freq)
1956 :
1957 9913 : if (rfMeters.dataFrameEnabled()) {
1958 9913 : rfMeters.newRow(
1959 9913 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
1960 : }
1961 9913 : PrintTimeStamp = false;
1962 9913 : PrintTimeStampToSQL = false;
1963 : }
1964 :
1965 53167 : if (period.Rpt) {
1966 53167 : period.WriteReportData(state, freq);
1967 53167 : rfMeters.pushVariableValue(period.RptNum, period.Value);
1968 53167 : period.Value = 0.0;
1969 :
1970 53167 : if (freq != ReportFreq::Hour) {
1971 17527 : period.MinVal = MinSetValue;
1972 17527 : period.MaxVal = MaxSetValue;
1973 : }
1974 : }
1975 :
1976 53167 : if (period.accRpt) {
1977 10 : WriteCumulativeReportMeterData(state, period.accRptNum, meter->periods[(int)ReportFreq::Simulation].Value, period.accRptFO);
1978 10 : rfMeters.pushVariableValue(period.accRptNum, meter->periods[(int)ReportFreq::Simulation].Value);
1979 : }
1980 110368 : } // for (meter)
1981 110368 : } // ReportMeters()
1982 :
1983 781 : 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 781 : auto const &op = state.dataOutputProcessor;
2000 :
2001 94167 : for (auto *meter : op->meters) {
2002 93387 : auto &period = meter->periodFinYrSM;
2003 :
2004 93387 : switch (meter->RT_forIPUnits) {
2005 43474 : case RT_IPUnits::Electricity: {
2006 86948 : OutputReportPredefined::PreDefTableEntry(
2007 43474 : state, state.dataOutRptPredefined->pdchEMelecannual, meter->Name, period.Value * Constant::convertJtoGJ);
2008 86948 : OutputReportPredefined::PreDefTableEntry(
2009 43474 : state, state.dataOutRptPredefined->pdchEMelecminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2010 86948 : OutputReportPredefined::PreDefTableEntry(
2011 130422 : state, state.dataOutRptPredefined->pdchEMelecminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2012 86948 : OutputReportPredefined::PreDefTableEntry(
2013 43474 : state, state.dataOutRptPredefined->pdchEMelecmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2014 86948 : OutputReportPredefined::PreDefTableEntry(
2015 130422 : state, state.dataOutRptPredefined->pdchEMelecmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2016 43474 : } break;
2017 :
2018 2607 : case RT_IPUnits::Gas: {
2019 5214 : OutputReportPredefined::PreDefTableEntry(
2020 2607 : state, state.dataOutRptPredefined->pdchEMgasannual, meter->Name, period.Value * Constant::convertJtoGJ);
2021 5214 : OutputReportPredefined::PreDefTableEntry(
2022 2607 : state, state.dataOutRptPredefined->pdchEMgasminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2023 5214 : OutputReportPredefined::PreDefTableEntry(
2024 7821 : state, state.dataOutRptPredefined->pdchEMgasminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2025 5214 : OutputReportPredefined::PreDefTableEntry(
2026 2607 : state, state.dataOutRptPredefined->pdchEMgasmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2027 5214 : OutputReportPredefined::PreDefTableEntry(
2028 7821 : state, state.dataOutRptPredefined->pdchEMgasmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2029 2607 : } break;
2030 :
2031 1978 : case RT_IPUnits::Cooling: {
2032 3956 : OutputReportPredefined::PreDefTableEntry(
2033 1978 : state, state.dataOutRptPredefined->pdchEMcoolannual, meter->Name, period.Value * Constant::convertJtoGJ);
2034 3956 : OutputReportPredefined::PreDefTableEntry(
2035 1978 : state, state.dataOutRptPredefined->pdchEMcoolminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2036 3956 : OutputReportPredefined::PreDefTableEntry(
2037 5934 : state, state.dataOutRptPredefined->pdchEMcoolminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2038 3956 : OutputReportPredefined::PreDefTableEntry(
2039 1978 : state, state.dataOutRptPredefined->pdchEMcoolmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2040 3956 : OutputReportPredefined::PreDefTableEntry(
2041 5934 : state, state.dataOutRptPredefined->pdchEMcoolmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2042 1978 : } break;
2043 :
2044 3382 : case RT_IPUnits::Water: {
2045 3383 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMwaterannual, meter->Name, period.Value);
2046 6762 : OutputReportPredefined::PreDefTableEntry(
2047 3381 : state, state.dataOutRptPredefined->pdchEMwaterminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2048 6762 : OutputReportPredefined::PreDefTableEntry(
2049 10143 : state, state.dataOutRptPredefined->pdchEMwaterminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2050 6762 : OutputReportPredefined::PreDefTableEntry(
2051 3381 : state, state.dataOutRptPredefined->pdchEMwatermaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2052 6762 : OutputReportPredefined::PreDefTableEntry(
2053 10143 : state, state.dataOutRptPredefined->pdchEMwatermaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2054 3381 : } break;
2055 :
2056 5466 : case RT_IPUnits::OtherKG: {
2057 5466 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherKGannual, meter->Name, period.Value);
2058 5466 : OutputReportPredefined::PreDefTableEntry(
2059 5466 : state, state.dataOutRptPredefined->pdchEMotherKGminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2060 10932 : OutputReportPredefined::PreDefTableEntry(
2061 16398 : state, state.dataOutRptPredefined->pdchEMotherKGminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2062 5466 : OutputReportPredefined::PreDefTableEntry(
2063 5466 : state, state.dataOutRptPredefined->pdchEMotherKGmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2064 10932 : OutputReportPredefined::PreDefTableEntry(
2065 16398 : state, state.dataOutRptPredefined->pdchEMotherKGmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2066 5466 : } break;
2067 :
2068 279 : case RT_IPUnits::OtherM3: {
2069 279 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherM3annual, meter->Name, period.Value, 3);
2070 279 : OutputReportPredefined::PreDefTableEntry(
2071 279 : state, state.dataOutRptPredefined->pdchEMotherM3minvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2072 558 : OutputReportPredefined::PreDefTableEntry(
2073 837 : state, state.dataOutRptPredefined->pdchEMotherM3minvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2074 279 : OutputReportPredefined::PreDefTableEntry(
2075 279 : state, state.dataOutRptPredefined->pdchEMotherM3maxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2076 558 : OutputReportPredefined::PreDefTableEntry(
2077 837 : state, state.dataOutRptPredefined->pdchEMotherM3maxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2078 279 : } break;
2079 :
2080 279 : case RT_IPUnits::OtherL: {
2081 279 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherLannual, meter->Name, period.Value, 3);
2082 279 : OutputReportPredefined::PreDefTableEntry(
2083 279 : state, state.dataOutRptPredefined->pdchEMotherLminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2084 558 : OutputReportPredefined::PreDefTableEntry(
2085 837 : state, state.dataOutRptPredefined->pdchEMotherLminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2086 279 : OutputReportPredefined::PreDefTableEntry(
2087 279 : state, state.dataOutRptPredefined->pdchEMotherLmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2088 558 : OutputReportPredefined::PreDefTableEntry(
2089 837 : state, state.dataOutRptPredefined->pdchEMotherLmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2090 279 : } break;
2091 :
2092 35922 : default: {
2093 71844 : OutputReportPredefined::PreDefTableEntry(
2094 35922 : state, state.dataOutRptPredefined->pdchEMotherJannual, meter->Name, period.Value * Constant::convertJtoGJ);
2095 71844 : OutputReportPredefined::PreDefTableEntry(
2096 35922 : state, state.dataOutRptPredefined->pdchEMotherJminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2097 71844 : OutputReportPredefined::PreDefTableEntry(
2098 107766 : state, state.dataOutRptPredefined->pdchEMotherJminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2099 71844 : OutputReportPredefined::PreDefTableEntry(
2100 35922 : state, state.dataOutRptPredefined->pdchEMotherJmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2101 71844 : OutputReportPredefined::PreDefTableEntry(
2102 107766 : state, state.dataOutRptPredefined->pdchEMotherJmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2103 35922 : } break;
2104 : } // switch
2105 782 : } // for (meter)
2106 780 : } // ReportForTabularReports()
2107 :
2108 186772 : 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 186772 : if (codedDate == 0) {
2121 0 : 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 186772 : General::DecodeMonDayHrMin(codedDate, Month, Day, Hour, Minute);
2133 :
2134 186772 : if (Month < 1 || Month > 12) {
2135 0 : return "-";
2136 : }
2137 186772 : if (Day < 1 || Day > 31) {
2138 0 : return "-";
2139 : }
2140 186772 : if (Hour < 1 || Hour > 24) {
2141 0 : return "-";
2142 : }
2143 186772 : if (Minute < 0 || Minute > 60) {
2144 0 : return "-";
2145 : }
2146 :
2147 186772 : --Hour;
2148 186772 : if (Minute == 60) {
2149 23170 : ++Hour;
2150 23170 : Minute = 0;
2151 : }
2152 :
2153 186772 : std::string monthName;
2154 186772 : switch (Month) {
2155 92727 : case 1:
2156 92727 : monthName = "JAN";
2157 92727 : break;
2158 33 : case 2:
2159 33 : monthName = "FEB";
2160 33 : break;
2161 22 : case 3:
2162 22 : monthName = "MAR";
2163 22 : break;
2164 32 : case 4:
2165 32 : monthName = "APR";
2166 32 : break;
2167 14 : case 5:
2168 14 : monthName = "MAY";
2169 14 : break;
2170 43 : case 6:
2171 43 : monthName = "JUN";
2172 43 : break;
2173 76751 : case 7:
2174 76751 : monthName = "JUL";
2175 76751 : break;
2176 480 : case 8:
2177 480 : monthName = "AUG";
2178 480 : break;
2179 235 : case 9:
2180 235 : monthName = "SEP";
2181 235 : break;
2182 5 : case 10:
2183 5 : monthName = "OCT";
2184 5 : break;
2185 3 : case 11:
2186 3 : monthName = "NOV";
2187 3 : break;
2188 16427 : case 12:
2189 16427 : monthName = "DEC";
2190 16427 : break;
2191 0 : default:
2192 0 : assert(false);
2193 : }
2194 :
2195 186772 : return format(DateFmt, Day, monthName, Hour, Minute);
2196 186772 : }
2197 :
2198 349762 : std::string OutVar::multiplierString() const
2199 : {
2200 339671 : return (ZoneMult == 1 && ZoneListMult == 1)
2201 349762 : ? ""
2202 1367741 : : format(" * {} (Zone Multiplier = {}, Zone List Multiplier = {})", ZoneMult * ZoneListMult, ZoneMult, ZoneListMult);
2203 : }
2204 :
2205 782 : 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 782 : auto &op = state.dataOutputProcessor;
2218 :
2219 172763 : for (auto const *var : op->outVars) {
2220 :
2221 171981 : if (var->meterNums.empty()) {
2222 113769 : continue;
2223 : }
2224 :
2225 58212 : print(state.files.mtd,
2226 : "\n Meters for {},{} [{}]{}\n",
2227 58212 : var->ReportID,
2228 58212 : var->keyColonName,
2229 58212 : Constant::unitNames[(int)var->units],
2230 116424 : var->multiplierString());
2231 :
2232 349762 : for (int const meterNum : var->meterNums) {
2233 291550 : auto const *meter = op->meters[meterNum];
2234 :
2235 291550 : print(state.files.mtd,
2236 : " On{}Meter={} [{}]\n",
2237 583100 : (meter->type == MeterType::Normal) ? "" : "Custom",
2238 291550 : meter->Name,
2239 291550 : Constant::unitNames[(int)meter->units]);
2240 58212 : }
2241 782 : } // for (var)
2242 :
2243 94414 : for (auto const *meter : op->meters) {
2244 :
2245 93632 : print(state.files.mtd, "\n For Meter={} [{}]", meter->Name, Constant::unitNames[(int)meter->units]);
2246 93632 : if (meter->resource != Constant::eResource::Invalid) {
2247 93632 : print(state.files.mtd, ", ResourceType={}", Constant::eResourceNames[(int)meter->resource]);
2248 : }
2249 93632 : if (meter->endUseCat != EndUseCat::Invalid) {
2250 67178 : print(state.files.mtd, ", EndUse={}", endUseCatNames[(int)meter->endUseCat]);
2251 : }
2252 93632 : if (meter->group != Group::Invalid) {
2253 57035 : print(state.files.mtd, ", Group={}", groupNames[(int)meter->group]);
2254 : }
2255 93632 : print(state.files.mtd, ", contents are:\n");
2256 :
2257 93632 : if (meter->type == MeterType::Normal) {
2258 384215 : for (int srcVarNum : meter->srcVarNums) {
2259 290710 : auto const *var = op->outVars[srcVarNum];
2260 290710 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2261 93505 : }
2262 :
2263 127 : } else if (meter->type == MeterType::Custom) {
2264 751 : for (int srcVarNum : meter->srcVarNums) {
2265 661 : auto const *var = op->outVars[srcVarNum];
2266 661 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2267 90 : }
2268 :
2269 37 : } else if (meter->type == MeterType::CustomDec) {
2270 37 : print(state.files.mtd,
2271 : " Values for this meter will be Source Meter={}; but will be decremented by:\n",
2272 37 : op->meters[meter->decMeterNum]->Name);
2273 216 : for (int srcVarNum : meter->srcVarNums) {
2274 179 : auto const *var = op->outVars[srcVarNum];
2275 179 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2276 37 : }
2277 : }
2278 782 : } // for (meter)
2279 782 : } // ReportMeterDetails()
2280 :
2281 : // *****************************************************************************
2282 : // End of routines for Energy Meters implementation in EnergyPlus.
2283 : // *****************************************************************************
2284 :
2285 361188 : 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 361188 : std::string reportStr = (reportID == -1) ? "" : std::to_string(reportID);
2315 :
2316 361188 : assert(reportStr.length() + DayOfSimChr.length() + (DayType.length()) + 26 < N_WriteTimeStampFormatData); // Check will fit in stamp size
2317 :
2318 361188 : if (!outputFile.good()) {
2319 0 : return;
2320 : }
2321 :
2322 361188 : auto &sql = state.dataSQLiteProcedures->sqlite;
2323 :
2324 361188 : switch (reportingInterval) {
2325 310690 : case ReportFreq::EachCall:
2326 : case ReportFreq::TimeStep: {
2327 310690 : assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && StartMinute != -1 && EndMinute != -1 && DST != -1 && !DayType.empty());
2328 310690 : 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 310690 : if (writeToSQL && sql) {
2341 35916 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2342 : reportID,
2343 17958 : state.dataGlobal->DayOfSim,
2344 17958 : state.dataEnvrn->CurEnvirNum,
2345 17958 : state.dataGlobal->CalendarYear,
2346 17958 : state.dataEnvrn->CurrentYearIsLeapYear,
2347 : Month,
2348 : DayOfMonth,
2349 : Hour,
2350 : EndMinute,
2351 : StartMinute,
2352 : DST,
2353 : DayType,
2354 17958 : state.dataGlobal->WarmupFlag);
2355 : }
2356 310690 : } break;
2357 :
2358 47688 : case ReportFreq::Hour: {
2359 47688 : assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && DST != -1 && !DayType.empty());
2360 47688 : 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 47688 : 60.0,
2370 : DayType);
2371 47688 : if (writeToSQL && sql) {
2372 9456 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2373 : reportID,
2374 4728 : state.dataGlobal->DayOfSim,
2375 4728 : state.dataEnvrn->CurEnvirNum,
2376 4728 : state.dataGlobal->CalendarYear,
2377 4728 : state.dataEnvrn->CurrentYearIsLeapYear,
2378 : Month,
2379 : DayOfMonth,
2380 : Hour,
2381 : -1, // EndMinute
2382 : -1, // StartMinute
2383 : DST,
2384 : DayType,
2385 4728 : state.dataGlobal->WarmupFlag);
2386 : }
2387 47688 : } break;
2388 144 : case ReportFreq::Day: {
2389 144 : assert(Month != -1 && DayOfMonth != -1 && DST != -1 && !DayType.empty());
2390 144 : print<FormatSyntax::FMT>(outputFile, "{},{},{:2d},{:2d},{:2d},{}\n", reportStr, DayOfSimChr, Month, DayOfMonth, DST, DayType);
2391 144 : if (writeToSQL && sql) {
2392 34 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2393 : reportID,
2394 17 : state.dataGlobal->DayOfSim,
2395 17 : state.dataEnvrn->CurEnvirNum,
2396 17 : state.dataGlobal->CalendarYear,
2397 17 : state.dataEnvrn->CurrentYearIsLeapYear,
2398 : Month,
2399 : DayOfMonth,
2400 : -1, // Hour
2401 : -1, // EndMinute
2402 : -1, // StartMinute
2403 : DST,
2404 : DayType,
2405 17 : state.dataGlobal->WarmupFlag);
2406 : }
2407 144 : } break;
2408 :
2409 1493 : case ReportFreq::Month: {
2410 1493 : assert(Month != -1);
2411 1493 : print<FormatSyntax::FMT>(outputFile, "{},{},{:2d}\n", reportStr, DayOfSimChr, Month);
2412 1493 : if (writeToSQL && sql) {
2413 194 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2414 : reportID,
2415 97 : state.dataGlobal->DayOfSim,
2416 97 : state.dataEnvrn->CurEnvirNum,
2417 97 : state.dataGlobal->CalendarYear,
2418 97 : state.dataEnvrn->CurrentYearIsLeapYear,
2419 : Month);
2420 : }
2421 1493 : } break;
2422 :
2423 1173 : case ReportFreq::Simulation: {
2424 1173 : print<FormatSyntax::FMT>(outputFile, "{},{}\n", reportStr, DayOfSimChr);
2425 1173 : if (writeToSQL && sql) {
2426 16 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2427 : reportID,
2428 8 : state.dataGlobal->DayOfSim,
2429 8 : state.dataEnvrn->CurEnvirNum,
2430 8 : state.dataGlobal->CalendarYear,
2431 8 : state.dataEnvrn->CurrentYearIsLeapYear);
2432 : }
2433 1173 : } break;
2434 0 : default: {
2435 0 : if (sql) {
2436 0 : sql->sqliteWriteMessage(
2437 0 : format<FormatSyntax::FMT>("Illegal reportingInterval passed to WriteTimeStampFormatData: {}", (int)reportingInterval));
2438 : }
2439 0 : } break;
2440 : } // switch (reportFreq)
2441 361188 : } // WriteTimeStampFormatData()
2442 :
2443 0 : 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 0 : print(outputFile, "{},{}\n", reportID, yearOfSimChr);
2450 0 : auto &sql = state.dataSQLiteProcedures->sqlite;
2451 0 : if (writeToSQL && sql) {
2452 0 : sql->createYearlyTimeIndexRecord(state.dataGlobal->CalendarYear, state.dataEnvrn->CurEnvirNum);
2453 : }
2454 0 : } // WriteYearlyTimeStamp()
2455 :
2456 61305 : 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 61305 : auto &rf = state.dataResultsFramework->resultsFramework;
2471 61305 : auto &sql = state.dataSQLiteProcedures->sqlite;
2472 :
2473 10 : std::string_view unitsString = (units == Constant::Units::customEMS && !unitNameCustomEMS.empty())
2474 61305 : ? unitNameCustomEMS
2475 61305 : : ((units == Constant::Units::Invalid) ? "" : Constant::unitNames[(int)units]);
2476 :
2477 120448 : std::string schedString = (sched != nullptr) ? sched->Name : "";
2478 :
2479 61305 : if (state.files.eso.good()) {
2480 61305 : print(state.files.eso,
2481 : "{},{},{},{} [{}]{}{}{}\n",
2482 61305 : ReportID,
2483 61305 : reportFreqArbitraryInts[(int)freq],
2484 61305 : key,
2485 61305 : name,
2486 : unitsString,
2487 61305 : reportingFrequencyNoticeStrings[(int)freq],
2488 122610 : !schedString.empty() ? "," : "",
2489 : schedString);
2490 : }
2491 :
2492 61305 : if (freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
2493 23887 : freq == ReportFreq::Simulation) {
2494 37713 : state.dataOutputProcessor->freqTrackingVariables[(int)freq] = true;
2495 : }
2496 :
2497 61305 : if (sql) {
2498 13761 : 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 61305 : rf->addReportVariable(key, name, unitsString, freq);
2503 :
2504 61305 : } // OutVar::WriteReportDictionaryItem()
2505 :
2506 8179 : 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 8179 : auto &rf = state.dataResultsFramework->resultsFramework;
2532 8179 : auto &sql = state.dataSQLiteProcedures->sqlite;
2533 :
2534 8179 : std::string FreqString = std::string(reportingFrequencyNoticeStrings[(int)freq]);
2535 8179 : std::string FreqString2 = FreqString.substr(0, index(FreqString, '['));
2536 :
2537 8179 : const auto print_meter = [&](EnergyPlusData &state, const int frequency) {
2538 10486 : const auto out = [&](InputOutputFile &of) {
2539 10486 : if (of.good()) {
2540 10486 : if (cumulativeMeterFlag) {
2541 : static constexpr std::string_view fmt = "{},{},Cumulative {} [{}]{}\n";
2542 6 : print(of, fmt, reportID, 1, meterName, Constant::unitNames[(int)units], FreqString2);
2543 : } else {
2544 : static constexpr std::string_view fmt = "{},{},{} [{}]{}\n";
2545 10480 : print(of, fmt, reportID, frequency, meterName, Constant::unitNames[(int)units], FreqString);
2546 : }
2547 : }
2548 10486 : };
2549 :
2550 8179 : out(state.files.mtr);
2551 8179 : if (!meterFileOnlyFlag) {
2552 2307 : out(state.files.eso);
2553 : }
2554 8179 : };
2555 :
2556 8179 : print_meter(state, reportFreqArbitraryInts[(int)freq]);
2557 :
2558 : static constexpr std::string_view keyedValueStringCum("Cumulative ");
2559 : static constexpr std::string_view keyedValueStringNon;
2560 8179 : std::string_view const keyedValueString(cumulativeMeterFlag ? keyedValueStringCum : keyedValueStringNon);
2561 :
2562 8179 : if (sql) {
2563 3374 : sql->createSQLiteReportDictionaryRecord(
2564 1687 : 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 8179 : rf->addReportMeter(meterName, Constant::unitNames[(int)units], freq);
2569 :
2570 8179 : } // WriteMeterDictionaryItem()
2571 :
2572 1457125 : 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 1457125 : if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) {
2590 978 : return;
2591 : }
2592 :
2593 1456147 : if (!Report || freq != reportFreq || !Stored) {
2594 1421367 : return;
2595 : }
2596 :
2597 34780 : if (NumStored > 0.0) {
2598 34774 : writeReportData(state);
2599 34774 : ++state.dataGlobal->StdOutputRecordCount;
2600 : }
2601 :
2602 34780 : StoreValue = 0.0;
2603 34780 : NumStored = 0.0;
2604 34780 : MinValue = MinSetValue;
2605 34780 : MaxValue = MaxSetValue;
2606 34780 : Stored = false;
2607 : } // OutVar::WriteOutput()
2608 :
2609 10 : 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 10 : std::string NumberOut; // Character for producing "number out"
2627 10 : auto &sql = state.dataSQLiteProcedures->sqlite;
2628 :
2629 10 : if (repValue == 0.0) {
2630 0 : NumberOut = "0.0";
2631 : } else {
2632 : char meterData[129];
2633 10 : dtoa(repValue, meterData);
2634 20 : NumberOut = std::string(meterData);
2635 : }
2636 :
2637 10 : if (sql) {
2638 4 : sql->createSQLiteReportDataRecord(reportID, repValue);
2639 : }
2640 :
2641 10 : if (state.files.mtr.good()) {
2642 10 : print(state.files.mtr, "{},{}\n", reportID, NumberOut);
2643 : }
2644 10 : ++state.dataGlobal->StdMeterRecordCount;
2645 :
2646 10 : 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 10 : } // WriteCumulativeReportMeterData()
2653 :
2654 79567 : 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 79567 : auto &sql = state.dataSQLiteProcedures->sqlite;
2666 :
2667 79567 : std::string NumberOut;
2668 :
2669 79567 : if (Value == 0.0) {
2670 27830 : NumberOut = "0.0";
2671 : } else {
2672 : char tmp[128];
2673 51737 : dtoa(Value, tmp);
2674 103474 : NumberOut = std::string(tmp);
2675 : }
2676 :
2677 79567 : if (sql) {
2678 38820 : sql->createSQLiteReportDataRecord(RptNum, Value, freq, MinVal, MinValDate, MaxVal, MaxValDate, state.dataGlobal->MinutesInTimeStep);
2679 : }
2680 :
2681 79567 : if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
2682 62040 : if (state.files.mtr.good()) {
2683 62040 : print(state.files.mtr, "{},{}\n", RptNum, NumberOut);
2684 : }
2685 62040 : ++state.dataGlobal->StdMeterRecordCount;
2686 114000 : if (state.files.eso.good() && !RptFO) {
2687 51960 : print(state.files.eso, "{},{}\n", RptNum, NumberOut);
2688 51960 : ++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 17527 : dtoa(MinVal, minValString);
2695 17527 : dtoa(MaxVal, maxValString);
2696 :
2697 17527 : std::string minDateString = produceDateString(MinValDate, freq);
2698 17527 : std::string maxDateString = produceDateString(MaxValDate, freq);
2699 :
2700 17527 : if (state.files.mtr.good()) {
2701 17527 : print(state.files.mtr, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
2702 : }
2703 :
2704 17527 : ++state.dataGlobal->StdMeterRecordCount;
2705 17527 : if (state.files.eso.good() && !RptFO) {
2706 4166 : print(state.files.eso, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
2707 4166 : ++state.dataGlobal->StdOutputRecordCount;
2708 : }
2709 17527 : }
2710 79567 : } // MeterPeriod::WriteReportData()
2711 :
2712 16584326 : 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 16584326 : auto &sql = state.dataSQLiteProcedures->sqlite;
2732 :
2733 16584326 : if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) {
2734 70848 : return;
2735 : }
2736 :
2737 16513478 : if (sql) {
2738 2070000 : sql->createSQLiteReportDataRecord(reportID, repValue);
2739 : }
2740 :
2741 16513478 : if (state.files.eso.good()) {
2742 : char numericData[129];
2743 16513478 : dtoa(repValue, numericData);
2744 16513478 : print<FormatSyntax::FMT>(state.files.eso, "{},{}\n", reportID, numericData);
2745 : }
2746 : } // WriteNumericData()
2747 :
2748 0 : 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 0 : auto &sql = state.dataSQLiteProcedures->sqlite;
2770 :
2771 0 : if (sql) {
2772 0 : sql->createSQLiteReportDataRecord(reportID, repValue);
2773 : }
2774 :
2775 0 : if (state.files.eso.good()) {
2776 0 : print<FormatSyntax::FMT>(state.files.eso, "{},{}\n", reportID, fmt::format_int(repValue).c_str());
2777 : }
2778 0 : } // WriteNumericData()
2779 :
2780 34774 : 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 34774 : auto &rf = state.dataResultsFramework->resultsFramework;
2794 34774 : auto &sql = state.dataSQLiteProcedures->sqlite;
2795 :
2796 34774 : Real64 repVal = (storeType == StoreType::Average) ? (StoreValue / NumStored) : StoreValue;
2797 :
2798 : // Append the min and max strings with date information
2799 34793 : if (rf->timeSeriesEnabled() &&
2800 19 : (freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year || freq == ReportFreq::Simulation)) {
2801 : // add to daily TS data store
2802 19 : rf->freqTSData[(int)freq].pushVariableValue(ReportID, repVal);
2803 : }
2804 :
2805 34774 : if (sql) {
2806 1704 : sql->createSQLiteReportDataRecord(ReportID, repVal, freq, MinValue, minValueDate, MaxValue, maxValueDate);
2807 : }
2808 :
2809 34774 : if (state.files.eso.good()) {
2810 34774 : std::string NumberOut;
2811 34774 : if (varType == VariableType::Real) {
2812 : char tmp[128];
2813 34366 : dtoa(repVal, tmp);
2814 68732 : 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 560 : NumberOut = (repVal == 0.0) ? "0.0" : format("{:f}", repVal);
2820 : }
2821 :
2822 34774 : if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
2823 0 : print(state.files.eso, "{},{}\n", ReportID, NumberOut);
2824 : } else {
2825 : char minValString[128], maxValString[128];
2826 34774 : dtoa(MinValue, minValString);
2827 34774 : dtoa(MaxValue, maxValString);
2828 :
2829 34774 : std::string minDateString = produceDateString(minValueDate, freq);
2830 34774 : std::string maxDateString = produceDateString(maxValueDate, freq);
2831 :
2832 34774 : print(state.files.eso, "{},{},{},{},{},{}\n", ReportID, NumberOut, minValString, minDateString, maxValString, maxDateString);
2833 34774 : }
2834 34774 : }
2835 34774 : } // OutVar::WriteReportData()
2836 :
2837 0 : 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 0 : if (has(meterName, "Electricity:Facility")) {
2851 0 : return 100;
2852 0 : } else if (has(meterName, "NaturalGas:Facility")) {
2853 0 : return 101;
2854 0 : } else if (has(meterName, "DistricHeatingWater:Facility")) {
2855 0 : return 102;
2856 0 : } else if (has(meterName, "DistricCooling:Facility")) {
2857 0 : return 103;
2858 0 : } else if (has(meterName, "ElectricityNet:Facility")) {
2859 0 : return 104;
2860 :
2861 : // Building indices are in the 200s
2862 0 : } else if (has(meterName, "Electricity:Building")) {
2863 0 : return 201;
2864 0 : } else if (has(meterName, "NaturalGas:Building")) {
2865 0 : return 202;
2866 :
2867 : // HVAC indices are in the 300s
2868 0 : } else if (has(meterName, "Electricity:HVAC")) {
2869 0 : return 301;
2870 :
2871 : // InteriorLights:Electricity:Zone indices are in the 500s
2872 0 : } else if (has(meterName, "InteriorLights:Electricity:Zone")) {
2873 0 : return 501;
2874 :
2875 : // InteriorLights:Electricity indices are in the 400s
2876 0 : } else if (has(meterName, "InteriorLights:Electricity")) {
2877 0 : return 401;
2878 :
2879 : // Unknown items have negative indices
2880 : } else {
2881 0 : return -11;
2882 : }
2883 :
2884 : return -1;
2885 : } // DetermineIndexGroupKeyFromMeterName()
2886 :
2887 8243 : 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 8243 : std::string indexGroup;
2902 :
2903 8243 : if (meter->group != Group::Invalid) {
2904 3140 : indexGroup = groupNames[(int)meter->group];
2905 : } else {
2906 5103 : indexGroup = "Facility";
2907 : }
2908 :
2909 8243 : if (meter->resource != Constant::eResource::Invalid) {
2910 8243 : indexGroup += format(":{}", Constant::eResourceNames[(int)meter->resource]);
2911 : }
2912 :
2913 8243 : if (meter->endUseCat != EndUseCat::Invalid) {
2914 1611 : indexGroup += format(":{}", endUseCatNames[(int)meter->endUseCat]);
2915 : }
2916 :
2917 8243 : if (len(meter->EndUseSub) > 0) {
2918 68 : indexGroup += ":" + meter->EndUseSub;
2919 : }
2920 :
2921 8243 : return indexGroup;
2922 0 : } // DetermineIndexGroupFromMeterGroup()
2923 :
2924 5495 : 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 5495 : auto &op = state.dataOutputProcessor;
2946 :
2947 5495 : 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 5495 : } else if (varType == VariableType::Real) {
2952 5442 : OutVarReal *varReal = dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]);
2953 5442 : assert(varReal != nullptr);
2954 5442 : *varReal->Which = SetRealVal;
2955 53 : } else if (varType == VariableType::Meter) {
2956 53 : op->meters[keyVarIndex]->CurTSValue = SetRealVal;
2957 : }
2958 5495 : } // SetInternalVariableValue()
2959 :
2960 : // returns the unit string for a DDVariableTypes item and custom string when customEMS is used
2961 0 : 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 0 : DDOutVar *ddVar = state.dataOutputProcessor->ddOutVars[ddNum];
2966 0 : Constant::Units units = ddVar->units;
2967 0 : 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 6256541 : 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 6256541 : auto &op = state.dataOutputProcessor;
3012 :
3013 6256541 : if (!op->OutputInitialized) {
3014 803 : InitializeOutput(state);
3015 : }
3016 :
3017 6256541 : std::vector<int> reqVarNums;
3018 :
3019 : // Determine whether to Report or not
3020 6256541 : CheckReportVariable(state, name, key, reqVarNums);
3021 6256541 : if (reqVarNums.empty()) {
3022 6198508 : reqVarNums.push_back(-1);
3023 : }
3024 :
3025 : // Is this redundant with CheckReportVariable?
3026 6256541 : bool const ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
3027 :
3028 6195647 : bool OnMeter = (resource != Constant::eResource::Invalid) || (endUseCat != EndUseCat::Invalid) || (!EndUseSub.empty()) ||
3029 12452188 : (group != Group::Invalid) || (!zone.empty()) || (!spaceType.empty());
3030 :
3031 6256541 : 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 6256541 : int ddOutVarNum = AddDDOutVar(state, name, timeStep, store, VariableType::Real, units, customUnitName);
3038 6256541 : auto *ddOutVar = op->ddOutVars[ddOutVarNum];
3039 :
3040 6256541 : ++op->NumOfRVariable_Setup;
3041 :
3042 : // If we add any output variables here at all, the first one will be at this index
3043 6256541 : int firstAddedOutVarNum = (int)op->outVars.size();
3044 :
3045 6256541 : op->NumTotalRVariable += reqVarNums.size();
3046 :
3047 6256541 : if (!OnMeter && !ThisOneOnTheList) {
3048 6080667 : return;
3049 : }
3050 :
3051 175874 : if (store == StoreType::Sum) {
3052 98797 : ++op->NumOfRVariable_Sum;
3053 : }
3054 175874 : if (OnMeter) {
3055 60894 : ++op->NumOfRVariable_Meter;
3056 : }
3057 :
3058 353404 : for (int reqVarNum : reqVarNums) {
3059 :
3060 177530 : ++op->NumOfRVariable;
3061 :
3062 177530 : OutVarReal *var = new OutVarReal;
3063 177530 : op->outVars.push_back(var);
3064 :
3065 : // Link this keyed variable to the dictionary entry
3066 177530 : ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
3067 177530 : var->ddVarNum = ddOutVarNum;
3068 :
3069 177530 : var->varType = VariableType::Real;
3070 177530 : var->timeStepType = timeStep;
3071 177530 : var->storeType = store;
3072 177530 : var->name = name;
3073 177530 : var->nameUC = Util::makeUPPER(var->name);
3074 177530 : var->key = key;
3075 177530 : var->keyUC = Util::makeUPPER(key);
3076 177530 : var->keyColonName = fmt::format("{}:{}", key, name);
3077 177530 : var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
3078 177530 : var->units = units;
3079 177530 : if (units == Constant::Units::customEMS) {
3080 10 : var->unitNameCustomEMS = customUnitName;
3081 : }
3082 177530 : var->freq = freq;
3083 177530 : var->sched = nullptr;
3084 177530 : var->ReportID = ++op->ReportNumberCounter;
3085 177530 : var->Which = &ActualVariable;
3086 177530 : var->ZoneMult = ZoneMult;
3087 177530 : var->ZoneListMult = ZoneListMult;
3088 177530 : var->indexGroupKey = indexGroupKey;
3089 177530 : 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 177530 : if (OnMeter) {
3095 60894 : AttachMeters(state, units, resource, endUseCat, EndUseSub, group, zone, spaceType, firstAddedOutVarNum);
3096 60894 : 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 177530 : if (reqVarNum == -1) {
3101 117841 : continue;
3102 : }
3103 :
3104 59689 : var->Report = true;
3105 :
3106 : // freq != ReportFreq::Hour
3107 59689 : if (freq == ReportFreq::Hour) {
3108 59688 : var->freq = op->reqVars[reqVarNum]->freq;
3109 59688 : var->sched = op->reqVars[reqVarNum]->sched;
3110 : }
3111 :
3112 59689 : var->writeReportDictionaryItem(state);
3113 175874 : }
3114 :
3115 6256541 : } // SetupOutputVariable()
3116 :
3117 272314 : 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 272314 : auto &op = state.dataOutputProcessor;
3147 :
3148 272314 : if (!op->OutputInitialized) {
3149 0 : InitializeOutput(state);
3150 : }
3151 :
3152 : // Determine whether to Report or not
3153 272314 : std::vector<int> reqVarNums;
3154 272314 : CheckReportVariable(state, name, key, reqVarNums);
3155 272314 : if (reqVarNums.empty()) {
3156 270718 : reqVarNums.push_back(-1);
3157 : }
3158 :
3159 : // DataOutputs::OutputVariablesForSimulation is case-insentitive
3160 272314 : int ddOutVarNum = AddDDOutVar(state, name, timeStepType, storeType, VariableType::Integer, units);
3161 272314 : auto *ddOutVar = op->ddOutVars[ddOutVarNum];
3162 :
3163 272314 : ++op->NumOfIVariable_Setup;
3164 :
3165 272314 : op->NumTotalIVariable += (!reqVarNums.empty()) ? reqVarNums.size() : 1;
3166 272314 : bool ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
3167 272314 : if (!ThisOneOnTheList) {
3168 268484 : return;
3169 : }
3170 :
3171 3830 : if (storeType == StoreType::Sum) {
3172 145 : ++op->NumOfIVariable_Sum;
3173 : }
3174 :
3175 7680 : for (int reqVarNum : reqVarNums) {
3176 :
3177 3850 : ++op->NumOfIVariable;
3178 :
3179 3850 : OutVarInt *var = new OutVarInt;
3180 3850 : op->outVars.push_back(var);
3181 : // Add to ddVar key list
3182 3850 : ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
3183 :
3184 3850 : var->varType = VariableType::Integer;
3185 3850 : var->timeStepType = timeStepType;
3186 3850 : var->storeType = storeType;
3187 3850 : var->name = name;
3188 3850 : var->nameUC = Util::makeUPPER(var->name);
3189 3850 : var->key = key;
3190 3850 : var->keyUC = Util::makeUPPER(key);
3191 3850 : var->keyColonName = fmt::format("{}:{}", key, name);
3192 3850 : var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
3193 3850 : var->units = units;
3194 3850 : var->ReportID = ++op->ReportNumberCounter;
3195 3850 : var->Which = &ActualVariable;
3196 3850 : var->indexGroupKey = -1;
3197 :
3198 3850 : if (reqVarNum == -1) {
3199 2234 : continue;
3200 : }
3201 :
3202 1616 : var->Report = true;
3203 :
3204 1616 : if (freq != ReportFreq::Hour) {
3205 0 : var->freq = freq;
3206 0 : var->sched = nullptr;
3207 : } else {
3208 1616 : var->freq = op->reqVars[reqVarNum]->freq;
3209 1616 : var->sched = op->reqVars[reqVarNum]->sched;
3210 : }
3211 :
3212 1616 : var->writeReportDictionaryItem(state);
3213 3830 : }
3214 272314 : } // SetOutputVariable()
3215 :
3216 1111755 : 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 1111755 : bool TimePrint(true); // True if the time needs to be printed
3236 1111755 : bool EndTimeStepFlag(false); // True when it's the end of the Zone Time Step
3237 1111755 : auto &op = state.dataOutputProcessor;
3238 1111755 : auto &rf = state.dataResultsFramework->resultsFramework;
3239 :
3240 1111755 : 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 1111755 : Real64 StartMinute = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // StartMinute for UpdateData call
3246 1111755 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute += (*op->TimeValue[(int)t_TimeStepTypeKey].TimeStep) * 60.0;
3247 1732350 : if (t_TimeStepTypeKey == TimeStepType::System &&
3248 620595 : (op->TimeValue[(int)TimeStepType::System].CurMinute == op->TimeValue[(int)TimeStepType::Zone].CurMinute)) {
3249 0 : EndTimeStepFlag = true;
3250 1111755 : } else if (t_TimeStepTypeKey == TimeStepType::Zone) {
3251 491160 : EndTimeStepFlag = true;
3252 : } else {
3253 620595 : EndTimeStepFlag = false;
3254 : }
3255 1111755 : Real64 MinuteNow = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // What minute it is now
3256 :
3257 : int MDHM; // Month,Day,Hour,Minute
3258 1111755 : EncodeMonDayHrMin(MDHM, state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, int(MinuteNow));
3259 1111755 : TimePrint = true;
3260 :
3261 1111755 : Real64 rxTime = (MinuteNow - StartMinute) /
3262 1111755 : double(state.dataGlobal->MinutesInTimeStep); // (MinuteNow-StartMinute)/REAL(MinutesPerTimeStep,r64) - for execution time
3263 :
3264 1111755 : if (rf->timeSeriesEnabled()) {
3265 : // R and I data frames for TimeStepType::TimeStepZone
3266 23657 : if (!rf->detailedTSData[(int)t_TimeStepTypeKey].variablesScanned()) {
3267 24 : rf->initializeTSDataFrame(ReportFreq::EachCall, op->outVars, t_TimeStepTypeKey);
3268 : }
3269 : }
3270 :
3271 1111755 : if (rf->timeSeriesEnabled()) {
3272 47314 : rf->detailedTSData[(int)t_TimeStepTypeKey].newRow(state.dataEnvrn->Month,
3273 23657 : state.dataEnvrn->DayOfMonth,
3274 23657 : state.dataGlobal->HourOfDay,
3275 23657 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
3276 23657 : state.dataGlobal->CalendarYear);
3277 : }
3278 :
3279 : // Main "Record Keeping" Loops for R and I variables
3280 175745690 : for (auto *var : op->outVars) {
3281 174633935 : if (var->timeStepType != t_TimeStepTypeKey) {
3282 82879950 : continue;
3283 : }
3284 :
3285 91753985 : Real64 value = (var->varType == VariableType::Real) ? *(dynamic_cast<OutVarReal *>(var))->Which : *(dynamic_cast<OutVarInt *>(var))->Which;
3286 :
3287 91753985 : var->Stored = true;
3288 :
3289 91753985 : if (var->storeType == StoreType::Average) {
3290 45308473 : Real64 CurVal = value * rxTime;
3291 : // TODO: Is this correct? Integer logic is different
3292 45308473 : if (var->varType == VariableType::Real) {
3293 43759585 : if (value > var->MaxValue) {
3294 2475569 : var->MaxValue = value;
3295 2475569 : var->maxValueDate = MDHM;
3296 : }
3297 43759585 : if (value < var->MinValue) {
3298 1655798 : var->MinValue = value;
3299 1655798 : var->minValueDate = MDHM;
3300 : }
3301 : } else { // var->varType == VariableType::Integer
3302 1548888 : if (CurVal > var->MaxValue) {
3303 4862 : var->MaxValue = CurVal;
3304 4862 : var->maxValueDate = MDHM;
3305 : }
3306 1548888 : if (CurVal < var->MinValue) {
3307 4400 : var->MinValue = CurVal;
3308 4400 : var->minValueDate = MDHM;
3309 : }
3310 : }
3311 45308473 : var->TSValue += CurVal;
3312 45308473 : var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
3313 : } else {
3314 46445512 : if (value > var->MaxValue) {
3315 1362302 : var->MaxValue = value;
3316 1362302 : var->maxValueDate = MDHM;
3317 : }
3318 46445512 : if (value < var->MinValue) {
3319 173208 : var->MinValue = value;
3320 173208 : var->minValueDate = MDHM;
3321 : }
3322 46445512 : var->TSValue += value;
3323 46445512 : var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
3324 : }
3325 :
3326 : // End of "record keeping" Report if applicable
3327 91753985 : if (!var->Report) {
3328 46975803 : continue;
3329 : }
3330 :
3331 44778182 : if (var->sched != nullptr && var->sched->getCurrentVal() == 0.0) {
3332 144004 : continue;
3333 : }
3334 :
3335 44634178 : var->tsStored = true;
3336 44634178 : if (!var->thisTSStored) {
3337 34705520 : ++var->thisTSCount;
3338 34705520 : var->thisTSStored = true;
3339 : }
3340 :
3341 44634178 : if (var->freq != ReportFreq::EachCall) {
3342 44008240 : continue;
3343 : }
3344 :
3345 625938 : if (TimePrint) {
3346 25626 : if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
3347 2854 : std::abs(op->LEndMin - op->TimeValue[(int)t_TimeStepTypeKey].CurMinute) > 0.001) {
3348 19918 : int CurDayType = state.dataEnvrn->DayOfWeek;
3349 19918 : if (state.dataEnvrn->HolidayIndex > 0) {
3350 19918 : CurDayType = state.dataEnvrn->HolidayIndex;
3351 : }
3352 119508 : WriteTimeStampFormatData(state,
3353 19918 : state.files.eso,
3354 : ReportFreq::EachCall,
3355 19918 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3356 19918 : state.dataGlobal->DayOfSimChr,
3357 : true,
3358 19918 : state.dataEnvrn->Month,
3359 19918 : state.dataEnvrn->DayOfMonth,
3360 19918 : state.dataGlobal->HourOfDay,
3361 19918 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
3362 : StartMinute,
3363 19918 : state.dataEnvrn->DSTIndicator,
3364 19918 : Sched::dayTypeNames[CurDayType]);
3365 19918 : op->LHourP = state.dataGlobal->HourOfDay;
3366 19918 : op->LStartMin = StartMinute;
3367 19918 : op->LEndMin = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute;
3368 : }
3369 22772 : TimePrint = false;
3370 : }
3371 :
3372 625938 : WriteNumericData(state, var->ReportID, value);
3373 625938 : ++state.dataGlobal->StdOutputRecordCount;
3374 :
3375 625938 : if (rf->timeSeriesEnabled()) {
3376 12448 : rf->detailedTSData[(int)t_TimeStepTypeKey].pushVariableValue(var->ReportID, value);
3377 : }
3378 1111755 : } // for (var)
3379 :
3380 1111755 : if (t_TimeStepTypeKey == TimeStepType::System) {
3381 1107477 : 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 491160 : if (EndTimeStepFlag) {
3387 491160 : if (rf->timeSeriesEnabled()) {
3388 11112 : if (!rf->freqTSData[(int)ReportFreq::TimeStep].variablesScanned()) {
3389 11112 : rf->initializeTSDataFrame(ReportFreq::TimeStep, op->outVars);
3390 : }
3391 22224 : rf->freqTSData[(int)ReportFreq::TimeStep].newRow(state.dataEnvrn->Month,
3392 11112 : state.dataEnvrn->DayOfMonth,
3393 11112 : state.dataGlobal->HourOfDay,
3394 11112 : op->TimeValue[(int)TimeStepType::Zone].CurMinute,
3395 11112 : state.dataGlobal->CalendarYear);
3396 : }
3397 :
3398 : // Update meters on the TimeStep (Zone)
3399 491160 : if (op->meterValues.capacity() > 0) {
3400 49781040 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
3401 49289880 : auto *meter = op->meters[iMeter];
3402 49289880 : if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
3403 187680144 : for (int srcVarNum : meter->srcVarNums) {
3404 138467160 : auto *var = op->outVars[srcVarNum];
3405 : // Separate the Zone variables from the HVAC variables using TimeStepType
3406 138467160 : 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 138467160 : op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
3411 49212984 : }
3412 49289880 : } else if (meter->type == MeterType::CustomDec) {
3413 76896 : auto *decMeter = op->meters[meter->decMeterNum];
3414 871584 : for (int srcVarNum : decMeter->srcVarNums) {
3415 794688 : auto *var = op->outVars[srcVarNum];
3416 794688 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3417 0 : continue;
3418 : }
3419 794688 : op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
3420 76896 : }
3421 460224 : for (int srcVarNum : meter->srcVarNums) {
3422 383328 : auto *var = op->outVars[srcVarNum];
3423 383328 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3424 0 : continue;
3425 : }
3426 383328 : op->meterValues[iMeter] -= var->TSValue * var->ZoneMult * var->ZoneListMult;
3427 76896 : }
3428 : } else {
3429 0 : assert(false);
3430 : }
3431 : } // for (iMeter)
3432 : } // if (op->meterValues.capacity() > 0)
3433 :
3434 72811032 : for (auto *var : op->outVars) {
3435 72319872 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3436 0 : continue;
3437 : }
3438 :
3439 72319872 : bool ReportNow = true;
3440 72319872 : if (var->sched != nullptr) {
3441 697248 : ReportNow = (var->sched->getCurrentVal() != 0.0); // SetReportNow(RVar%SchedPtr)
3442 : }
3443 72319872 : if (!ReportNow || !var->Report) {
3444 37614352 : var->TSValue = 0.0;
3445 : }
3446 : // IF (RVar%StoreType == AveragedVar) THEN
3447 : // RVar%Value=RVar%Value+RVar%TSValue/NumOfTimeStepInHour
3448 : // ELSE
3449 72319872 : var->Value += var->TSValue;
3450 : // ENDIF
3451 :
3452 72319872 : if (!ReportNow || !var->Report) {
3453 37614352 : continue;
3454 : }
3455 :
3456 34705520 : if (var->freq == ReportFreq::TimeStep) {
3457 13179504 : if (TimePrint) {
3458 285900 : if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
3459 636 : std::abs(op->LEndMin - op->TimeValue[(int)var->timeStepType].CurMinute) > 0.001) {
3460 284628 : int CurDayType = state.dataEnvrn->DayOfWeek;
3461 284628 : if (state.dataEnvrn->HolidayIndex > 0) {
3462 102036 : CurDayType = state.dataEnvrn->HolidayIndex;
3463 : }
3464 1707768 : WriteTimeStampFormatData(state,
3465 284628 : state.files.eso,
3466 : ReportFreq::EachCall,
3467 284628 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3468 284628 : state.dataGlobal->DayOfSimChr,
3469 : true,
3470 284628 : state.dataEnvrn->Month,
3471 284628 : state.dataEnvrn->DayOfMonth,
3472 284628 : state.dataGlobal->HourOfDay,
3473 284628 : op->TimeValue[(int)var->timeStepType].CurMinute,
3474 : StartMinute,
3475 284628 : state.dataEnvrn->DSTIndicator,
3476 284628 : Sched::dayTypeNames[CurDayType]);
3477 284628 : op->LHourP = state.dataGlobal->HourOfDay;
3478 284628 : op->LStartMin = StartMinute;
3479 284628 : op->LEndMin = op->TimeValue[(int)var->timeStepType].CurMinute;
3480 : }
3481 285264 : TimePrint = false;
3482 : } // if (TimePrint)
3483 :
3484 13179504 : WriteNumericData(state, var->ReportID, var->TSValue);
3485 13179504 : ++state.dataGlobal->StdOutputRecordCount;
3486 :
3487 13179504 : if (rf->timeSeriesEnabled()) {
3488 7488 : rf->freqTSData[(int)ReportFreq::TimeStep].pushVariableValue(var->ReportID, var->TSValue);
3489 : }
3490 : }
3491 34705520 : var->TSValue = 0.0;
3492 34705520 : var->thisTSStored = false;
3493 491160 : } // for (var)
3494 :
3495 491160 : UpdateMeters(state, MDHM);
3496 :
3497 491160 : ReportTSMeters(state, StartMinute, op->TimeValue[(int)TimeStepType::Zone].CurMinute, TimePrint, TimePrint);
3498 :
3499 : } // TimeStep Block
3500 :
3501 : // Hour Block
3502 491160 : if (state.dataGlobal->EndHourFlag) {
3503 102672 : if (op->freqTrackingVariables[(int)ReportFreq::Hour]) {
3504 40176 : int CurDayType = state.dataEnvrn->DayOfWeek;
3505 40176 : if (state.dataEnvrn->HolidayIndex > 0) {
3506 22488 : CurDayType = state.dataEnvrn->HolidayIndex;
3507 : }
3508 200880 : WriteTimeStampFormatData(state,
3509 40176 : state.files.eso,
3510 : ReportFreq::Hour,
3511 40176 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3512 40176 : state.dataGlobal->DayOfSimChr,
3513 : true,
3514 40176 : state.dataEnvrn->Month,
3515 40176 : state.dataEnvrn->DayOfMonth,
3516 40176 : state.dataGlobal->HourOfDay,
3517 : -1, // EndMinute
3518 : -1, // startMinute
3519 40176 : state.dataEnvrn->DSTIndicator,
3520 40176 : Sched::dayTypeNames[CurDayType]);
3521 40176 : TimePrint = false;
3522 : }
3523 :
3524 102672 : if (rf->timeSeriesEnabled()) {
3525 9288 : if (!rf->freqTSData[(int)ReportFreq::Hour].variablesScanned()) {
3526 9288 : rf->initializeTSDataFrame(ReportFreq::Hour, op->outVars);
3527 : }
3528 18576 : rf->freqTSData[(int)ReportFreq::Hour].newRow(
3529 9288 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3530 : }
3531 :
3532 102672 : op->TimeValue[(int)TimeStepType::Zone].CurMinute = 0.0;
3533 102672 : op->TimeValue[(int)TimeStepType::System].CurMinute = 0.0;
3534 :
3535 15416760 : for (auto *var : op->outVars) {
3536 :
3537 15314088 : 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 15314088 : if (var->tsStored) {
3547 6783134 : if (var->storeType == StoreType::Average) {
3548 6006830 : var->Value /= double(var->thisTSCount);
3549 : }
3550 6783134 : if (var->Report && var->freq == ReportFreq::Hour && var->Stored) {
3551 2778884 : WriteNumericData(state, var->ReportID, var->Value);
3552 2778884 : ++state.dataGlobal->StdOutputRecordCount;
3553 2778884 : var->Stored = false;
3554 : // add time series value for hourly to data store
3555 2778884 : if (rf->timeSeriesEnabled()) {
3556 20892 : rf->freqTSData[(int)ReportFreq::Hour].pushVariableValue(var->ReportID, var->Value);
3557 : }
3558 : }
3559 6783134 : var->StoreValue += var->Value;
3560 6783134 : ++var->NumStored;
3561 : }
3562 15314088 : var->tsStored = false;
3563 15314088 : var->thisTSStored = false;
3564 15314088 : var->thisTSCount = 0;
3565 15314088 : var->Value = 0.0;
3566 102672 : } // for (var)
3567 :
3568 102672 : ReportMeters(state, ReportFreq::Hour, TimePrint);
3569 : } // Hour Block
3570 :
3571 491160 : if (!state.dataGlobal->EndHourFlag) {
3572 388488 : return;
3573 : }
3574 :
3575 : // Day Block
3576 102672 : if (state.dataGlobal->EndDayFlag) {
3577 4278 : if (op->freqTrackingVariables[(int)ReportFreq::Day]) {
3578 96 : int CurDayType = state.dataEnvrn->DayOfWeek;
3579 96 : if (state.dataEnvrn->HolidayIndex > 0) {
3580 92 : CurDayType = state.dataEnvrn->HolidayIndex;
3581 : }
3582 384 : WriteTimeStampFormatData(state,
3583 96 : state.files.eso,
3584 : ReportFreq::Day,
3585 96 : op->freqStampReportNums[(int)ReportFreq::Day],
3586 96 : state.dataGlobal->DayOfSimChr,
3587 : true,
3588 96 : state.dataEnvrn->Month,
3589 96 : state.dataEnvrn->DayOfMonth,
3590 : -1, // Hour
3591 : -1, // EndMinute
3592 : -1, // StartMinute
3593 96 : state.dataEnvrn->DSTIndicator,
3594 96 : Sched::dayTypeNames[CurDayType]);
3595 96 : TimePrint = false;
3596 : }
3597 4278 : if (rf->timeSeriesEnabled()) {
3598 387 : if (!rf->freqTSData[(int)ReportFreq::Day].variablesScanned()) {
3599 387 : rf->initializeTSDataFrame(ReportFreq::Day, op->outVars);
3600 : }
3601 774 : rf->freqTSData[(int)ReportFreq::Day].newRow(
3602 387 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3603 : }
3604 :
3605 4278 : op->NumHoursInMonth += 24;
3606 642365 : for (auto *var : op->outVars) {
3607 638087 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3608 0 : continue;
3609 : }
3610 638087 : var->writeOutput(state, ReportFreq::Day);
3611 4278 : }
3612 :
3613 4278 : ReportMeters(state, ReportFreq::Day, TimePrint);
3614 :
3615 : } // Day Block
3616 :
3617 : // Only continue if EndDayFlag is set
3618 102672 : if (!state.dataGlobal->EndDayFlag) {
3619 98394 : return;
3620 : }
3621 :
3622 : // Month Block
3623 4278 : if (state.dataEnvrn->EndMonthFlag || state.dataGlobal->EndEnvrnFlag) {
3624 1745 : if (op->freqTrackingVariables[(int)ReportFreq::Month]) {
3625 225 : WriteTimeStampFormatData(state,
3626 225 : state.files.eso,
3627 : ReportFreq::Month,
3628 225 : op->freqStampReportNums[(int)ReportFreq::Month],
3629 225 : state.dataGlobal->DayOfSimChr,
3630 : true,
3631 225 : state.dataEnvrn->Month);
3632 225 : TimePrint = false;
3633 : }
3634 :
3635 1745 : if (rf->timeSeriesEnabled()) {
3636 34 : if (!rf->freqTSData[(int)ReportFreq::Month].variablesScanned()) {
3637 34 : rf->initializeTSDataFrame(ReportFreq::Month, op->outVars);
3638 : }
3639 68 : rf->freqTSData[(int)ReportFreq::Month].newRow(
3640 34 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3641 : }
3642 :
3643 1745 : op->NumHoursInSim += op->NumHoursInMonth;
3644 1745 : state.dataEnvrn->EndMonthFlag = false;
3645 414469 : for (auto *var : op->outVars) {
3646 412724 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3647 0 : continue;
3648 : }
3649 412724 : var->writeOutput(state, ReportFreq::Month);
3650 1745 : }
3651 :
3652 1745 : ReportMeters(state, ReportFreq::Month, TimePrint);
3653 :
3654 1745 : op->NumHoursInMonth = 0;
3655 : } // Month Block
3656 :
3657 : // Sim/Environment Block
3658 4278 : if (state.dataGlobal->EndEnvrnFlag) {
3659 1666 : if (op->freqTrackingVariables[(int)ReportFreq::Simulation]) {
3660 88 : WriteTimeStampFormatData(state,
3661 88 : state.files.eso,
3662 : ReportFreq::Simulation,
3663 88 : op->freqStampReportNums[(int)ReportFreq::Simulation],
3664 88 : state.dataGlobal->DayOfSimChr,
3665 : true);
3666 88 : TimePrint = false;
3667 : }
3668 :
3669 1666 : if (rf->timeSeriesEnabled()) {
3670 23 : if (!rf->freqTSData[(int)ReportFreq::Simulation].variablesScanned()) {
3671 23 : rf->initializeTSDataFrame(ReportFreq::Simulation, op->outVars);
3672 : }
3673 46 : rf->freqTSData[(int)ReportFreq::Simulation].newRow(
3674 23 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3675 : }
3676 :
3677 407352 : for (auto *var : op->outVars) {
3678 405686 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3679 0 : continue;
3680 : }
3681 405686 : var->writeOutput(state, ReportFreq::Simulation);
3682 1666 : }
3683 :
3684 1666 : ReportMeters(state, ReportFreq::Simulation, TimePrint);
3685 :
3686 1666 : op->NumHoursInSim = 0;
3687 : }
3688 :
3689 : // Yearly Block
3690 4278 : if (state.dataEnvrn->EndYearFlag) {
3691 7 : 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 7 : if (rf->timeSeriesEnabled()) {
3696 1 : if (!rf->freqTSData[(int)ReportFreq::Year].variablesScanned()) {
3697 1 : rf->initializeTSDataFrame(ReportFreq::Year, op->outVars);
3698 : }
3699 2 : rf->freqTSData[(int)ReportFreq::Year].newRow(
3700 1 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
3701 : }
3702 :
3703 635 : for (auto *var : op->outVars) {
3704 628 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3705 0 : continue;
3706 : }
3707 628 : var->writeOutput(state, ReportFreq::Year);
3708 7 : }
3709 :
3710 7 : ReportMeters(state, ReportFreq::Year, TimePrint);
3711 :
3712 7 : state.dataGlobal->CalendarYear += 1;
3713 7 : state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
3714 : }
3715 : } // UpdateDataandReport()
3716 :
3717 780 : 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 780 : 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 22214 : for (auto *reqVar : op->reqVars) {
3750 21434 : if (reqVar->Used) {
3751 21354 : continue;
3752 : }
3753 80 : if (reqVar->key.empty()) {
3754 67 : reqVar->key = "*";
3755 : }
3756 80 : 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 80 : if (!state.dataOutputProcessor->Rept) {
3764 40 : ShowWarningMessage(state, "The following Report Variables were requested but not generated -- check.rdd file");
3765 40 : ShowContinueError(state, "Either the IDF did not contain these elements, the variable name is misspelled,");
3766 40 : ShowContinueError(state,
3767 : "or the requested variable is an advanced output which requires Output : Diagnostics, DisplayAdvancedReportVariables;");
3768 20 : state.dataOutputProcessor->Rept = true;
3769 : }
3770 80 : ShowMessage(state, format("Key={}, VarName={}, Frequency={}", reqVar->key, reqVar->name, localReportFreqNames[(int)reqVar->freq]));
3771 780 : }
3772 780 : } // GenOutputVariablesAuditReport()
3773 :
3774 782 : 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 782 : Array1D_string Alphas(2);
3834 782 : Array1D<Real64> Numbers(1);
3835 : int NumAlpha;
3836 : int NumNumbers;
3837 : int IOStat;
3838 : int NumReqMeters;
3839 : int NumReqMeterFOs;
3840 :
3841 782 : bool ErrorsFound(false); // If errors detected in input
3842 782 : auto const &op = state.dataOutputProcessor;
3843 782 : auto &ipsc = state.dataIPShortCut;
3844 :
3845 782 : GetCustomMeterInput(state, ErrorsFound);
3846 782 : 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 6714 : [&state](std::string &name, ReportFreq freq, bool MeterFileOnlyIndicator, bool CumulativeIndicator) -> bool {
3853 6714 : bool result = false;
3854 :
3855 6714 : size_t varnameLen = index(name, '[');
3856 6714 : if (varnameLen != std::string::npos) {
3857 0 : name.erase(varnameLen);
3858 : }
3859 :
3860 6714 : auto const &op = state.dataOutputProcessor;
3861 :
3862 6714 : std::string::size_type wildCardPosition = index(name, '*');
3863 :
3864 6714 : if (wildCardPosition == std::string::npos) {
3865 6690 : if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
3866 6627 : SetInitialMeterReportingAndOutputNames(state, found->second, MeterFileOnlyIndicator, freq, CumulativeIndicator);
3867 6627 : result = true;
3868 6690 : }
3869 : } else { // Wildcard input
3870 24 : std::string nameSubstr = name.substr(0, wildCardPosition);
3871 2395 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
3872 2371 : if (Util::SameString(op->meters[iMeter]->Name.substr(0, wildCardPosition), nameSubstr)) {
3873 184 : SetInitialMeterReportingAndOutputNames(state, iMeter, MeterFileOnlyIndicator, freq, CumulativeIndicator);
3874 184 : result = true;
3875 : }
3876 : }
3877 24 : }
3878 :
3879 6714 : return result;
3880 782 : };
3881 :
3882 782 : ipsc->cCurrentModuleObject = "Output:Meter";
3883 782 : NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3884 :
3885 1573 : for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
3886 :
3887 2373 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3888 791 : ipsc->cCurrentModuleObject,
3889 : Loop,
3890 : Alphas,
3891 : NumAlpha,
3892 : Numbers,
3893 : NumNumbers,
3894 : IOStat,
3895 791 : ipsc->lNumericFieldBlanks,
3896 791 : ipsc->lAlphaFieldBlanks,
3897 791 : ipsc->cAlphaFieldNames,
3898 791 : ipsc->cNumericFieldNames);
3899 :
3900 791 : bool meterFileOnlyIndicator = false;
3901 791 : bool cumulativeIndicator = false;
3902 791 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3903 :
3904 791 : if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
3905 16 : ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
3906 : }
3907 : }
3908 :
3909 782 : ipsc->cCurrentModuleObject = "Output:Meter:MeterFileOnly";
3910 782 : NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3911 6700 : for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
3912 :
3913 17754 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3914 5918 : ipsc->cCurrentModuleObject,
3915 : Loop,
3916 : Alphas,
3917 : NumAlpha,
3918 : Numbers,
3919 : NumNumbers,
3920 : IOStat,
3921 5918 : ipsc->lNumericFieldBlanks,
3922 5918 : ipsc->lAlphaFieldBlanks,
3923 5918 : ipsc->cAlphaFieldNames,
3924 5918 : ipsc->cNumericFieldNames);
3925 :
3926 5918 : bool meterFileOnlyIndicator = true;
3927 5918 : bool cumulativeIndicator = false;
3928 5918 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3929 5918 : if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
3930 47 : ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
3931 : }
3932 : }
3933 :
3934 782 : ipsc->cCurrentModuleObject = "Output:Meter:Cumulative";
3935 782 : NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3936 :
3937 783 : for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
3938 :
3939 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3940 1 : ipsc->cCurrentModuleObject,
3941 : Loop,
3942 : Alphas,
3943 : NumAlpha,
3944 : Numbers,
3945 : NumNumbers,
3946 : IOStat,
3947 1 : ipsc->lNumericFieldBlanks,
3948 1 : ipsc->lAlphaFieldBlanks,
3949 1 : ipsc->cAlphaFieldNames,
3950 1 : ipsc->cNumericFieldNames);
3951 :
3952 1 : bool meterFileOnlyIndicator = false;
3953 1 : bool cumulativeIndicator = true;
3954 1 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3955 1 : 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 782 : ipsc->cCurrentModuleObject = "Output:Meter:Cumulative:MeterFileOnly";
3961 782 : NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3962 786 : for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
3963 :
3964 12 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3965 4 : ipsc->cCurrentModuleObject,
3966 : Loop,
3967 : Alphas,
3968 : NumAlpha,
3969 : Numbers,
3970 : NumNumbers,
3971 : IOStat,
3972 4 : ipsc->lNumericFieldBlanks,
3973 4 : ipsc->lAlphaFieldBlanks,
3974 4 : ipsc->cAlphaFieldNames,
3975 4 : ipsc->cNumericFieldNames);
3976 :
3977 4 : bool meterFileOnlyIndicator = true;
3978 4 : bool cumulativeIndicator = true;
3979 4 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3980 4 : 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 782 : ReportMeterDetails(state);
3986 :
3987 782 : if (op->ErrorsLogged) {
3988 0 : ShowFatalError(state, "UpdateMeterReporting: Previous Meter Specification errors cause program termination.");
3989 : }
3990 :
3991 782 : op->meterValues.resize(op->meters.size(), 0.0);
3992 782 : std::fill(op->meterValues.begin(), op->meterValues.end(), 0.0);
3993 782 : } // UpdateMeterReporting()
3994 :
3995 6811 : 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 6811 : auto &op = state.dataOutputProcessor;
4015 :
4016 6811 : auto *meter = op->meters[WhichMeter];
4017 6811 : auto &period = meter->periods[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq];
4018 6811 : if (!CumulativeIndicator) {
4019 6806 : if (MeterFileOnlyIndicator && period.Rpt) {
4020 6 : ShowWarningError(state,
4021 6 : format(R"(Output:Meter:MeterFileOnly requested for "{}" ({}), already on "Output:Meter". Will report to both {} and {})",
4022 3 : meter->Name,
4023 3 : reportFreqNames[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq],
4024 6 : state.files.eso.filePath.filename(),
4025 6 : state.files.mtr.filePath.filename()));
4026 : }
4027 6806 : if (!period.Rpt) {
4028 6801 : period.Rpt = true;
4029 6801 : if (MeterFileOnlyIndicator) {
4030 5868 : period.RptFO = true;
4031 : } else {
4032 933 : op->freqTrackingVariables[(int)freq] = true;
4033 : }
4034 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4035 6801 : meter->indexGroup = DetermineIndexGroupFromMeterGroup(meter);
4036 6801 : WriteMeterDictionaryItem(
4037 6801 : state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, MeterFileOnlyIndicator);
4038 : }
4039 : } else { // !CumulativeIndicator
4040 5 : if (MeterFileOnlyIndicator && period.accRpt) {
4041 0 : ShowWarningError(state,
4042 0 : format("Output:Meter:MeterFileOnly requested for \"Cumulative {}\" (TimeStep), already on \"Output:Meter\". "
4043 : "Will report to both {} and {}",
4044 0 : meter->Name,
4045 0 : state.files.eso.filePath.filename(),
4046 0 : state.files.mtr.filePath.filename()));
4047 : }
4048 :
4049 5 : if (!period.accRpt) {
4050 5 : period.accRpt = true;
4051 5 : if (MeterFileOnlyIndicator) {
4052 4 : period.accRptFO = true;
4053 : }
4054 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4055 5 : meter->indexGroup = DetermineIndexGroupFromMeterGroup(op->meters[WhichMeter]);
4056 5 : WriteMeterDictionaryItem(
4057 5 : state, freq, StoreType::Sum, period.accRptNum, meter->indexGroup, meter->Name, meter->units, true, MeterFileOnlyIndicator);
4058 : }
4059 : } // if (CumulativeIndicator)
4060 6811 : } // SetInitialMeterReportingAndOutputNames()
4061 :
4062 358945 : 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 358945 : auto const &op = state.dataOutputProcessor;
4075 :
4076 358945 : auto found = op->meterMap.find(name);
4077 717890 : return (found != op->meterMap.end()) ? found->second : -1;
4078 358945 : } // 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 13921352 : 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 13921352 : return (MeterNumber != -1) ? state.dataOutputProcessor->meters[MeterNumber]->CurTSValue : 0.0;
4104 : } // GetCurrentMeterValue()
4105 :
4106 272356035 : 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 272356035 : Real64 InstantMeterValue = 0.0;
4124 :
4125 272356035 : if (meterNum == -1) {
4126 207267915 : return InstantMeterValue;
4127 : }
4128 :
4129 65088120 : auto const &op = state.dataOutputProcessor;
4130 65088120 : auto *meter = op->meters[meterNum];
4131 :
4132 65088120 : if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
4133 1838691423 : for (int srcVarNum : meter->srcVarNums) {
4134 1773709505 : auto *var = op->outVars[srcVarNum];
4135 : // Separate the Zone variables from the HVAC variables using TimeStepType
4136 1773709505 : if (var->timeStepType != timeStepType) {
4137 884907367 : continue;
4138 : }
4139 :
4140 888802138 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4141 888802138 : assert(rVar != nullptr);
4142 : // Add to the total all of the appropriate variables
4143 888802138 : InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4144 64981918 : }
4145 :
4146 65088120 : } else if (meter->type == MeterType::CustomDec) {
4147 106202 : auto *decMeter = op->meters[meter->decMeterNum];
4148 3189052 : for (int srcVarNum : decMeter->srcVarNums) {
4149 3082850 : auto *var = op->outVars[srcVarNum];
4150 3082850 : if (var->timeStepType != timeStepType) {
4151 1541425 : continue;
4152 : }
4153 1541425 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4154 1541425 : assert(rVar != nullptr);
4155 1541425 : InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4156 106202 : }
4157 440980 : for (int srcVarNum : meter->srcVarNums) {
4158 334778 : auto *var = op->outVars[srcVarNum];
4159 334778 : if (var->timeStepType != timeStepType) {
4160 167389 : continue;
4161 : }
4162 167389 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4163 167389 : assert(rVar != nullptr);
4164 167389 : InstantMeterValue -= (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4165 106202 : }
4166 : } else {
4167 0 : assert(false);
4168 : }
4169 :
4170 65088120 : return InstantMeterValue;
4171 : } // GetInstantMeterValue()
4172 :
4173 110904178 : 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 110904178 : auto const &op = state.dataOutputProcessor;
4200 :
4201 : // Select based on variable type: integer, real, or meter
4202 110904178 : if (varType == VariableType::Invalid) { // Variable not a found variable
4203 12309868 : resultVal = 0.0;
4204 98594310 : } else if (varType == VariableType::Integer || varType == VariableType::Real) {
4205 97785904 : 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 195571808 : resultVal = (varType == VariableType::Integer) ? (double)*(dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]))->Which
4212 97785904 : : (double)*(dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]))->Which;
4213 808406 : } else if (varType == VariableType::Meter) {
4214 808406 : 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 110904178 : return resultVal;
4222 : } // GetInternalVariableValue()
4223 :
4224 360760 : 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 360760 : auto const &op = state.dataOutputProcessor;
4250 :
4251 : // Select based on variable type: integer, REAL(r64), or meter
4252 360760 : if (varType == VariableType::Invalid) { // Variable not a found variable
4253 0 : resultVal = 0.0;
4254 360760 : } else if (varType == VariableType::Integer || varType == VariableType::Real) {
4255 360760 : if (keyVarIndex < 0 || keyVarIndex >= (int)op->outVars.size()) {
4256 0 : ShowFatalError(state, "GetInternalVariableValueExternalInterface: passed index beyond range of array.");
4257 : }
4258 :
4259 360760 : 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 360760 : return resultVal;
4269 : } // GetInternalVariableValueExternalInterface()
4270 :
4271 33410 : 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 33410 : int NumVariables = 0;
4288 33410 : auto const &op = state.dataOutputProcessor;
4289 :
4290 39947632 : for (auto *var : op->outVars) {
4291 : // Pos=INDEX(RVariableTypes(Loop)%VarName,':')
4292 : // IF (ComponentName /= RVariableTypes(Loop)%VarNameUC(1:Pos-1)) CYCLE
4293 39914222 : if (var->varType != OutputProcessor::VariableType::Real) {
4294 779587 : continue;
4295 : }
4296 39134635 : if (ComponentName != var->keyUC) {
4297 39070062 : continue;
4298 : }
4299 64573 : if (!var->meterNums.empty()) {
4300 39446 : ++NumVariables;
4301 : }
4302 33410 : }
4303 33410 : return NumVariables;
4304 : } // GetNumMeteredVariables()
4305 :
4306 17331 : 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 17331 : auto &op = state.dataOutputProcessor;
4328 :
4329 17331 : NumVariables = 0;
4330 :
4331 23390232 : 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 23372901 : auto *var = op->outVars[iVar];
4336 23372901 : if (var->varType != VariableType::Real) {
4337 445170 : continue;
4338 : }
4339 22927731 : if (ComponentName != var->keyUC) {
4340 22865513 : continue;
4341 : }
4342 62218 : if (var->meterNums.empty()) {
4343 22772 : continue;
4344 : }
4345 :
4346 39446 : auto &meteredVar = meteredVars(++NumVariables);
4347 :
4348 39446 : meteredVar.num = iVar;
4349 39446 : meteredVar.varType = VariableType::Real;
4350 39446 : meteredVar.timeStepType = var->timeStepType;
4351 39446 : meteredVar.units = var->units;
4352 :
4353 39446 : meteredVar.resource = op->meters[var->meterNums[0]]->resource;
4354 39446 : meteredVar.name = var->keyColonNameUC;
4355 :
4356 39446 : bool foundEndUse = false;
4357 39446 : bool foundGroup = false;
4358 118338 : for (int meterNum : var->meterNums) {
4359 118315 : auto *meter = op->meters[meterNum];
4360 118315 : if (!foundEndUse && meter->endUseCat != EndUseCat::Invalid) {
4361 39423 : meteredVar.endUseCat = meter->endUseCat;
4362 39423 : foundEndUse = true;
4363 : }
4364 :
4365 118315 : if (!foundGroup && meter->group != Group::Invalid) {
4366 39446 : meteredVar.group = meter->group;
4367 39446 : foundGroup = true;
4368 : }
4369 :
4370 118315 : if (foundEndUse && foundGroup) {
4371 39423 : break;
4372 : }
4373 39446 : }
4374 :
4375 39446 : meteredVar.rptNum = var->ReportID;
4376 : }
4377 17331 : return NumVariables;
4378 : } // GetMeteredVariables()
4379 :
4380 57582 : 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 57582 : auto const &op = state.dataOutputProcessor;
4417 :
4418 57582 : varType = VariableType::Invalid;
4419 57582 : numKeys = 0;
4420 57582 : storeType = StoreType::Average;
4421 57582 : timeStepType = TimeStepType::Zone;
4422 57582 : units = Constant::Units::None; // Why is this None and not Invalid?
4423 :
4424 57582 : std::string nameUC = Util::makeUPPER(name);
4425 :
4426 : // Search Variable List First
4427 57582 : if (auto found = op->ddOutVarMap.find(nameUC); found != op->ddOutVarMap.end()) {
4428 1806 : auto const *ddOutVar = op->ddOutVars[found->second];
4429 1806 : varType = ddOutVar->variableType;
4430 1806 : storeType = ddOutVar->storeType;
4431 1806 : timeStepType = ddOutVar->timeStepType;
4432 1806 : units = ddOutVar->units;
4433 1806 : numKeys = ddOutVar->keyOutVarNums.size();
4434 :
4435 55776 : } 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 279 : int meterNum = found2->second;
4440 279 : numKeys = 1;
4441 279 : varType = VariableType::Meter;
4442 279 : units = op->meters[meterNum]->units;
4443 279 : storeType = StoreType::Sum;
4444 279 : 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 55497 : auto *sched = Sched::GetSchedule(state, nameUC);
4450 55497 : if (sched != nullptr) {
4451 0 : numKeys = 1;
4452 0 : varType = VariableType::Schedule;
4453 0 : if (sched->schedTypeNum != Sched::SchedNum_Invalid) {
4454 0 : std::string const &schedTypeName = state.dataSched->scheduleTypes[sched->schedTypeNum]->Name;
4455 0 : units = static_cast<Constant::Units>(getEnumValue(Constant::unitNamesUC, schedTypeName));
4456 : }
4457 0 : storeType = StoreType::Average;
4458 0 : timeStepType = TimeStepType::Zone;
4459 : }
4460 113358 : }
4461 57582 : } // GetVariableKeyCountandType()
4462 :
4463 2085 : 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 2085 : std::string nameUC = Util::makeUPPER(varName);
4494 :
4495 : // Select based on variable type: integer, real, or meter
4496 2085 : if (varType == VariableType::Integer || varType == VariableType::Real) {
4497 1806 : auto const &op = state.dataOutputProcessor;
4498 1806 : auto found = op->ddOutVarMap.find(nameUC);
4499 1806 : if (found == op->ddOutVarMap.end()) {
4500 0 : return;
4501 : }
4502 :
4503 1806 : auto const *ddOutVar = op->ddOutVars[found->second];
4504 :
4505 1806 : if (ddOutVar->keyOutVarNums.size() > size(keyOutVarNums)) {
4506 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4507 : }
4508 :
4509 1806 : int iKey = 0;
4510 147267 : for (int keyOutVarNum : ddOutVar->keyOutVarNums) {
4511 145461 : ++iKey;
4512 145461 : keyOutVarNums(iKey) = keyOutVarNum;
4513 145461 : keyNames(iKey) = op->outVars[keyOutVarNum]->keyUC;
4514 1806 : }
4515 :
4516 3891 : } else if (varType == VariableType::Meter) { // Meter
4517 :
4518 279 : if (size(keyOutVarNums) == 0) {
4519 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4520 : }
4521 279 : keyOutVarNums(1) = GetMeterIndex(state, varName);
4522 279 : keyNames(1) = "Meter";
4523 :
4524 0 : } else if (varType == VariableType::Schedule) { // Schedule
4525 :
4526 0 : if (size(keyOutVarNums) == 0) {
4527 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4528 : }
4529 0 : keyOutVarNums(1) = Sched::GetScheduleNum(state, varName);
4530 0 : keyNames(1) = "Environment";
4531 : } else {
4532 : // do nothing
4533 : }
4534 2085 : } // GetVariableKeys()
4535 :
4536 8390 : 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 8390 : auto const &op = state.dataOutputProcessor;
4549 :
4550 8390 : std::string name = Util::makeUPPER(RepVarName);
4551 :
4552 237127 : for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
4553 228737 : if (op->reqVars[iReqVar]->name == name) {
4554 0 : return true;
4555 : }
4556 : }
4557 :
4558 8390 : if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
4559 1427 : auto const *meter = op->meters[found->second];
4560 9974 : for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
4561 8550 : if (iFreq == (int)ReportFreq::Year) {
4562 1424 : continue;
4563 : }
4564 7126 : auto const &period = meter->periods[iFreq];
4565 7126 : if (period.Rpt || period.RptFO || period.accRpt || period.accRptFO) {
4566 3 : return true;
4567 : }
4568 : }
4569 8390 : }
4570 :
4571 8387 : return false;
4572 8390 : } // ReportingThisVariable()
4573 :
4574 53 : 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 53 : auto &op = state.dataOutputProcessor;
4628 :
4629 2597 : for (int iResource = 0; iResource < (int)Constant::eResource::Num; ++iResource) {
4630 2544 : std::string meterName = format("{}:Facility", Constant::eResourceNames[iResource]);
4631 2544 : std::string meterNameUC = Util::makeUPPER(meterName);
4632 :
4633 2544 : auto found = op->meterMap.find(meterNameUC);
4634 2544 : if (found == op->meterMap.end()) {
4635 1107 : continue;
4636 : }
4637 :
4638 1437 : auto *meter = op->meters[found->second];
4639 1437 : auto &period = meter->periods[(int)freq];
4640 :
4641 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4642 1437 : 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 1437 : if (!period.Rpt) {
4647 1373 : period.Rpt = true;
4648 1373 : WriteMeterDictionaryItem(state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, false);
4649 1373 : op->freqTrackingVariables[(int)freq] = true;
4650 : }
4651 4758 : }
4652 53 : } // InitPollutionMeterReporting()
4653 :
4654 782 : 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 782 : std::string VarOption1;
4670 782 : std::string VarOption2;
4671 : bool DoReport;
4672 : bool SortByName;
4673 :
4674 782 : auto const &op = state.dataOutputProcessor;
4675 782 : auto const &rf = state.dataResultsFramework->resultsFramework;
4676 :
4677 : // See if Report Variables should be turned on
4678 782 : SortByName = false;
4679 2346 : ScanForReports(state, "VariableDictionary", DoReport, _, VarOption1, VarOption2);
4680 : // IF (.not. DoReport) RETURN
4681 :
4682 782 : if (DoReport) {
4683 770 : op->ProduceReportVDD = ReportVDD::Yes;
4684 1540 : if (VarOption1 == std::string("IDF")) {
4685 259 : op->ProduceReportVDD = ReportVDD::IDF;
4686 : }
4687 770 : if (!VarOption2.empty()) {
4688 65 : if (Util::SameString(VarOption2, "Name") || Util::SameString(VarOption2, "AscendingName")) {
4689 3 : SortByName = true;
4690 : }
4691 : }
4692 : }
4693 :
4694 1564 : state.files.rdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.rdd);
4695 1564 : state.files.mdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.mdd);
4696 782 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4697 511 : print(state.files.rdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4698 511 : print(state.files.rdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
4699 :
4700 511 : print(state.files.mdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4701 511 : print(state.files.mdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
4702 271 : } else if (op->ProduceReportVDD == ReportVDD::IDF) {
4703 259 : print(state.files.rdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4704 259 : print(state.files.rdd, "! Output:Variable Objects (applicable to this run){}", '\n');
4705 :
4706 259 : print(state.files.mdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4707 259 : print(state.files.mdd, "! Output:Meter Objects (applicable to this run){}", '\n');
4708 : }
4709 :
4710 782 : if (op->ProduceReportVDD == ReportVDD::Yes || op->ProduceReportVDD == ReportVDD::IDF) {
4711 :
4712 770 : auto miVar = op->ddOutVarMap.begin();
4713 770 : int aiVar = 0;
4714 : for (;;) {
4715 449538 : int iVar = -1;
4716 : // Too complicated to do this logic in the for loop header
4717 449538 : if (SortByName) {
4718 1222 : if (miVar == op->ddOutVarMap.end()) {
4719 3 : break;
4720 : }
4721 1219 : iVar = miVar->second;
4722 1219 : ++miVar;
4723 : } else {
4724 448316 : if (aiVar == (int)op->ddOutVars.size()) {
4725 767 : break;
4726 : }
4727 447549 : iVar = aiVar;
4728 447549 : ++aiVar;
4729 : }
4730 :
4731 448768 : auto *ddVar = op->ddOutVars[iVar];
4732 :
4733 448768 : if (ddVar->ReportedOnDDFile) {
4734 0 : continue;
4735 : }
4736 :
4737 : static constexpr std::array<std::string_view, (int)TimeStepType::Num> timeStepNamesLocal = {"Zone", "HVAC"};
4738 448768 : std::string_view timeStepName = timeStepNamesLocal[(int)ddVar->timeStepType];
4739 448768 : std::string_view storeTypeName = storeTypeNames[(int)ddVar->storeType];
4740 448768 : std::string_view varName = ddVar->name;
4741 : std::string_view unitName =
4742 448768 : (ddVar->units == Constant::Units::customEMS) ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)ddVar->units];
4743 448768 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4744 293620 : print(state.files.rdd, "{},{},{} [{}]\n", timeStepName, storeTypeName, varName, unitName);
4745 293620 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4746 : } else {
4747 155148 : print(state.files.rdd, "Output:Variable,*,{},hourly; !- {} {} [{}]\n", varName, timeStepName, storeTypeName, unitName);
4748 155148 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4749 : }
4750 :
4751 448768 : ddVar->ReportedOnDDFile = true;
4752 448768 : if (SortByName) {
4753 1219 : 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 448768 : } // for (aiVar, miVar)
4771 770 : } // if (produceReportVDD)
4772 782 : state.files.rdd.close();
4773 :
4774 782 : auto miMeter = op->meterMap.begin();
4775 94414 : for (int aiMeter = 0; aiMeter < (int)op->meters.size() && miMeter != op->meterMap.end(); ++aiMeter, ++miMeter) {
4776 93632 : int iMeter = (SortByName) ? miMeter->second : aiMeter;
4777 93632 : auto *meter = op->meters[iMeter];
4778 93632 : std::string_view unitName = Constant::unitNames[(int)meter->units];
4779 93632 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4780 46668 : print(state.files.mdd, "Zone,Meter,{} [{}]\n", meter->Name, unitName);
4781 46668 : rf->MDD.push_back(format("Zone,Meter,{} [{}]", meter->Name, unitName));
4782 46964 : } else if (op->ProduceReportVDD == ReportVDD::IDF) {
4783 45655 : print(state.files.mdd, "Output:Meter,{},hourly; !- [{}]\n", meter->Name, unitName);
4784 45655 : rf->MDD.push_back(format("Output:Meter,{} [{}]", meter->Name, unitName));
4785 45655 : print(state.files.mdd, "Output:Meter:Cumulative,{},hourly; !- [{}]\n", meter->Name, unitName);
4786 45655 : rf->MDD.push_back(format("Output:Meter:Cumulative,{} [{}]", meter->Name, unitName));
4787 : }
4788 : }
4789 782 : state.files.mdd.close();
4790 782 : } // ProduceRDDMDD()
4791 :
4792 6528855 : 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 6528855 : auto const &op = state.dataOutputProcessor;
4815 :
4816 6528855 : std::string nameUC = Util::makeUPPER(name);
4817 :
4818 6528855 : auto found = op->ddOutVarMap.find(nameUC);
4819 6528855 : if (found == op->ddOutVarMap.end()) {
4820 467991 : auto *ddVar = new DDOutVar();
4821 467991 : op->ddOutVars.push_back(ddVar);
4822 : // Add to map
4823 467991 : op->ddOutVarMap.insert_or_assign(nameUC, op->ddOutVars.size() - 1);
4824 :
4825 467991 : ddVar->timeStepType = timeStepType;
4826 467991 : ddVar->storeType = storeType;
4827 467991 : ddVar->variableType = variableType;
4828 467991 : ddVar->name = name;
4829 467991 : ddVar->units = units;
4830 467991 : if (!customUnitName.empty() && units == Constant::Units::customEMS) {
4831 10 : ddVar->unitNameCustomEMS = customUnitName;
4832 : }
4833 467991 : return op->ddOutVars.size() - 1;
4834 :
4835 6060864 : } else if (units == op->ddOutVars[found->second]->units) {
4836 6060858 : return found->second;
4837 :
4838 : } else { // not the same as first units
4839 6 : int dup2 = -1; // for duplicate variable name
4840 6 : auto *ddVarDup = op->ddOutVars[found->second];
4841 7 : while (ddVarDup->Next != -1) {
4842 2 : if (units != op->ddOutVars[ddVarDup->Next]->units) {
4843 1 : ddVarDup = op->ddOutVars[ddVarDup->Next];
4844 1 : continue;
4845 : }
4846 1 : dup2 = ddVarDup->Next;
4847 1 : break;
4848 : }
4849 6 : if (dup2 == -1) {
4850 5 : DDOutVar *ddVar2 = new DDOutVar();
4851 5 : op->ddOutVars.push_back(ddVar2);
4852 : // Don't add this one to the map. Leave the map pointing to the first one
4853 5 : ddVar2->timeStepType = timeStepType;
4854 5 : ddVar2->storeType = storeType;
4855 5 : ddVar2->variableType = variableType;
4856 5 : ddVar2->name = name;
4857 5 : ddVar2->units = units;
4858 5 : if (!customUnitName.empty() && units == Constant::Units::customEMS) {
4859 0 : ddVar2->unitNameCustomEMS = customUnitName;
4860 : }
4861 5 : ddVarDup->Next = op->ddOutVars.size() - 1;
4862 :
4863 5 : return op->ddOutVars.size() - 1;
4864 : } else {
4865 1 : return dup2;
4866 : } // if (dup2 == 0)
4867 : } // if (unitsForVar)
4868 6528855 : } // AddDDOutVar()
4869 :
4870 803 : int initErrorFile(EnergyPlusData &state)
4871 : {
4872 803 : state.files.err_stream = std::make_unique<std::ofstream>(state.files.outputErrFilePath);
4873 803 : 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 803 : return EXIT_SUCCESS;
4878 : } // initErrorFile()
4879 :
4880 : } // namespace EnergyPlus
|