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 801 : 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 801 : auto &op = state.dataOutputProcessor;
149 :
150 : // Initialize end use category names - the indices must match up with endUseNames in OutputReportTabular
151 801 : op->EndUseCategory.allocate((int)Constant::EndUse::Num);
152 801 : op->EndUseCategory((int)Constant::EndUse::Heating + 1).Name = "Heating";
153 801 : op->EndUseCategory((int)Constant::EndUse::Cooling + 1).Name = "Cooling";
154 801 : op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).Name = "InteriorLights";
155 801 : op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).Name = "ExteriorLights";
156 801 : op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).Name = "InteriorEquipment";
157 801 : op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).Name = "ExteriorEquipment";
158 801 : op->EndUseCategory((int)Constant::EndUse::Fans + 1).Name = "Fans";
159 801 : op->EndUseCategory((int)Constant::EndUse::Pumps + 1).Name = "Pumps";
160 801 : op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).Name = "HeatRejection";
161 801 : op->EndUseCategory((int)Constant::EndUse::Humidification + 1).Name = "Humidifier";
162 801 : op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).Name = "HeatRecovery";
163 801 : op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).Name = "WaterSystems";
164 801 : op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).Name = "Refrigeration";
165 801 : 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 801 : op->EndUseCategory((int)Constant::EndUse::Heating + 1).DisplayName = "Heating";
169 801 : op->EndUseCategory((int)Constant::EndUse::Cooling + 1).DisplayName = "Cooling";
170 801 : op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).DisplayName = "Interior Lighting";
171 801 : op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).DisplayName = "Exterior Lighting";
172 801 : op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).DisplayName = "Interior Equipment";
173 801 : op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).DisplayName = "Exterior Equipment";
174 801 : op->EndUseCategory((int)Constant::EndUse::Fans + 1).DisplayName = "Fans";
175 801 : op->EndUseCategory((int)Constant::EndUse::Pumps + 1).DisplayName = "Pumps";
176 801 : op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).DisplayName = "Heat Rejection";
177 801 : op->EndUseCategory((int)Constant::EndUse::Humidification + 1).DisplayName = "Humidification";
178 801 : op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).DisplayName = "Heat Recovery";
179 801 : op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).DisplayName = "Water Systems";
180 801 : op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).DisplayName = "Refrigeration";
181 801 : op->EndUseCategory((int)Constant::EndUse::Cogeneration + 1).DisplayName = "Generators";
182 :
183 801 : op->OutputInitialized = true;
184 :
185 801 : op->TimeStepZoneSec = double(state.dataGlobal->MinutesInTimeStep) * 60.0;
186 :
187 1602 : state.files.mtd.ensure_open(state, "InitializeMeters", state.files.outputControl.mtd);
188 801 : } // InitializeOutput()
189 :
190 43210 : 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 43210 : auto &op = state.dataOutputProcessor;
201 :
202 43210 : Constant::EndUse endUse = endUseCat2endUse[(int)endUseCat];
203 43210 : 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 43210 : auto &endUseCategory = op->EndUseCategory((int)endUse + 1);
209 :
210 49472 : for (int EndUseSubNum = 1; EndUseSubNum <= endUseCategory.NumSubcategories; ++EndUseSubNum) {
211 42941 : if (Util::SameString(endUseCategory.SubcategoryName(EndUseSubNum), endUseSubName)) {
212 36679 : return; // Subcategory already exists, no further action required
213 : }
214 : }
215 :
216 : // Add the subcategory by reallocating the array
217 6531 : endUseCategory.SubcategoryName.redimension(++endUseCategory.NumSubcategories);
218 6531 : endUseCategory.SubcategoryName(endUseCategory.NumSubcategories) = endUseSubName;
219 :
220 6531 : if (endUseCategory.NumSubcategories > op->MaxNumSubcategories) {
221 708 : op->MaxNumSubcategories = endUseCategory.NumSubcategories;
222 : }
223 : } // addEndUseSubcategory()
224 :
225 9185 : void addEndUseSpaceType(EnergyPlusData &state, OutputProcessor::EndUseCat sovEndUseCat, std::string_view const EndUseSpaceTypeName)
226 : {
227 9185 : auto &op = state.dataOutputProcessor;
228 :
229 9185 : Constant::EndUse endUse = endUseCat2endUse[(int)sovEndUseCat];
230 :
231 9185 : 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 9185 : auto &endUseCat = op->EndUseCategory((int)endUse + 1);
237 :
238 9233 : for (int endUseSpTypeNum = 1; endUseSpTypeNum <= endUseCat.numSpaceTypes; ++endUseSpTypeNum) {
239 7886 : if (Util::SameString(endUseCat.spaceTypeName(endUseSpTypeNum), EndUseSpaceTypeName)) {
240 7838 : return; // SpaceType already exists, no further action required
241 : }
242 : }
243 :
244 : // Add the space type by reallocating the array
245 1347 : endUseCat.spaceTypeName.redimension(++endUseCat.numSpaceTypes);
246 1347 : endUseCat.spaceTypeName(endUseCat.numSpaceTypes) = EndUseSpaceTypeName;
247 :
248 1347 : if (endUseCat.numSpaceTypes > op->maxNumEndUseSpaceTypes) {
249 12 : op->maxNumEndUseSpaceTypes = endUseCat.numSpaceTypes;
250 : }
251 : } // addEndUseSpaceType()
252 :
253 1602 : 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 1602 : if (state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep != nullptr) {
276 0 : ShowFatalError(state, format("SetupTimePointers was already called for {}", timeStepTypeNames[(int)timeStep]));
277 : }
278 1602 : state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep = &TimeStep;
279 1602 : }
280 :
281 6630812 : 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 6630812 : GetReportVariableInput(state);
312 :
313 6630812 : auto const &op = state.dataOutputProcessor;
314 :
315 223024128 : for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
316 216393316 : auto *reqVar = op->reqVars[iReqVar];
317 :
318 216393316 : if (!Util::SameString(reqVar->name, Name)) {
319 215259704 : continue;
320 : }
321 :
322 2204361 : if (!reqVar->key.empty() && !(reqVar->is_simple_string && Util::SameString(reqVar->key, Key)) &&
323 2204361 : !(!reqVar->is_simple_string && RE2::FullMatch(std::string{Key}, *(reqVar->case_insensitive_pattern)))) {
324 1070749 : continue;
325 : }
326 :
327 : // A match. Make sure doesn't duplicate
328 62863 : reqVar->Used = true;
329 62863 : 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 65110 : for (int iReqVar2 : reqVarList) {
332 3557 : if (op->reqVars[iReqVar2]->freq == reqVar->freq && op->reqVars[iReqVar2]->sched == reqVar->sched) {
333 1310 : Dup = true;
334 1310 : break;
335 : }
336 62863 : }
337 :
338 62863 : if (!Dup) {
339 61553 : reqVarList.push_back(iReqVar);
340 : }
341 : }
342 6630812 : }
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 28635 : 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 28635 : 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 28635 : const std::string FreqStringUpper = Util::makeUPPER(FreqString);
400 28635 : std::string::size_type const LenString = min(len(FreqString), static_cast<std::string::size_type>(4u));
401 :
402 28635 : if (LenString < 4u) {
403 0 : return freq;
404 : }
405 :
406 28635 : std::string const FreqStringTrim(FreqStringUpper.substr(0, LenString));
407 187454 : for (unsigned Loop = 0; Loop < FreqValues.size(); ++Loop) {
408 93727 : if (FreqStringTrim == PossibleFreqs[Loop]) {
409 28635 : 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 28635 : freq = std::max(FreqValues[Loop], state.dataOutputProcessor->minimumReportFreq);
414 28635 : break;
415 : }
416 : }
417 28635 : return freq;
418 28635 : }
419 :
420 6630812 : 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 6630812 : constexpr std::string_view routineName = "GetReportVariableInput";
450 :
451 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
452 : int NumAlpha;
453 : int NumNumbers;
454 : int IOStat;
455 6630812 : bool ErrorsFound(false); // If errors detected in input
456 6630812 : std::string cCurrentModuleObject;
457 6630812 : Array1D_string cAlphaArgs(4);
458 6630812 : Array1D_string cAlphaFieldNames(4);
459 6630812 : Array1D_bool lAlphaBlanks(4);
460 6630812 : Array1D<Real64> rNumericArgs(1);
461 6630812 : Array1D_string cNumericFieldNames(1);
462 6630812 : Array1D_bool lNumericBlanks(1);
463 6630812 : auto &op = state.dataOutputProcessor;
464 :
465 : // Bail out if the input has already been read in
466 6630812 : if (!op->GetOutputInputFlag) {
467 6630011 : return;
468 : }
469 801 : op->GetOutputInputFlag = false;
470 :
471 : // First check environment variable to see of possible override for minimum reporting frequency
472 801 : 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 801 : cCurrentModuleObject = "Output:Variable";
482 801 : int numReqVariables = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
483 :
484 22594 : for (int Loop = 1; Loop <= numReqVariables; ++Loop) {
485 :
486 21793 : 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 21793 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
500 :
501 : // Check for duplicates?
502 21793 : ReqVar *reqVar = new ReqVar();
503 21793 : op->reqVars.push_back(reqVar);
504 :
505 21793 : reqVar->key = cAlphaArgs(1);
506 21793 : if (reqVar->key == "*") {
507 15603 : reqVar->key = std::string();
508 : }
509 :
510 21793 : bool is_simple_string = !DataOutputs::isKeyRegexLike(reqVar->key);
511 21793 : reqVar->is_simple_string = is_simple_string;
512 21793 : if (!is_simple_string) {
513 0 : reqVar->case_insensitive_pattern = std::make_shared<RE2>("(?i)" + reqVar->key);
514 : }
515 :
516 21793 : std::string::size_type const lbpos = index(cAlphaArgs(2), '['); // Remove Units designation if user put it in
517 21793 : 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 21793 : reqVar->name = cAlphaArgs(2);
523 :
524 21793 : reqVar->freq = determineFrequency(state, Util::makeUPPER(cAlphaArgs(3)));
525 21793 : if (reqVar->freq == ReportFreq::Invalid) {
526 0 : ShowSevereInvalidKey(state, eoh, cAlphaFieldNames(3), cAlphaArgs(3));
527 0 : ErrorsFound = true;
528 : }
529 :
530 : // Schedule information
531 21793 : if (lAlphaBlanks(4)) {
532 21158 : 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 21793 : reqVar->Used = false;
539 : }
540 :
541 801 : if (ErrorsFound) {
542 0 : ShowFatalError(state, format("GetReportVariableInput:{}: errors in input.", cCurrentModuleObject));
543 : }
544 46410878 : }
545 :
546 105882 : 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 105882 : General::DecodeMonDayHrMin(date, Mon, Day, Hour, Minute);
570 :
571 105882 : switch (freq) {
572 20386 : case ReportFreq::Day:
573 20386 : return format("{:2},{:2}", Hour, Minute);
574 71948 : case ReportFreq::Month:
575 71948 : return format("{:2},{:2},{:2}", Day, Hour, Minute);
576 13548 : case ReportFreq::Year:
577 : case ReportFreq::Simulation:
578 13548 : 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 800 : 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 800 : constexpr std::string_view routineName = "GetCustomMeterInput";
660 :
661 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
662 800 : auto &op = state.dataOutputProcessor;
663 800 : auto &ip = state.dataInputProcessing->inputProcessor;
664 800 : auto &ipsc = state.dataIPShortCut;
665 :
666 : int NumAlpha;
667 : int NumNumbers;
668 : int IOStat;
669 800 : Array1D_string NamesOfKeys; // Specific key name
670 800 : Array1D_int IndexesForKeyVar; // Array index
671 :
672 800 : Array1D_int VarsOnSourceMeter;
673 :
674 800 : bool BigErrorsFound = false;
675 :
676 800 : int numCustomMeters = 0, numCustomDecMeters = 0;
677 800 : std::vector<std::string> customMeterNames;
678 800 : std::vector<std::string> customDecMeterNames;
679 2400 : if (auto const found = ip->epJSON.find("Meter:Custom"); found != ip->epJSON.end()) {
680 143 : for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomMeters) {
681 97 : customMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
682 46 : }
683 800 : }
684 :
685 2400 : if (auto const found = ip->epJSON.find("Meter:CustomDecrement"); found != ip->epJSON.end()) {
686 82 : for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomDecMeters) {
687 41 : customDecMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
688 41 : }
689 800 : }
690 :
691 800 : ipsc->cCurrentModuleObject = "Meter:Custom";
692 897 : for (int Loop = 1; Loop <= numCustomMeters; ++Loop) {
693 194 : ip->getObjectItem(state,
694 97 : ipsc->cCurrentModuleObject,
695 : Loop,
696 97 : ipsc->cAlphaArgs,
697 : NumAlpha,
698 97 : ipsc->rNumericArgs,
699 : NumNumbers,
700 : IOStat,
701 97 : ipsc->lNumericFieldBlanks,
702 97 : ipsc->lAlphaFieldBlanks,
703 97 : ipsc->cAlphaFieldNames,
704 97 : ipsc->cNumericFieldNames);
705 :
706 97 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
707 :
708 97 : std::string meterName = ipsc->cAlphaArgs(1);
709 97 : std::string::size_type lbrackPos = index(meterName, '[');
710 97 : if (lbrackPos != std::string::npos) {
711 0 : meterName.erase(lbrackPos);
712 : }
713 :
714 97 : std::string meterNameUC = Util::makeUPPER(meterName);
715 :
716 : // Check for duplicate name
717 97 : 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 97 : static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
726 97 : 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 97 : 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 97 : bool foundBadSrc = false;
740 97 : bool itemsAssigned = false;
741 :
742 477 : for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
743 380 : if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
744 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
745 0 : foundBadSrc = true;
746 0 : break;
747 : }
748 :
749 380 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
750 380 : lbrackPos = index(meterOrVarNameUC, '[');
751 380 : if (lbrackPos != std::string::npos) {
752 0 : meterOrVarNameUC.erase(lbrackPos);
753 : }
754 :
755 : // A custom meter cannot reference another custom meter
756 380 : 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 380 : 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 380 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
778 47 : int srcMeterNum = foundSrcMeter->second;
779 47 : auto *srcMeter = op->meters[srcMeterNum];
780 47 : assert(srcMeter->type == MeterType::Normal);
781 :
782 : // If it's the first meter, it gets to set the units
783 47 : if (units == Constant::Units::Invalid) {
784 46 : units = srcMeter->units;
785 46 : 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 333 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
803 332 : int srcDDVarNum = foundSrcDDVar->second;
804 332 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
805 :
806 : // Has to be a summed variable
807 332 : 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 332 : if (units == Constant::Units::Invalid) {
824 50 : units = srcDDVar->units;
825 : // Otherwise it has to match the existing units
826 282 : } 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 332 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
838 : // Have already checked for mismatching units between meter and source variable and assigned units
839 332 : if (KeyIsStar) {
840 13 : if (srcDDVar->keyOutVarNums.empty()) {
841 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
842 0 : foundBadSrc = true;
843 0 : break;
844 : }
845 :
846 13 : itemsAssigned = true;
847 : } else { // Key is not "*"
848 319 : bool foundKey = false;
849 3367 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
850 3367 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
851 319 : foundKey = true;
852 319 : itemsAssigned = true;
853 319 : break;
854 : }
855 319 : }
856 319 : 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 713 : }
874 :
875 380 : } // for (fldIndex)
876 :
877 : // Somehow, this meter is not linked to any variables either directly or via another meter
878 97 : 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 96 : if (foundBadSrc) {
887 0 : continue;
888 : }
889 :
890 96 : auto *meter = new Meter(meterName);
891 96 : meter->type = MeterType::Custom;
892 96 : meter->resource = resource;
893 96 : meter->units = units;
894 96 : bool errFlag = false;
895 96 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
896 96 : 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 96 : int meterNum = op->meters.size();
903 96 : op->meters.push_back(meter);
904 96 : op->meterMap.insert_or_assign(meterNameUC, meterNum);
905 :
906 96 : for (ReportFreq reportFreq :
907 768 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
908 576 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
909 : }
910 :
911 96 : for (ReportFreq reportFreq :
912 768 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
913 576 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
914 : }
915 :
916 : // Do the loop again, this time without error checking
917 475 : for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
918 : // No need to check for empty fields
919 379 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
920 379 : lbrackPos = index(meterOrVarNameUC, '[');
921 379 : if (lbrackPos != std::string::npos) {
922 0 : meterOrVarNameUC.erase(lbrackPos);
923 : }
924 :
925 : // No need to check for custom source meters
926 379 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
927 47 : int srcMeterNum = foundSrcMeter->second;
928 47 : auto *srcMeter = op->meters[srcMeterNum];
929 47 : 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 47 : 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 47 : meter->srcMeterNums.push_back(srcMeterNum);
946 47 : srcMeter->dstMeterNums.push_back(meterNum);
947 :
948 408 : for (int srcVarNum : srcMeter->srcVarNums) {
949 361 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
950 361 : meter->srcVarNums.push_back(srcVarNum);
951 361 : op->outVars[srcVarNum]->meterNums.push_back(meterNum);
952 : }
953 47 : }
954 :
955 : // It's a variable
956 332 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
957 332 : int srcDDVarNum = foundSrcDDVar->second;
958 332 : 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 332 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
964 : // Have already checked for mismatching units between meter and source variable and assigned units
965 332 : if (KeyIsStar) {
966 : // No need to check for empty keys
967 26 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
968 13 : 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 13 : meter->srcVarNums.push_back(keyOutVarNum);
976 13 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
977 : }
978 13 : }
979 : } else { // Key is not "*"
980 3367 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
981 3367 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
982 319 : 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 319 : meter->srcVarNums.push_back(keyOutVarNum);
989 319 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
990 : }
991 319 : break;
992 : }
993 319 : }
994 : } // if (keyIsStar)
995 711 : } // if (meter or variable)
996 :
997 379 : } // for (fldIndex)
998 98 : } // for (Loop)
999 :
1000 800 : ipsc->cCurrentModuleObject = "Meter:CustomDecrement";
1001 841 : for (int Loop = 1; Loop <= numCustomDecMeters; ++Loop) {
1002 82 : ip->getObjectItem(state,
1003 41 : ipsc->cCurrentModuleObject,
1004 : Loop,
1005 41 : ipsc->cAlphaArgs,
1006 : NumAlpha,
1007 41 : ipsc->rNumericArgs,
1008 : NumNumbers,
1009 : IOStat,
1010 41 : ipsc->lNumericFieldBlanks,
1011 41 : ipsc->lAlphaFieldBlanks,
1012 41 : ipsc->cAlphaFieldNames,
1013 41 : ipsc->cNumericFieldNames);
1014 :
1015 41 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
1016 :
1017 41 : std::string meterName = ipsc->cAlphaArgs(1);
1018 41 : std::string::size_type lbrackPos = index(meterName, '[');
1019 41 : if (lbrackPos != std::string::npos) {
1020 0 : meterName.erase(lbrackPos);
1021 : }
1022 41 : std::string meterNameUC = Util::makeUPPER(meterName);
1023 :
1024 : // Search for duplicate name
1025 41 : 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 41 : static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
1034 41 : 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 41 : bool itemsAssigned = false;
1041 :
1042 41 : std::string decMeterName = ipsc->cAlphaArgs(3);
1043 41 : lbrackPos = index(decMeterName, '[');
1044 41 : if (lbrackPos != std::string::npos) {
1045 0 : decMeterName.erase(lbrackPos);
1046 : }
1047 41 : std::string decMeterNameUC = Util::makeUPPER(decMeterName);
1048 :
1049 : // DecMeter cannot be a Meter:Custom
1050 41 : 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 41 : auto foundDecMeter = op->meterMap.find(decMeterName);
1061 41 : if (foundDecMeter == op->meterMap.end()) {
1062 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), decMeterName);
1063 0 : ErrorsFound = true;
1064 0 : continue;
1065 : }
1066 :
1067 41 : int decMeterNum = foundDecMeter->second;
1068 41 : auto *decMeter = op->meters[decMeterNum];
1069 41 : assert(decMeter->type == MeterType::Normal);
1070 :
1071 41 : Constant::Units units = decMeter->units;
1072 :
1073 41 : 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 41 : bool foundBadSrc = false;
1081 :
1082 82 : for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
1083 41 : if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
1084 0 : ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
1085 0 : foundBadSrc = true;
1086 0 : break;
1087 : }
1088 :
1089 41 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
1090 41 : lbrackPos = index(meterOrVarNameUC, '[');
1091 41 : if (lbrackPos != std::string::npos) {
1092 0 : meterOrVarNameUC.erase(lbrackPos);
1093 : }
1094 :
1095 : // A custom meter cannot reference another custom meter
1096 41 : 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 41 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
1107 37 : int srcMeterNum = foundSrcMeter->second;
1108 37 : auto *srcMeter = op->meters[srcMeterNum];
1109 37 : assert(srcMeter->type == MeterType::Normal || srcMeter->type == MeterType::Custom);
1110 :
1111 : // If it's the first meter, it gets to set the units
1112 37 : if (units == Constant::Units::Invalid) {
1113 0 : units = srcMeter->units;
1114 0 : itemsAssigned = true;
1115 37 : } 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 4 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
1132 4 : int srcDDVarNum = foundSrcDDVar->second;
1133 4 : auto *srcDDVar = op->ddOutVars[srcDDVarNum];
1134 :
1135 : // Has to be a summed variable
1136 4 : 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 4 : if (units == Constant::Units::Invalid) {
1153 0 : units = srcDDVar->units;
1154 : // Otherwise it has to match the existing units
1155 4 : } 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 4 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
1167 : // Have already checked for mismatching units between meter and source variable and assigned units
1168 4 : 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 4 : bool foundKey = false;
1178 195 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1179 195 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
1180 4 : foundKey = true;
1181 4 : itemsAssigned = true;
1182 4 : break;
1183 : }
1184 4 : }
1185 4 : 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 45 : }
1204 :
1205 41 : } // for (fldIndex)
1206 :
1207 : // Somehow, this meter is not linked to any variables either directly or via another meter
1208 41 : 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 41 : if (foundBadSrc) {
1217 0 : continue;
1218 : }
1219 :
1220 41 : auto *meter = new Meter(meterName);
1221 41 : meter->type = MeterType::CustomDec;
1222 41 : meter->resource = resource;
1223 41 : meter->units = units;
1224 41 : bool errFlag = false;
1225 41 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
1226 41 : 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 41 : meter->decMeterNum = decMeterNum;
1232 :
1233 : // This meter is good
1234 41 : int meterNum = op->meters.size();
1235 41 : op->meters.push_back(meter);
1236 41 : op->meterMap.insert_or_assign(meterNameUC, meterNum);
1237 :
1238 41 : for (ReportFreq reportFreq :
1239 328 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1240 246 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
1241 : }
1242 :
1243 41 : for (ReportFreq reportFreq :
1244 328 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1245 246 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
1246 : }
1247 :
1248 : // Links meter to dec meter and its output variable and vice versa
1249 41 : meter->srcMeterNums.push_back(meter->decMeterNum);
1250 41 : 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 82 : for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
1261 : // No need to check for empty fields
1262 41 : std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
1263 41 : lbrackPos = index(meterOrVarNameUC, '[');
1264 41 : if (lbrackPos != std::string::npos) {
1265 0 : meterOrVarNameUC.erase(lbrackPos);
1266 : }
1267 :
1268 : // No need to check for custom source meters
1269 41 : if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
1270 37 : int srcMeterNum = foundSrcMeter->second;
1271 37 : auto *srcMeter = op->meters[srcMeterNum];
1272 37 : 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 37 : 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 37 : meter->srcMeterNums.push_back(srcMeterNum);
1289 37 : srcMeter->dstMeterNums.push_back(meterNum);
1290 :
1291 224 : for (int srcVarNum : srcMeter->srcVarNums) {
1292 187 : if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
1293 187 : meter->srcVarNums.push_back(srcVarNum);
1294 187 : op->outVars[srcVarNum]->meterNums.push_back(meterNum);
1295 : }
1296 37 : }
1297 :
1298 : // It's a variable
1299 4 : } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
1300 4 : int srcDDVarNum = foundSrcDDVar->second;
1301 4 : 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 4 : bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
1307 : // Have already checked for mismatching units between meter and source variable and assigned units
1308 4 : 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 195 : for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
1324 195 : if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
1325 4 : 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 4 : meter->srcVarNums.push_back(keyOutVarNum);
1332 4 : op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
1333 : }
1334 4 : break;
1335 : }
1336 4 : }
1337 : } // if (keyIsStar)
1338 45 : } // if (meter or variable)
1339 :
1340 41 : } // for (fldIndex)
1341 41 : }
1342 :
1343 800 : if (BigErrorsFound) {
1344 0 : ErrorsFound = true;
1345 : }
1346 800 : }
1347 :
1348 311260 : 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 311260 : auto &op = state.dataOutputProcessor;
1369 :
1370 311260 : int meterNum = -1;
1371 311260 : Meter *meter = nullptr;
1372 :
1373 311260 : std::string nameUC = Util::makeUPPER(Name);
1374 :
1375 311260 : if (auto found = op->meterMap.find(nameUC); found != op->meterMap.end()) {
1376 212045 : meterNum = found->second;
1377 212045 : meter = op->meters[meterNum];
1378 : } else {
1379 :
1380 99215 : meterNum = op->meters.size();
1381 99215 : meter = new Meter(Name);
1382 99215 : op->meters.push_back(meter);
1383 99215 : op->meterMap.insert_or_assign(nameUC, meterNum);
1384 :
1385 99215 : meter->type = MeterType::Normal;
1386 99215 : meter->resource = resource;
1387 99215 : meter->endUseCat = endUseCat;
1388 99215 : meter->EndUseSub = EndUseSub;
1389 99215 : meter->group = group;
1390 99215 : meter->units = units;
1391 99215 : meter->CurTSValue = 0.0;
1392 :
1393 99215 : for (ReportFreq reportFreq :
1394 793720 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1395 595290 : meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
1396 : }
1397 :
1398 99215 : for (ReportFreq reportFreq :
1399 793720 : {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
1400 595290 : meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
1401 : }
1402 :
1403 99215 : if (meter->resource != Constant::eResource::Invalid) {
1404 99215 : bool errFlag = false;
1405 99215 : meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, units, errFlag);
1406 99215 : 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 311260 : }
1412 :
1413 : // outVarNum == -1 is only true in unit tests
1414 311260 : if (outVarNum != -1) {
1415 311260 : OutVar *var = op->outVars[outVarNum];
1416 311260 : var->meterNums.push_back(meterNum);
1417 311260 : meter->srcVarNums.push_back(outVarNum);
1418 : }
1419 :
1420 311260 : return meterNum;
1421 311260 : }
1422 :
1423 61893 : 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 61893 : std::string_view resourceName = Constant::eResourceNames[(int)resource];
1444 :
1445 61893 : std::string endUseSub = standardizeEndUseSub(endUseCat, EndUseSub);
1446 :
1447 61893 : if (!endUseSub.empty()) {
1448 43210 : addEndUseSubcategory(state, endUseCat, endUseSub);
1449 : }
1450 :
1451 61893 : if (!SpaceType.empty()) {
1452 9185 : addEndUseSpaceType(state, endUseCat, SpaceType);
1453 : }
1454 :
1455 61893 : std::string meterName = format("{}:Facility", resourceName);
1456 61893 : AddMeter(state, meterName, units, resource, EndUseCat::Invalid, "", Group::Invalid, outVarNum);
1457 :
1458 61893 : if (group != Group::Invalid) {
1459 55350 : std::string groupMeterName = format("{}:{}", resourceName, groupNames[(int)group]);
1460 55350 : AddMeter(state, groupMeterName, units, resource, EndUseCat::Invalid, "", group, outVarNum);
1461 :
1462 55350 : if (group == Group::Building) {
1463 20809 : if (!ZoneName.empty()) {
1464 20464 : std::string zoneMeterName = format("{}:Zone:{}", resourceName, ZoneName);
1465 20464 : AddMeter(state, zoneMeterName, units, resource, EndUseCat::Invalid, "", Group::Zone, outVarNum);
1466 20464 : }
1467 20809 : if (!SpaceType.empty()) {
1468 9185 : std::string spaceMeterName = format("{}:SpaceType:{}", resourceName, SpaceType);
1469 9185 : AddMeter(state, spaceMeterName, units, resource, EndUseCat::Invalid, "", Group::SpaceType, outVarNum);
1470 9185 : }
1471 : } // if (Group == "Building")
1472 55350 : }
1473 :
1474 : //!! Following if we do EndUse by ResourceType
1475 61893 : if (endUseCat != EndUseCat::Invalid) {
1476 61860 : std::string_view endUseCatName = endUseCatNames[(int)endUseCat];
1477 61860 : std::string enduseMeterName = format("{}:{}", endUseCatName, resourceName);
1478 61860 : AddMeter(state, enduseMeterName, units, resource, endUseCat, "", Group::Invalid, outVarNum);
1479 :
1480 61860 : if (group == Group::Building) { // Match to Zone and Space
1481 20803 : if (!ZoneName.empty()) {
1482 20464 : std::string enduseZoneMeterName = format("{}:{}:Zone:{}", endUseCatName, resourceName, ZoneName);
1483 20464 : AddMeter(state, enduseZoneMeterName, units, resource, endUseCat, "", Group::Zone, outVarNum);
1484 20464 : }
1485 20803 : if (!SpaceType.empty()) {
1486 9185 : std::string enduseSpaceMeterName = format("{}:{}:SpaceType:{}", endUseCatName, resourceName, SpaceType);
1487 9185 : AddMeter(state, enduseSpaceMeterName, units, resource, endUseCat, "", Group::SpaceType, outVarNum);
1488 9185 : }
1489 : }
1490 :
1491 : // End-Use Subcategories
1492 61860 : if (!endUseSub.empty()) {
1493 43210 : std::string subEnduseMeterName = format("{}:{}:{}", endUseSub, endUseCatNames[(int)endUseCat], resourceName);
1494 43210 : AddMeter(state, subEnduseMeterName, units, resource, endUseCat, endUseSub, Group::Invalid, outVarNum);
1495 :
1496 43210 : if (group == Group::Building) { // Match to Zone and Space
1497 20803 : if (!ZoneName.empty()) {
1498 20464 : std::string subEnduseZoneMeterName = format("{}:{}:{}:Zone:{}", endUseSub, endUseCatName, resourceName, ZoneName);
1499 20464 : AddMeter(state, subEnduseZoneMeterName, units, resource, endUseCat, endUseSub, Group::Zone, outVarNum);
1500 20464 : }
1501 20803 : if (!SpaceType.empty()) {
1502 9185 : std::string subEnduseSpaceMeterName = format("{}:{}:{}:SpaceType:{}", endUseSub, endUseCatName, resourceName, SpaceType);
1503 9185 : AddMeter(state, subEnduseSpaceMeterName, units, resource, endUseCat, endUseSub, Group::SpaceType, outVarNum);
1504 9185 : }
1505 : } // if (sovGroup == Building)
1506 43210 : } // if (!endUseSub.empty())
1507 61860 : } // if (sovEndUseCat != Invalid)
1508 61893 : } // AttachMeters()
1509 :
1510 61893 : std::string standardizeEndUseSub(EndUseCat endUseCat, std::string_view endUseSubName)
1511 : {
1512 61893 : if (!endUseSubName.empty()) {
1513 40410 : return std::string(endUseSubName);
1514 41688 : } else if (endUseCat == EndUseCat::Invalid) {
1515 66 : return "";
1516 41655 : } else if (endUseCat2endUse[(int)endUseCat] != Constant::EndUse::Invalid) {
1517 46010 : return "General";
1518 : } else {
1519 37300 : return "";
1520 : }
1521 : }
1522 :
1523 99352 : 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 99352 : switch (resource) {
1555 46140 : 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 46140 : IPUnits = RT_IPUnits::Electricity;
1561 46140 : } break;
1562 2832 : case Constant::eResource::NaturalGas: {
1563 2832 : IPUnits = RT_IPUnits::Gas;
1564 2832 : } break;
1565 3584 : 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 3584 : IPUnits = RT_IPUnits::Water;
1571 3584 : } break;
1572 2018 : case Constant::eResource::DistrictCooling:
1573 : case Constant::eResource::PlantLoopCoolingDemand: {
1574 2018 : IPUnits = RT_IPUnits::Cooling;
1575 2018 : } break;
1576 44778 : default: {
1577 44778 : if (units == Constant::Units::m3) {
1578 308 : IPUnits = RT_IPUnits::OtherM3;
1579 44470 : } else if (units == Constant::Units::kg) {
1580 5912 : IPUnits = RT_IPUnits::OtherKG;
1581 38558 : } else if (units == Constant::Units::L) {
1582 308 : IPUnits = RT_IPUnits::OtherL;
1583 : } else {
1584 38250 : IPUnits = RT_IPUnits::OtherJ;
1585 : }
1586 44778 : } break;
1587 : } // switch
1588 :
1589 : // write(outputfiledebug,*) 'resourcetype=',TRIM(resourcetype)
1590 : // write(outputfiledebug,*) 'ipunits type=',CodeForIPUnits
1591 99352 : 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 99352 : return IPUnits;
1597 : }
1598 :
1599 495336 : 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 495336 : if (state.dataGlobal->WarmupFlag) {
1611 3456 : return;
1612 : }
1613 :
1614 491880 : auto &op = state.dataOutputProcessor;
1615 :
1616 491880 : if (op->meters.size() == 0 || op->meterValues.size() == 0) {
1617 0 : return;
1618 : }
1619 :
1620 51035232 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
1621 50543352 : auto *meter = op->meters[iMeter];
1622 50543352 : if (meter->type != MeterType::CustomDec && meter->type != MeterType::CustomDiff) {
1623 50465832 : meter->periods[(int)ReportFreq::TimeStep].Value += op->meterValues[iMeter];
1624 : // Is this correct? What is going on here?
1625 : } else {
1626 77520 : 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 50543352 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1632 50543352 : meter->periods[(int)ReportFreq::Hour].Value += TSValue;
1633 50543352 : meter->periods[(int)ReportFreq::Day].Value += TSValue;
1634 50543352 : meter->periods[(int)ReportFreq::Month].Value += TSValue;
1635 50543352 : meter->periods[(int)ReportFreq::Year].Value += TSValue;
1636 50543352 : meter->periods[(int)ReportFreq::Simulation].Value += TSValue;
1637 50543352 : meter->periodFinYrSM.Value += TSValue;
1638 : } // for (iMeter)
1639 :
1640 : // Set Max
1641 51035232 : for (auto *meter : op->meters) {
1642 50543352 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1643 50543352 : Real64 TSValueComp = TSValue; // - 0.00001;
1644 :
1645 : // Todo - HRMinVal, HRMaxVal not used
1646 50543352 : auto &periodDY = meter->periods[(int)ReportFreq::Day];
1647 50543352 : if (TSValueComp <= periodDY.MaxVal) {
1648 48707074 : continue;
1649 : }
1650 1836278 : periodDY.MaxVal = TSValue;
1651 1836278 : periodDY.MaxValDate = TimeStamp;
1652 :
1653 1836278 : auto &periodMN = meter->periods[(int)ReportFreq::Month];
1654 1836278 : if (TSValueComp <= periodMN.MaxVal) {
1655 136 : continue;
1656 : }
1657 1836142 : periodMN.MaxVal = TSValue;
1658 1836142 : periodMN.MaxValDate = TimeStamp;
1659 :
1660 1836142 : auto &periodYR = meter->periods[(int)ReportFreq::Year];
1661 1836142 : if (TSValueComp > periodYR.MaxVal) {
1662 1835407 : periodYR.MaxVal = TSValue;
1663 1835407 : periodYR.MaxValDate = TimeStamp;
1664 : }
1665 :
1666 1836142 : auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
1667 1836142 : if (TSValueComp > periodSM.MaxVal) {
1668 1835995 : periodSM.MaxVal = TSValue;
1669 1835995 : periodSM.MaxValDate = TimeStamp;
1670 : }
1671 :
1672 1836142 : if (TSValueComp > meter->periodFinYrSM.MaxVal) {
1673 1835407 : meter->periodFinYrSM.MaxVal = TSValue;
1674 1835407 : meter->periodFinYrSM.MaxValDate = TimeStamp;
1675 : }
1676 491880 : } // for (meter)
1677 :
1678 : // Set Min
1679 51035232 : for (auto *meter : op->meters) {
1680 50543352 : Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
1681 50543352 : Real64 TSValueComp = TSValue; // + 0.00001;
1682 :
1683 50543352 : auto &periodDY = meter->periods[(int)ReportFreq::Day];
1684 50543352 : if (TSValueComp >= periodDY.MinVal) {
1685 49857173 : continue;
1686 : }
1687 :
1688 686179 : periodDY.MinVal = TSValue;
1689 686179 : periodDY.MinValDate = TimeStamp;
1690 :
1691 686179 : auto &periodMN = meter->periods[(int)ReportFreq::Month];
1692 686179 : if (TSValueComp >= periodMN.MinVal) {
1693 36 : continue;
1694 : }
1695 :
1696 686143 : periodMN.MinVal = TSValue;
1697 686143 : periodMN.MinValDate = TimeStamp;
1698 :
1699 686143 : auto &periodYR = meter->periods[(int)ReportFreq::Year];
1700 686143 : if (TSValueComp < periodYR.MinVal) {
1701 686024 : periodYR.MinVal = TSValue;
1702 686024 : periodYR.MinValDate = TimeStamp;
1703 : }
1704 :
1705 686143 : auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
1706 686143 : if (TSValueComp < periodSM.MinVal) {
1707 686126 : periodSM.MinVal = TSValue;
1708 686126 : periodSM.MinValDate = TimeStamp;
1709 : }
1710 :
1711 686143 : if (TSValueComp < meter->periodFinYrSM.MinVal) {
1712 686024 : meter->periodFinYrSM.MinVal = TSValue;
1713 686024 : meter->periodFinYrSM.MinValDate = TimeStamp;
1714 : }
1715 491880 : } // for (meter)
1716 :
1717 51035232 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
1718 50543352 : 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 495336 : 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 495336 : auto &op = state.dataOutputProcessor;
1771 495336 : auto &rf = state.dataResultsFramework->resultsFramework;
1772 495336 : auto &rfMetersTS = rf->Meters[(int)ReportFreq::TimeStep];
1773 :
1774 495336 : if (!rfMetersTS.dataFrameEnabled()) {
1775 489780 : rf->initializeMeters(op->meters, ReportFreq::TimeStep);
1776 : }
1777 :
1778 495336 : PrintTimeStamp = true;
1779 51225312 : for (int Loop = 0; Loop < (int)op->meters.size(); ++Loop) {
1780 50729976 : auto *meter = op->meters[Loop];
1781 50729976 : auto &periodTS = meter->periods[(int)ReportFreq::TimeStep];
1782 50729976 : meter->CurTSValue = periodTS.Value;
1783 50729976 : if (!periodTS.Rpt && !periodTS.accRpt) {
1784 50704152 : continue;
1785 : }
1786 25824 : if (PrintTimeStamp) {
1787 5568 : CurDayType = state.dataEnvrn->DayOfWeek;
1788 5568 : if (state.dataEnvrn->HolidayIndex > 0) {
1789 5568 : CurDayType = state.dataEnvrn->HolidayIndex;
1790 : }
1791 33408 : WriteTimeStampFormatData(state,
1792 5568 : state.files.mtr,
1793 : ReportFreq::EachCall,
1794 5568 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
1795 5568 : state.dataGlobal->DayOfSimChr,
1796 5568 : PrintTimeStamp && PrintTimeStampToSQL,
1797 5568 : state.dataEnvrn->Month,
1798 5568 : state.dataEnvrn->DayOfMonth,
1799 5568 : state.dataGlobal->HourOfDay,
1800 : EndMinute,
1801 : StartMinute,
1802 5568 : state.dataEnvrn->DSTIndicator,
1803 5568 : Sched::dayTypeNames[CurDayType]);
1804 5568 : if (rfMetersTS.dataFrameEnabled()) {
1805 5568 : rfMetersTS.newRow(
1806 5568 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, EndMinute, state.dataGlobal->CalendarYear);
1807 : }
1808 5568 : PrintTimeStamp = false;
1809 5568 : PrintTimeStampToSQL = false;
1810 : }
1811 :
1812 25824 : 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 25824 : if (periodTS.Rpt) {
1831 25824 : periodTS.WriteReportData(state, ReportFreq::TimeStep);
1832 25824 : rfMetersTS.pushVariableValue(periodTS.RptNum, periodTS.Value);
1833 : }
1834 :
1835 25824 : 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 51225312 : for (auto *meter : op->meters) {
1842 50729976 : meter->periods[(int)ReportFreq::TimeStep].Value = 0.0;
1843 495336 : }
1844 495336 : } // ReportTSMeters()
1845 :
1846 111448 : 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 111448 : auto &op = state.dataOutputProcessor;
1864 111448 : auto &rf = state.dataResultsFramework->resultsFramework;
1865 111448 : auto &rfMeters = rf->Meters[(int)freq];
1866 :
1867 111448 : assert(freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
1868 : freq == ReportFreq::Simulation);
1869 :
1870 111448 : if (!rfMeters.dataFrameEnabled()) {
1871 101992 : rf->initializeMeters(op->meters, freq);
1872 : }
1873 :
1874 111448 : PrintTimeStamp = true;
1875 11931984 : for (auto *meter : op->meters) {
1876 11820536 : auto &period = meter->periods[(int)freq];
1877 :
1878 11820536 : if (freq == ReportFreq::Simulation) {
1879 218478 : meter->periodLastSM.Value = period.Value;
1880 218478 : meter->periodLastSM.MinVal = period.MinVal;
1881 218478 : meter->periodLastSM.MinValDate = period.MinValDate;
1882 218478 : meter->periodLastSM.MaxVal = period.MaxVal;
1883 218478 : meter->periodLastSM.MaxValDate = period.MaxValDate;
1884 : }
1885 :
1886 11820536 : if (!period.Rpt && !period.accRpt) {
1887 11761737 : continue;
1888 : }
1889 58799 : if (PrintTimeStamp) {
1890 10713 : CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
1891 :
1892 10713 : switch (freq) {
1893 :
1894 8280 : case ReportFreq::Hour: {
1895 49680 : WriteTimeStampFormatData(state,
1896 8280 : state.files.mtr,
1897 : freq,
1898 8280 : op->freqStampReportNums[(int)freq],
1899 8280 : state.dataGlobal->DayOfSimChr,
1900 8280 : PrintTimeStamp && PrintTimeStampToSQL,
1901 8280 : state.dataEnvrn->Month,
1902 8280 : state.dataEnvrn->DayOfMonth,
1903 8280 : state.dataGlobal->HourOfDay,
1904 : -1, // EndMinute
1905 : -1, // StartMinute
1906 8280 : state.dataEnvrn->DSTIndicator,
1907 8280 : Sched::dayTypeNames[CurDayType]);
1908 8280 : } 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 1294 : case ReportFreq::Month: {
1927 1294 : WriteTimeStampFormatData(state,
1928 1294 : state.files.mtr,
1929 : freq,
1930 1294 : op->freqStampReportNums[(int)freq],
1931 1294 : state.dataGlobal->DayOfSimChr,
1932 1294 : PrintTimeStamp && PrintTimeStampToSQL,
1933 1294 : state.dataEnvrn->Month);
1934 1294 : } 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 1091 : case ReportFreq::Simulation: {
1945 1091 : WriteTimeStampFormatData(state,
1946 1091 : state.files.mtr,
1947 : freq,
1948 1091 : op->freqStampReportNums[(int)freq],
1949 1091 : state.dataGlobal->DayOfSimChr,
1950 1091 : PrintTimeStamp && PrintTimeStampToSQL);
1951 1091 : } break;
1952 :
1953 0 : default: {
1954 0 : } break;
1955 : } // switch (freq)
1956 :
1957 10713 : if (rfMeters.dataFrameEnabled()) {
1958 10713 : rfMeters.newRow(
1959 10713 : state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
1960 : }
1961 10713 : PrintTimeStamp = false;
1962 10713 : PrintTimeStampToSQL = false;
1963 : }
1964 :
1965 58799 : if (period.Rpt) {
1966 58799 : period.WriteReportData(state, freq);
1967 58799 : rfMeters.pushVariableValue(period.RptNum, period.Value);
1968 58799 : period.Value = 0.0;
1969 :
1970 58799 : if (freq != ReportFreq::Hour) {
1971 18167 : period.MinVal = MinSetValue;
1972 18167 : period.MaxVal = MaxSetValue;
1973 : }
1974 : }
1975 :
1976 58799 : 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 111448 : } // for (meter)
1981 111448 : } // ReportMeters()
1982 :
1983 799 : 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 799 : auto const &op = state.dataOutputProcessor;
2000 :
2001 99850 : for (auto *meter : op->meters) {
2002 99051 : auto &period = meter->periodFinYrSM;
2003 :
2004 99051 : switch (meter->RT_forIPUnits) {
2005 46032 : case RT_IPUnits::Electricity: {
2006 92064 : OutputReportPredefined::PreDefTableEntry(
2007 46032 : state, state.dataOutRptPredefined->pdchEMelecannual, meter->Name, period.Value * Constant::convertJtoGJ);
2008 92064 : OutputReportPredefined::PreDefTableEntry(
2009 46032 : state, state.dataOutRptPredefined->pdchEMelecminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2010 92064 : OutputReportPredefined::PreDefTableEntry(
2011 138096 : state, state.dataOutRptPredefined->pdchEMelecminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2012 92064 : OutputReportPredefined::PreDefTableEntry(
2013 46032 : state, state.dataOutRptPredefined->pdchEMelecmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2014 92064 : OutputReportPredefined::PreDefTableEntry(
2015 138096 : state, state.dataOutRptPredefined->pdchEMelecmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2016 46032 : } break;
2017 :
2018 2825 : case RT_IPUnits::Gas: {
2019 5650 : OutputReportPredefined::PreDefTableEntry(
2020 2825 : state, state.dataOutRptPredefined->pdchEMgasannual, meter->Name, period.Value * Constant::convertJtoGJ);
2021 5650 : OutputReportPredefined::PreDefTableEntry(
2022 2825 : state, state.dataOutRptPredefined->pdchEMgasminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2023 5650 : OutputReportPredefined::PreDefTableEntry(
2024 8475 : state, state.dataOutRptPredefined->pdchEMgasminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2025 5650 : OutputReportPredefined::PreDefTableEntry(
2026 2825 : state, state.dataOutRptPredefined->pdchEMgasmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2027 5650 : OutputReportPredefined::PreDefTableEntry(
2028 8475 : state, state.dataOutRptPredefined->pdchEMgasmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2029 2825 : } break;
2030 :
2031 2018 : case RT_IPUnits::Cooling: {
2032 4036 : OutputReportPredefined::PreDefTableEntry(
2033 2018 : state, state.dataOutRptPredefined->pdchEMcoolannual, meter->Name, period.Value * Constant::convertJtoGJ);
2034 4036 : OutputReportPredefined::PreDefTableEntry(
2035 2018 : state, state.dataOutRptPredefined->pdchEMcoolminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2036 4036 : OutputReportPredefined::PreDefTableEntry(
2037 6054 : state, state.dataOutRptPredefined->pdchEMcoolminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2038 4036 : OutputReportPredefined::PreDefTableEntry(
2039 2018 : state, state.dataOutRptPredefined->pdchEMcoolmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2040 4036 : OutputReportPredefined::PreDefTableEntry(
2041 6054 : state, state.dataOutRptPredefined->pdchEMcoolmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2042 2018 : } break;
2043 :
2044 3574 : case RT_IPUnits::Water: {
2045 3574 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMwaterannual, meter->Name, period.Value);
2046 7148 : OutputReportPredefined::PreDefTableEntry(
2047 3574 : state, state.dataOutRptPredefined->pdchEMwaterminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2048 7148 : OutputReportPredefined::PreDefTableEntry(
2049 10722 : state, state.dataOutRptPredefined->pdchEMwaterminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2050 7148 : OutputReportPredefined::PreDefTableEntry(
2051 3574 : state, state.dataOutRptPredefined->pdchEMwatermaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2052 7148 : OutputReportPredefined::PreDefTableEntry(
2053 10722 : state, state.dataOutRptPredefined->pdchEMwatermaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2054 3574 : } break;
2055 :
2056 5840 : case RT_IPUnits::OtherKG: {
2057 5840 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherKGannual, meter->Name, period.Value);
2058 5840 : OutputReportPredefined::PreDefTableEntry(
2059 5840 : state, state.dataOutRptPredefined->pdchEMotherKGminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2060 11680 : OutputReportPredefined::PreDefTableEntry(
2061 17520 : state, state.dataOutRptPredefined->pdchEMotherKGminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2062 5840 : OutputReportPredefined::PreDefTableEntry(
2063 5840 : state, state.dataOutRptPredefined->pdchEMotherKGmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2064 11680 : OutputReportPredefined::PreDefTableEntry(
2065 17520 : state, state.dataOutRptPredefined->pdchEMotherKGmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2066 5840 : } break;
2067 :
2068 303 : case RT_IPUnits::OtherM3: {
2069 303 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherM3annual, meter->Name, period.Value, 3);
2070 303 : OutputReportPredefined::PreDefTableEntry(
2071 303 : state, state.dataOutRptPredefined->pdchEMotherM3minvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2072 606 : OutputReportPredefined::PreDefTableEntry(
2073 909 : state, state.dataOutRptPredefined->pdchEMotherM3minvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2074 303 : OutputReportPredefined::PreDefTableEntry(
2075 303 : state, state.dataOutRptPredefined->pdchEMotherM3maxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2076 606 : OutputReportPredefined::PreDefTableEntry(
2077 909 : state, state.dataOutRptPredefined->pdchEMotherM3maxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2078 303 : } break;
2079 :
2080 303 : case RT_IPUnits::OtherL: {
2081 303 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherLannual, meter->Name, period.Value, 3);
2082 303 : OutputReportPredefined::PreDefTableEntry(
2083 303 : state, state.dataOutRptPredefined->pdchEMotherLminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
2084 606 : OutputReportPredefined::PreDefTableEntry(
2085 909 : state, state.dataOutRptPredefined->pdchEMotherLminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2086 303 : OutputReportPredefined::PreDefTableEntry(
2087 303 : state, state.dataOutRptPredefined->pdchEMotherLmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
2088 606 : OutputReportPredefined::PreDefTableEntry(
2089 909 : state, state.dataOutRptPredefined->pdchEMotherLmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2090 303 : } break;
2091 :
2092 38156 : default: {
2093 76312 : OutputReportPredefined::PreDefTableEntry(
2094 38156 : state, state.dataOutRptPredefined->pdchEMotherJannual, meter->Name, period.Value * Constant::convertJtoGJ);
2095 76312 : OutputReportPredefined::PreDefTableEntry(
2096 38156 : state, state.dataOutRptPredefined->pdchEMotherJminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
2097 76312 : OutputReportPredefined::PreDefTableEntry(
2098 114468 : state, state.dataOutRptPredefined->pdchEMotherJminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
2099 76312 : OutputReportPredefined::PreDefTableEntry(
2100 38156 : state, state.dataOutRptPredefined->pdchEMotherJmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
2101 76312 : OutputReportPredefined::PreDefTableEntry(
2102 114468 : state, state.dataOutRptPredefined->pdchEMotherJmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
2103 38156 : } break;
2104 : } // switch
2105 799 : } // for (meter)
2106 799 : } // ReportForTabularReports()
2107 :
2108 198102 : 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 198102 : 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 198102 : General::DecodeMonDayHrMin(codedDate, Month, Day, Hour, Minute);
2133 :
2134 198102 : if (Month < 1 || Month > 12) {
2135 0 : return "-";
2136 : }
2137 198102 : if (Day < 1 || Day > 31) {
2138 0 : return "-";
2139 : }
2140 198102 : if (Hour < 1 || Hour > 24) {
2141 0 : return "-";
2142 : }
2143 198102 : if (Minute < 0 || Minute > 60) {
2144 0 : return "-";
2145 : }
2146 :
2147 198102 : --Hour;
2148 198102 : if (Minute == 60) {
2149 25640 : ++Hour;
2150 25640 : Minute = 0;
2151 : }
2152 :
2153 198102 : std::string monthName;
2154 198102 : switch (Month) {
2155 97706 : case 1:
2156 97706 : monthName = "JAN";
2157 97706 : 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 81798 : case 7:
2174 81798 : monthName = "JUL";
2175 81798 : 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 17731 : case 12:
2189 17731 : monthName = "DEC";
2190 17731 : break;
2191 0 : default:
2192 0 : assert(false);
2193 : }
2194 :
2195 198102 : return format(DateFmt, Day, monthName, Hour, Minute);
2196 198102 : }
2197 :
2198 373972 : std::string OutVar::multiplierString() const
2199 : {
2200 362296 : return (ZoneMult == 1 && ZoneListMult == 1)
2201 373972 : ? ""
2202 1459826 : : format(" * {} (Zone Multiplier = {}, Zone List Multiplier = {})", ZoneMult * ZoneListMult, ZoneMult, ZoneListMult);
2203 : }
2204 :
2205 800 : 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 800 : auto &op = state.dataOutputProcessor;
2218 :
2219 184488 : for (auto const *var : op->outVars) {
2220 :
2221 183688 : if (var->meterNums.empty()) {
2222 121660 : continue;
2223 : }
2224 :
2225 62028 : print(state.files.mtd,
2226 : "\n Meters for {},{} [{}]{}\n",
2227 62028 : var->ReportID,
2228 62028 : var->keyColonName,
2229 62028 : Constant::unitNames[(int)var->units],
2230 124056 : var->multiplierString());
2231 :
2232 373972 : for (int const meterNum : var->meterNums) {
2233 311944 : auto const *meter = op->meters[meterNum];
2234 :
2235 311944 : print(state.files.mtd,
2236 : " On{}Meter={} [{}]\n",
2237 623888 : (meter->type == MeterType::Normal) ? "" : "Custom",
2238 311944 : meter->Name,
2239 311944 : Constant::unitNames[(int)meter->units]);
2240 62028 : }
2241 800 : } // for (var)
2242 :
2243 100064 : for (auto const *meter : op->meters) {
2244 :
2245 99264 : print(state.files.mtd, "\n For Meter={} [{}]", meter->Name, Constant::unitNames[(int)meter->units]);
2246 99264 : if (meter->resource != Constant::eResource::Invalid) {
2247 99264 : print(state.files.mtd, ", ResourceType={}", Constant::eResourceNames[(int)meter->resource]);
2248 : }
2249 99264 : if (meter->endUseCat != EndUseCat::Invalid) {
2250 71460 : print(state.files.mtd, ", EndUse={}", endUseCatNames[(int)meter->endUseCat]);
2251 : }
2252 99264 : if (meter->group != Group::Invalid) {
2253 61016 : print(state.files.mtd, ", Group={}", groupNames[(int)meter->group]);
2254 : }
2255 99264 : print(state.files.mtd, ", contents are:\n");
2256 :
2257 99264 : if (meter->type == MeterType::Normal) {
2258 410187 : for (int srcVarNum : meter->srcVarNums) {
2259 311060 : auto const *var = op->outVars[srcVarNum];
2260 311060 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2261 99127 : }
2262 :
2263 137 : } else if (meter->type == MeterType::Custom) {
2264 789 : for (int srcVarNum : meter->srcVarNums) {
2265 693 : auto const *var = op->outVars[srcVarNum];
2266 693 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2267 96 : }
2268 :
2269 41 : } else if (meter->type == MeterType::CustomDec) {
2270 41 : print(state.files.mtd,
2271 : " Values for this meter will be Source Meter={}; but will be decremented by:\n",
2272 41 : op->meters[meter->decMeterNum]->Name);
2273 232 : for (int srcVarNum : meter->srcVarNums) {
2274 191 : auto const *var = op->outVars[srcVarNum];
2275 191 : print(state.files.mtd, " {}{}\n", var->keyColonName, var->multiplierString());
2276 41 : }
2277 : }
2278 800 : } // for (meter)
2279 800 : } // ReportMeterDetails()
2280 :
2281 : // *****************************************************************************
2282 : // End of routines for Energy Meters implementation in EnergyPlus.
2283 : // *****************************************************************************
2284 :
2285 361960 : 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 361960 : std::string reportStr = (reportID == -1) ? "" : std::to_string(reportID);
2315 :
2316 361960 : assert(reportStr.length() + DayOfSimChr.length() + (DayType.length()) + 26 < N_WriteTimeStampFormatData); // Check will fit in stamp size
2317 :
2318 361960 : if (!outputFile.good()) {
2319 0 : return;
2320 : }
2321 :
2322 361960 : auto &sql = state.dataSQLiteProcedures->sqlite;
2323 :
2324 361960 : switch (reportingInterval) {
2325 309730 : case ReportFreq::EachCall:
2326 : case ReportFreq::TimeStep: {
2327 309730 : assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && StartMinute != -1 && EndMinute != -1 && DST != -1 && !DayType.empty());
2328 309730 : 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 309730 : if (writeToSQL && sql) {
2341 36300 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2342 : reportID,
2343 18150 : state.dataGlobal->DayOfSim,
2344 18150 : state.dataEnvrn->CurEnvirNum,
2345 18150 : state.dataGlobal->CalendarYear,
2346 18150 : state.dataEnvrn->CurrentYearIsLeapYear,
2347 : Month,
2348 : DayOfMonth,
2349 : Hour,
2350 : EndMinute,
2351 : StartMinute,
2352 : DST,
2353 : DayType,
2354 18150 : state.dataGlobal->WarmupFlag);
2355 : }
2356 309730 : } break;
2357 :
2358 49368 : case ReportFreq::Hour: {
2359 49368 : assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && DST != -1 && !DayType.empty());
2360 49368 : 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 49368 : 60.0,
2370 : DayType);
2371 49368 : if (writeToSQL && sql) {
2372 10416 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2373 : reportID,
2374 5208 : state.dataGlobal->DayOfSim,
2375 5208 : state.dataEnvrn->CurEnvirNum,
2376 5208 : state.dataGlobal->CalendarYear,
2377 5208 : state.dataEnvrn->CurrentYearIsLeapYear,
2378 : Month,
2379 : DayOfMonth,
2380 : Hour,
2381 : -1, // EndMinute
2382 : -1, // StartMinute
2383 : DST,
2384 : DayType,
2385 5208 : state.dataGlobal->WarmupFlag);
2386 : }
2387 49368 : } 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 1539 : case ReportFreq::Month: {
2410 1539 : assert(Month != -1);
2411 1539 : print<FormatSyntax::FMT>(outputFile, "{},{},{:2d}\n", reportStr, DayOfSimChr, Month);
2412 1539 : if (writeToSQL && sql) {
2413 234 : sql->createSQLiteTimeIndexRecord(reportingInterval,
2414 : reportID,
2415 117 : state.dataGlobal->DayOfSim,
2416 117 : state.dataEnvrn->CurEnvirNum,
2417 117 : state.dataGlobal->CalendarYear,
2418 117 : state.dataEnvrn->CurrentYearIsLeapYear,
2419 : Month);
2420 : }
2421 1539 : } break;
2422 :
2423 1179 : case ReportFreq::Simulation: {
2424 1179 : print<FormatSyntax::FMT>(outputFile, "{},{}\n", reportStr, DayOfSimChr);
2425 1179 : 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 1179 : } 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 361960 : } // 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 61553 : 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 61553 : auto &rf = state.dataResultsFramework->resultsFramework;
2471 61553 : auto &sql = state.dataSQLiteProcedures->sqlite;
2472 :
2473 10 : std::string_view unitsString = (units == Constant::Units::customEMS && !unitNameCustomEMS.empty())
2474 61553 : ? unitNameCustomEMS
2475 61553 : : ((units == Constant::Units::Invalid) ? "" : Constant::unitNames[(int)units]);
2476 :
2477 120944 : std::string schedString = (sched != nullptr) ? sched->Name : "";
2478 :
2479 61553 : if (state.files.eso.good()) {
2480 61553 : print(state.files.eso,
2481 : "{},{},{},{} [{}]{}{}{}\n",
2482 61553 : ReportID,
2483 61553 : reportFreqArbitraryInts[(int)freq],
2484 61553 : key,
2485 61553 : name,
2486 : unitsString,
2487 61553 : reportingFrequencyNoticeStrings[(int)freq],
2488 123106 : !schedString.empty() ? "," : "",
2489 : schedString);
2490 : }
2491 :
2492 61553 : if (freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
2493 23852 : freq == ReportFreq::Simulation) {
2494 37996 : state.dataOutputProcessor->freqTrackingVariables[(int)freq] = true;
2495 : }
2496 :
2497 61553 : if (sql) {
2498 14031 : 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 61553 : rf->addReportVariable(key, name, unitsString, freq);
2503 :
2504 61553 : } // OutVar::WriteReportDictionaryItem()
2505 :
2506 8526 : 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 8526 : auto &rf = state.dataResultsFramework->resultsFramework;
2532 8526 : auto &sql = state.dataSQLiteProcedures->sqlite;
2533 :
2534 8526 : std::string FreqString = std::string(reportingFrequencyNoticeStrings[(int)freq]);
2535 8526 : std::string FreqString2 = FreqString.substr(0, index(FreqString, '['));
2536 :
2537 8526 : const auto print_meter = [&](EnergyPlusData &state, const int frequency) {
2538 11130 : const auto out = [&](InputOutputFile &of) {
2539 11130 : if (of.good()) {
2540 11130 : 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 11124 : print(of, fmt, reportID, frequency, meterName, Constant::unitNames[(int)units], FreqString);
2546 : }
2547 : }
2548 11130 : };
2549 :
2550 8526 : out(state.files.mtr);
2551 8526 : if (!meterFileOnlyFlag) {
2552 2604 : out(state.files.eso);
2553 : }
2554 8526 : };
2555 :
2556 8526 : print_meter(state, reportFreqArbitraryInts[(int)freq]);
2557 :
2558 : static constexpr std::string_view keyedValueStringCum("Cumulative ");
2559 : static constexpr std::string_view keyedValueStringNon;
2560 8526 : std::string_view const keyedValueString(cumulativeMeterFlag ? keyedValueStringCum : keyedValueStringNon);
2561 :
2562 8526 : if (sql) {
2563 3952 : sql->createSQLiteReportDictionaryRecord(
2564 1976 : 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 8526 : rf->addReportMeter(meterName, Constant::unitNames[(int)units], freq);
2569 :
2570 8526 : } // WriteMeterDictionaryItem()
2571 :
2572 1533139 : 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 1533139 : if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) {
2590 978 : return;
2591 : }
2592 :
2593 1532161 : if (!Report || freq != reportFreq || !Stored) {
2594 1497381 : 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 84623 : 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 84623 : auto &sql = state.dataSQLiteProcedures->sqlite;
2666 :
2667 84623 : std::string NumberOut;
2668 :
2669 84623 : if (Value == 0.0) {
2670 28915 : NumberOut = "0.0";
2671 : } else {
2672 : char tmp[128];
2673 55708 : dtoa(Value, tmp);
2674 111416 : NumberOut = std::string(tmp);
2675 : }
2676 :
2677 84623 : if (sql) {
2678 43688 : sql->createSQLiteReportDataRecord(RptNum, Value, freq, MinVal, MinValDate, MaxVal, MaxValDate, state.dataGlobal->MinutesInTimeStep);
2679 : }
2680 :
2681 84623 : if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
2682 66456 : if (state.files.mtr.good()) {
2683 66456 : print(state.files.mtr, "{},{}\n", RptNum, NumberOut);
2684 : }
2685 66456 : ++state.dataGlobal->StdMeterRecordCount;
2686 122640 : if (state.files.eso.good() && !RptFO) {
2687 56184 : print(state.files.eso, "{},{}\n", RptNum, NumberOut);
2688 56184 : ++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 18167 : dtoa(MinVal, minValString);
2695 18167 : dtoa(MaxVal, maxValString);
2696 :
2697 18167 : std::string minDateString = produceDateString(MinValDate, freq);
2698 18167 : std::string maxDateString = produceDateString(MaxValDate, freq);
2699 :
2700 18167 : if (state.files.mtr.good()) {
2701 18167 : print(state.files.mtr, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
2702 : }
2703 :
2704 18167 : ++state.dataGlobal->StdMeterRecordCount;
2705 18167 : if (state.files.eso.good() && !RptFO) {
2706 4714 : print(state.files.eso, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
2707 4714 : ++state.dataGlobal->StdOutputRecordCount;
2708 : }
2709 18167 : }
2710 84623 : } // MeterPeriod::WriteReportData()
2711 :
2712 16628006 : 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 16628006 : auto &sql = state.dataSQLiteProcedures->sqlite;
2732 :
2733 16628006 : if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) {
2734 70848 : return;
2735 : }
2736 :
2737 16557158 : if (sql) {
2738 2121216 : sql->createSQLiteReportDataRecord(reportID, repValue);
2739 : }
2740 :
2741 16557158 : if (state.files.eso.good()) {
2742 : char numericData[129];
2743 16557158 : dtoa(repValue, numericData);
2744 16557158 : 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 8590 : 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 8590 : std::string indexGroup;
2902 :
2903 8590 : if (meter->group != Group::Invalid) {
2904 3173 : indexGroup = groupNames[(int)meter->group];
2905 : } else {
2906 5417 : indexGroup = "Facility";
2907 : }
2908 :
2909 8590 : if (meter->resource != Constant::eResource::Invalid) {
2910 8590 : indexGroup += format(":{}", Constant::eResourceNames[(int)meter->resource]);
2911 : }
2912 :
2913 8590 : if (meter->endUseCat != EndUseCat::Invalid) {
2914 1675 : indexGroup += format(":{}", endUseCatNames[(int)meter->endUseCat]);
2915 : }
2916 :
2917 8590 : if (len(meter->EndUseSub) > 0) {
2918 75 : indexGroup += ":" + meter->EndUseSub;
2919 : }
2920 :
2921 8590 : return indexGroup;
2922 0 : } // DetermineIndexGroupFromMeterGroup()
2923 :
2924 6233 : 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 6233 : auto &op = state.dataOutputProcessor;
2946 :
2947 6233 : if (varType == VariableType::Integer) {
2948 15 : OutVarInt *varInt = dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]);
2949 15 : assert(varInt != nullptr);
2950 15 : *varInt->Which = SetIntVal;
2951 6218 : } else if (varType == VariableType::Real) {
2952 6159 : OutVarReal *varReal = dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]);
2953 6159 : assert(varReal != nullptr);
2954 6159 : *varReal->Which = SetRealVal;
2955 59 : } else if (varType == VariableType::Meter) {
2956 59 : op->meters[keyVarIndex]->CurTSValue = SetRealVal;
2957 : }
2958 6233 : } // 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 6358373 : 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 6358373 : auto &op = state.dataOutputProcessor;
3012 :
3013 6358373 : if (!op->OutputInitialized) {
3014 801 : InitializeOutput(state);
3015 : }
3016 :
3017 6358373 : std::vector<int> reqVarNums;
3018 :
3019 : // Determine whether to Report or not
3020 6358373 : CheckReportVariable(state, name, key, reqVarNums);
3021 6358373 : if (reqVarNums.empty()) {
3022 6300124 : reqVarNums.push_back(-1);
3023 : }
3024 :
3025 : // Is this redundant with CheckReportVariable?
3026 6358373 : bool const ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
3027 :
3028 6296480 : bool OnMeter = (resource != Constant::eResource::Invalid) || (endUseCat != EndUseCat::Invalid) || (!EndUseSub.empty()) ||
3029 12654853 : (group != Group::Invalid) || (!zone.empty()) || (!spaceType.empty());
3030 :
3031 6358373 : 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 6358373 : int ddOutVarNum = AddDDOutVar(state, name, timeStep, store, VariableType::Real, units, customUnitName);
3038 6358373 : auto *ddOutVar = op->ddOutVars[ddOutVarNum];
3039 :
3040 6358373 : ++op->NumOfRVariable_Setup;
3041 :
3042 : // If we add any output variables here at all, the first one will be at this index
3043 6358373 : int firstAddedOutVarNum = (int)op->outVars.size();
3044 :
3045 6358373 : op->NumTotalRVariable += reqVarNums.size();
3046 :
3047 6358373 : if (!OnMeter && !ThisOneOnTheList) {
3048 6180057 : return;
3049 : }
3050 :
3051 178316 : if (store == StoreType::Sum) {
3052 100909 : ++op->NumOfRVariable_Sum;
3053 : }
3054 178316 : if (OnMeter) {
3055 61893 : ++op->NumOfRVariable_Meter;
3056 : }
3057 :
3058 358288 : for (int reqVarNum : reqVarNums) {
3059 :
3060 179972 : ++op->NumOfRVariable;
3061 :
3062 179972 : OutVarReal *var = new OutVarReal;
3063 179972 : op->outVars.push_back(var);
3064 :
3065 : // Link this keyed variable to the dictionary entry
3066 179972 : ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
3067 179972 : var->ddVarNum = ddOutVarNum;
3068 :
3069 179972 : var->varType = VariableType::Real;
3070 179972 : var->timeStepType = timeStep;
3071 179972 : var->storeType = store;
3072 179972 : var->name = name;
3073 179972 : var->nameUC = Util::makeUPPER(var->name);
3074 179972 : var->key = key;
3075 179972 : var->keyUC = Util::makeUPPER(key);
3076 179972 : var->keyColonName = fmt::format("{}:{}", key, name);
3077 179972 : var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
3078 179972 : var->units = units;
3079 179972 : if (units == Constant::Units::customEMS) {
3080 10 : var->unitNameCustomEMS = customUnitName;
3081 : }
3082 179972 : var->freq = freq;
3083 179972 : var->sched = nullptr;
3084 179972 : var->ReportID = ++op->ReportNumberCounter;
3085 179972 : var->Which = &ActualVariable;
3086 179972 : var->ZoneMult = ZoneMult;
3087 179972 : var->ZoneListMult = ZoneListMult;
3088 179972 : var->indexGroupKey = indexGroupKey;
3089 179972 : 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 179972 : if (OnMeter) {
3095 61893 : AttachMeters(state, units, resource, endUseCat, EndUseSub, group, zone, spaceType, firstAddedOutVarNum);
3096 61893 : 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 179972 : if (reqVarNum == -1) {
3101 120067 : continue;
3102 : }
3103 :
3104 59905 : var->Report = true;
3105 :
3106 : // freq != ReportFreq::Hour
3107 59905 : if (freq == ReportFreq::Hour) {
3108 59904 : var->freq = op->reqVars[reqVarNum]->freq;
3109 59904 : var->sched = op->reqVars[reqVarNum]->sched;
3110 : }
3111 :
3112 59905 : var->writeReportDictionaryItem(state);
3113 178316 : }
3114 :
3115 6358373 : } // SetupOutputVariable()
3116 :
3117 272439 : 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 272439 : auto &op = state.dataOutputProcessor;
3147 :
3148 272439 : if (!op->OutputInitialized) {
3149 0 : InitializeOutput(state);
3150 : }
3151 :
3152 : // Determine whether to Report or not
3153 272439 : std::vector<int> reqVarNums;
3154 272439 : CheckReportVariable(state, name, key, reqVarNums);
3155 272439 : if (reqVarNums.empty()) {
3156 270811 : reqVarNums.push_back(-1);
3157 : }
3158 :
3159 : // DataOutputs::OutputVariablesForSimulation is case-insentitive
3160 272439 : int ddOutVarNum = AddDDOutVar(state, name, timeStepType, storeType, VariableType::Integer, units);
3161 272439 : auto *ddOutVar = op->ddOutVars[ddOutVarNum];
3162 :
3163 272439 : ++op->NumOfIVariable_Setup;
3164 :
3165 272439 : op->NumTotalIVariable += (!reqVarNums.empty()) ? reqVarNums.size() : 1;
3166 272439 : bool ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
3167 272439 : if (!ThisOneOnTheList) {
3168 268572 : return;
3169 : }
3170 :
3171 3867 : if (storeType == StoreType::Sum) {
3172 145 : ++op->NumOfIVariable_Sum;
3173 : }
3174 :
3175 7754 : for (int reqVarNum : reqVarNums) {
3176 :
3177 3887 : ++op->NumOfIVariable;
3178 :
3179 3887 : OutVarInt *var = new OutVarInt;
3180 3887 : op->outVars.push_back(var);
3181 : // Add to ddVar key list
3182 3887 : ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
3183 :
3184 3887 : var->varType = VariableType::Integer;
3185 3887 : var->timeStepType = timeStepType;
3186 3887 : var->storeType = storeType;
3187 3887 : var->name = name;
3188 3887 : var->nameUC = Util::makeUPPER(var->name);
3189 3887 : var->key = key;
3190 3887 : var->keyUC = Util::makeUPPER(key);
3191 3887 : var->keyColonName = fmt::format("{}:{}", key, name);
3192 3887 : var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
3193 3887 : var->units = units;
3194 3887 : var->ReportID = ++op->ReportNumberCounter;
3195 3887 : var->Which = &ActualVariable;
3196 3887 : var->indexGroupKey = -1;
3197 :
3198 3887 : if (reqVarNum == -1) {
3199 2239 : continue;
3200 : }
3201 :
3202 1648 : var->Report = true;
3203 :
3204 1648 : if (freq != ReportFreq::Hour) {
3205 0 : var->freq = freq;
3206 0 : var->sched = nullptr;
3207 : } else {
3208 1648 : var->freq = op->reqVars[reqVarNum]->freq;
3209 1648 : var->sched = op->reqVars[reqVarNum]->sched;
3210 : }
3211 :
3212 1648 : var->writeReportDictionaryItem(state);
3213 3867 : }
3214 272439 : } // SetOutputVariable()
3215 :
3216 1123599 : 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 1123599 : bool TimePrint(true); // True if the time needs to be printed
3236 1123599 : bool EndTimeStepFlag(false); // True when it's the end of the Zone Time Step
3237 1123599 : auto &op = state.dataOutputProcessor;
3238 1123599 : auto &rf = state.dataResultsFramework->resultsFramework;
3239 :
3240 1123599 : 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 1123599 : Real64 StartMinute = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // StartMinute for UpdateData call
3246 1123599 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute += (*op->TimeValue[(int)t_TimeStepTypeKey].TimeStep) * 60.0;
3247 1751862 : if (t_TimeStepTypeKey == TimeStepType::System &&
3248 628263 : (op->TimeValue[(int)TimeStepType::System].CurMinute == op->TimeValue[(int)TimeStepType::Zone].CurMinute)) {
3249 0 : EndTimeStepFlag = true;
3250 1123599 : } else if (t_TimeStepTypeKey == TimeStepType::Zone) {
3251 495336 : EndTimeStepFlag = true;
3252 : } else {
3253 628263 : EndTimeStepFlag = false;
3254 : }
3255 1123599 : Real64 MinuteNow = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // What minute it is now
3256 :
3257 : int MDHM; // Month,Day,Hour,Minute
3258 1123599 : EncodeMonDayHrMin(MDHM, state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, int(MinuteNow));
3259 1123599 : TimePrint = true;
3260 :
3261 1123599 : Real64 rxTime = (MinuteNow - StartMinute) /
3262 1123599 : double(state.dataGlobal->MinutesInTimeStep); // (MinuteNow-StartMinute)/REAL(MinutesPerTimeStep,r64) - for execution time
3263 :
3264 1123599 : 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 1123599 : 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 181722536 : for (auto *var : op->outVars) {
3281 180598937 : if (var->timeStepType != t_TimeStepTypeKey) {
3282 85565871 : continue;
3283 : }
3284 :
3285 95033066 : Real64 value = (var->varType == VariableType::Real) ? *(dynamic_cast<OutVarReal *>(var))->Which : *(dynamic_cast<OutVarInt *>(var))->Which;
3286 :
3287 95033066 : var->Stored = true;
3288 :
3289 95033066 : if (var->storeType == StoreType::Average) {
3290 46260103 : Real64 CurVal = value * rxTime;
3291 : // TODO: Is this correct? Integer logic is different
3292 46260103 : if (var->varType == VariableType::Real) {
3293 44568817 : if (value > var->MaxValue) {
3294 2547734 : var->MaxValue = value;
3295 2547734 : var->maxValueDate = MDHM;
3296 : }
3297 44568817 : if (value < var->MinValue) {
3298 1673028 : var->MinValue = value;
3299 1673028 : var->minValueDate = MDHM;
3300 : }
3301 : } else { // var->varType == VariableType::Integer
3302 1691286 : if (CurVal > var->MaxValue) {
3303 5397 : var->MaxValue = CurVal;
3304 5397 : var->maxValueDate = MDHM;
3305 : }
3306 1691286 : if (CurVal < var->MinValue) {
3307 4913 : var->MinValue = CurVal;
3308 4913 : var->minValueDate = MDHM;
3309 : }
3310 : }
3311 46260103 : var->TSValue += CurVal;
3312 46260103 : var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
3313 : } else {
3314 48772963 : if (value > var->MaxValue) {
3315 1457797 : var->MaxValue = value;
3316 1457797 : var->maxValueDate = MDHM;
3317 : }
3318 48772963 : if (value < var->MinValue) {
3319 183422 : var->MinValue = value;
3320 183422 : var->minValueDate = MDHM;
3321 : }
3322 48772963 : var->TSValue += value;
3323 48772963 : var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
3324 : }
3325 :
3326 : // End of "record keeping" Report if applicable
3327 95033066 : if (!var->Report) {
3328 49805314 : continue;
3329 : }
3330 :
3331 45227752 : if (var->sched != nullptr && var->sched->getCurrentVal() == 0.0) {
3332 145316 : continue;
3333 : }
3334 :
3335 45082436 : var->tsStored = true;
3336 45082436 : if (!var->thisTSStored) {
3337 34966448 : ++var->thisTSCount;
3338 34966448 : var->thisTSStored = true;
3339 : }
3340 :
3341 45082436 : if (var->freq != ReportFreq::EachCall) {
3342 44458226 : continue;
3343 : }
3344 :
3345 624210 : if (TimePrint) {
3346 25050 : 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 19342 : int CurDayType = state.dataEnvrn->DayOfWeek;
3349 19342 : if (state.dataEnvrn->HolidayIndex > 0) {
3350 19342 : CurDayType = state.dataEnvrn->HolidayIndex;
3351 : }
3352 116052 : WriteTimeStampFormatData(state,
3353 19342 : state.files.eso,
3354 : ReportFreq::EachCall,
3355 19342 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3356 19342 : state.dataGlobal->DayOfSimChr,
3357 : true,
3358 19342 : state.dataEnvrn->Month,
3359 19342 : state.dataEnvrn->DayOfMonth,
3360 19342 : state.dataGlobal->HourOfDay,
3361 19342 : op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
3362 : StartMinute,
3363 19342 : state.dataEnvrn->DSTIndicator,
3364 19342 : Sched::dayTypeNames[CurDayType]);
3365 19342 : op->LHourP = state.dataGlobal->HourOfDay;
3366 19342 : op->LStartMin = StartMinute;
3367 19342 : op->LEndMin = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute;
3368 : }
3369 22196 : TimePrint = false;
3370 : }
3371 :
3372 624210 : WriteNumericData(state, var->ReportID, value);
3373 624210 : ++state.dataGlobal->StdOutputRecordCount;
3374 :
3375 624210 : if (rf->timeSeriesEnabled()) {
3376 12448 : rf->detailedTSData[(int)t_TimeStepTypeKey].pushVariableValue(var->ReportID, value);
3377 : }
3378 1123599 : } // for (var)
3379 :
3380 1123599 : if (t_TimeStepTypeKey == TimeStepType::System) {
3381 1119281 : 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 495336 : if (EndTimeStepFlag) {
3387 495336 : 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 495336 : if (op->meterValues.capacity() > 0) {
3400 51225312 : for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
3401 50729976 : auto *meter = op->meters[iMeter];
3402 50729976 : if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
3403 194325648 : for (int srcVarNum : meter->srcVarNums) {
3404 143673192 : auto *var = op->outVars[srcVarNum];
3405 : // Separate the Zone variables from the HVAC variables using TimeStepType
3406 143673192 : 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 143673192 : op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
3411 50652456 : }
3412 50729976 : } else if (meter->type == MeterType::CustomDec) {
3413 77520 : auto *decMeter = op->meters[meter->decMeterNum];
3414 888048 : for (int srcVarNum : decMeter->srcVarNums) {
3415 810528 : auto *var = op->outVars[srcVarNum];
3416 810528 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3417 0 : continue;
3418 : }
3419 810528 : op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
3420 77520 : }
3421 463008 : for (int srcVarNum : meter->srcVarNums) {
3422 385488 : auto *var = op->outVars[srcVarNum];
3423 385488 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3424 0 : continue;
3425 : }
3426 385488 : op->meterValues[iMeter] -= var->TSValue * var->ZoneMult * var->ZoneListMult;
3427 77520 : }
3428 : } else {
3429 0 : assert(false);
3430 : }
3431 : } // for (iMeter)
3432 : } // if (op->meterValues.capacity() > 0)
3433 :
3434 75195528 : for (auto *var : op->outVars) {
3435 74700192 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3436 0 : continue;
3437 : }
3438 :
3439 74700192 : bool ReportNow = true;
3440 74700192 : if (var->sched != nullptr) {
3441 699552 : ReportNow = (var->sched->getCurrentVal() != 0.0); // SetReportNow(RVar%SchedPtr)
3442 : }
3443 74700192 : if (!ReportNow || !var->Report) {
3444 39733744 : var->TSValue = 0.0;
3445 : }
3446 : // IF (RVar%StoreType == AveragedVar) THEN
3447 : // RVar%Value=RVar%Value+RVar%TSValue/NumOfTimeStepInHour
3448 : // ELSE
3449 74700192 : var->Value += var->TSValue;
3450 : // ENDIF
3451 :
3452 74700192 : if (!ReportNow || !var->Report) {
3453 39733744 : continue;
3454 : }
3455 :
3456 34966448 : if (var->freq == ReportFreq::TimeStep) {
3457 13170384 : if (TimePrint) {
3458 284940 : if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
3459 60 : std::abs(op->LEndMin - op->TimeValue[(int)var->timeStepType].CurMinute) > 0.001) {
3460 284820 : int CurDayType = state.dataEnvrn->DayOfWeek;
3461 284820 : if (state.dataEnvrn->HolidayIndex > 0) {
3462 102228 : CurDayType = state.dataEnvrn->HolidayIndex;
3463 : }
3464 1708920 : WriteTimeStampFormatData(state,
3465 284820 : state.files.eso,
3466 : ReportFreq::EachCall,
3467 284820 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3468 284820 : state.dataGlobal->DayOfSimChr,
3469 : true,
3470 284820 : state.dataEnvrn->Month,
3471 284820 : state.dataEnvrn->DayOfMonth,
3472 284820 : state.dataGlobal->HourOfDay,
3473 284820 : op->TimeValue[(int)var->timeStepType].CurMinute,
3474 : StartMinute,
3475 284820 : state.dataEnvrn->DSTIndicator,
3476 284820 : Sched::dayTypeNames[CurDayType]);
3477 284820 : op->LHourP = state.dataGlobal->HourOfDay;
3478 284820 : op->LStartMin = StartMinute;
3479 284820 : op->LEndMin = op->TimeValue[(int)var->timeStepType].CurMinute;
3480 : }
3481 284880 : TimePrint = false;
3482 : } // if (TimePrint)
3483 :
3484 13170384 : WriteNumericData(state, var->ReportID, var->TSValue);
3485 13170384 : ++state.dataGlobal->StdOutputRecordCount;
3486 :
3487 13170384 : if (rf->timeSeriesEnabled()) {
3488 7488 : rf->freqTSData[(int)ReportFreq::TimeStep].pushVariableValue(var->ReportID, var->TSValue);
3489 : }
3490 : }
3491 34966448 : var->TSValue = 0.0;
3492 34966448 : var->thisTSStored = false;
3493 495336 : } // for (var)
3494 :
3495 495336 : UpdateMeters(state, MDHM);
3496 :
3497 495336 : ReportTSMeters(state, StartMinute, op->TimeValue[(int)TimeStepType::Zone].CurMinute, TimePrint, TimePrint);
3498 :
3499 : } // TimeStep Block
3500 :
3501 : // Hour Block
3502 495336 : if (state.dataGlobal->EndHourFlag) {
3503 103632 : if (op->freqTrackingVariables[(int)ReportFreq::Hour]) {
3504 41088 : int CurDayType = state.dataEnvrn->DayOfWeek;
3505 41088 : if (state.dataEnvrn->HolidayIndex > 0) {
3506 23400 : CurDayType = state.dataEnvrn->HolidayIndex;
3507 : }
3508 205440 : WriteTimeStampFormatData(state,
3509 41088 : state.files.eso,
3510 : ReportFreq::Hour,
3511 41088 : op->freqStampReportNums[(int)ReportFreq::TimeStep],
3512 41088 : state.dataGlobal->DayOfSimChr,
3513 : true,
3514 41088 : state.dataEnvrn->Month,
3515 41088 : state.dataEnvrn->DayOfMonth,
3516 41088 : state.dataGlobal->HourOfDay,
3517 : -1, // EndMinute
3518 : -1, // startMinute
3519 41088 : state.dataEnvrn->DSTIndicator,
3520 41088 : Sched::dayTypeNames[CurDayType]);
3521 41088 : TimePrint = false;
3522 : }
3523 :
3524 103632 : 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 103632 : op->TimeValue[(int)TimeStepType::Zone].CurMinute = 0.0;
3533 103632 : op->TimeValue[(int)TimeStepType::System].CurMinute = 0.0;
3534 :
3535 16025832 : for (auto *var : op->outVars) {
3536 :
3537 15922200 : 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 15922200 : if (var->tsStored) {
3547 6837038 : if (var->storeType == StoreType::Average) {
3548 6051422 : var->Value /= double(var->thisTSCount);
3549 : }
3550 6837038 : if (var->Report && var->freq == ReportFreq::Hour && var->Stored) {
3551 2833412 : WriteNumericData(state, var->ReportID, var->Value);
3552 2833412 : ++state.dataGlobal->StdOutputRecordCount;
3553 2833412 : var->Stored = false;
3554 : // add time series value for hourly to data store
3555 2833412 : if (rf->timeSeriesEnabled()) {
3556 20892 : rf->freqTSData[(int)ReportFreq::Hour].pushVariableValue(var->ReportID, var->Value);
3557 : }
3558 : }
3559 6837038 : var->StoreValue += var->Value;
3560 6837038 : ++var->NumStored;
3561 : }
3562 15922200 : var->tsStored = false;
3563 15922200 : var->thisTSStored = false;
3564 15922200 : var->thisTSCount = 0;
3565 15922200 : var->Value = 0.0;
3566 103632 : } // for (var)
3567 :
3568 103632 : ReportMeters(state, ReportFreq::Hour, TimePrint);
3569 : } // Hour Block
3570 :
3571 495336 : if (!state.dataGlobal->EndHourFlag) {
3572 391704 : return;
3573 : }
3574 :
3575 : // Day Block
3576 103632 : if (state.dataGlobal->EndDayFlag) {
3577 4318 : 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 4318 : 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 4318 : op->NumHoursInMonth += 24;
3606 667743 : for (auto *var : op->outVars) {
3607 663425 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3608 0 : continue;
3609 : }
3610 663425 : var->writeOutput(state, ReportFreq::Day);
3611 4318 : }
3612 :
3613 4318 : ReportMeters(state, ReportFreq::Day, TimePrint);
3614 :
3615 : } // Day Block
3616 :
3617 : // Only continue if EndDayFlag is set
3618 103632 : if (!state.dataGlobal->EndDayFlag) {
3619 99314 : return;
3620 : }
3621 :
3622 : // Month Block
3623 4318 : if (state.dataEnvrn->EndMonthFlag || state.dataGlobal->EndEnvrnFlag) {
3624 1785 : if (op->freqTrackingVariables[(int)ReportFreq::Month]) {
3625 245 : WriteTimeStampFormatData(state,
3626 245 : state.files.eso,
3627 : ReportFreq::Month,
3628 245 : op->freqStampReportNums[(int)ReportFreq::Month],
3629 245 : state.dataGlobal->DayOfSimChr,
3630 : true,
3631 245 : state.dataEnvrn->Month);
3632 245 : TimePrint = false;
3633 : }
3634 :
3635 1785 : 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 1785 : op->NumHoursInSim += op->NumHoursInMonth;
3644 1785 : state.dataEnvrn->EndMonthFlag = false;
3645 439847 : for (auto *var : op->outVars) {
3646 438062 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3647 0 : continue;
3648 : }
3649 438062 : var->writeOutput(state, ReportFreq::Month);
3650 1785 : }
3651 :
3652 1785 : ReportMeters(state, ReportFreq::Month, TimePrint);
3653 :
3654 1785 : op->NumHoursInMonth = 0;
3655 : } // Month Block
3656 :
3657 : // Sim/Environment Block
3658 4318 : if (state.dataGlobal->EndEnvrnFlag) {
3659 1706 : 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 1706 : 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 432730 : for (auto *var : op->outVars) {
3678 431024 : if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) {
3679 0 : continue;
3680 : }
3681 431024 : var->writeOutput(state, ReportFreq::Simulation);
3682 1706 : }
3683 :
3684 1706 : ReportMeters(state, ReportFreq::Simulation, TimePrint);
3685 :
3686 1706 : op->NumHoursInSim = 0;
3687 : }
3688 :
3689 : // Yearly Block
3690 4318 : 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 799 : 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 799 : 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 22585 : for (auto *reqVar : op->reqVars) {
3750 21786 : if (reqVar->Used) {
3751 21707 : continue;
3752 : }
3753 79 : if (reqVar->key.empty()) {
3754 66 : reqVar->key = "*";
3755 : }
3756 79 : 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 79 : if (!state.dataOutputProcessor->Rept) {
3764 38 : ShowWarningMessage(state, "The following Report Variables were requested but not generated -- check.rdd file");
3765 38 : ShowContinueError(state, "Either the IDF did not contain these elements, the variable name is misspelled,");
3766 38 : ShowContinueError(state,
3767 : "or the requested variable is an advanced output which requires Output : Diagnostics, DisplayAdvancedReportVariables;");
3768 19 : state.dataOutputProcessor->Rept = true;
3769 : }
3770 79 : ShowMessage(state, format("Key={}, VarName={}, Frequency={}", reqVar->key, reqVar->name, localReportFreqNames[(int)reqVar->freq]));
3771 799 : }
3772 799 : } // GenOutputVariablesAuditReport()
3773 :
3774 800 : 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 800 : Array1D_string Alphas(2);
3834 800 : Array1D<Real64> Numbers(1);
3835 : int NumAlpha;
3836 : int NumNumbers;
3837 : int IOStat;
3838 : int NumReqMeters;
3839 : int NumReqMeterFOs;
3840 :
3841 800 : bool ErrorsFound(false); // If errors detected in input
3842 800 : auto const &op = state.dataOutputProcessor;
3843 800 : auto &ipsc = state.dataIPShortCut;
3844 :
3845 800 : GetCustomMeterInput(state, ErrorsFound);
3846 800 : 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 6842 : [&state](std::string &name, ReportFreq freq, bool MeterFileOnlyIndicator, bool CumulativeIndicator) -> bool {
3853 6842 : bool result = false;
3854 :
3855 6842 : size_t varnameLen = index(name, '[');
3856 6842 : if (varnameLen != std::string::npos) {
3857 0 : name.erase(varnameLen);
3858 : }
3859 :
3860 6842 : auto const &op = state.dataOutputProcessor;
3861 :
3862 6842 : std::string::size_type wildCardPosition = index(name, '*');
3863 :
3864 6842 : if (wildCardPosition == std::string::npos) {
3865 6818 : if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
3866 6755 : SetInitialMeterReportingAndOutputNames(state, found->second, MeterFileOnlyIndicator, freq, CumulativeIndicator);
3867 6755 : result = true;
3868 6818 : }
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 6842 : return result;
3880 800 : };
3881 :
3882 800 : ipsc->cCurrentModuleObject = "Output:Meter";
3883 800 : NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3884 :
3885 1669 : for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
3886 :
3887 2607 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3888 869 : ipsc->cCurrentModuleObject,
3889 : Loop,
3890 : Alphas,
3891 : NumAlpha,
3892 : Numbers,
3893 : NumNumbers,
3894 : IOStat,
3895 869 : ipsc->lNumericFieldBlanks,
3896 869 : ipsc->lAlphaFieldBlanks,
3897 869 : ipsc->cAlphaFieldNames,
3898 869 : ipsc->cNumericFieldNames);
3899 :
3900 869 : bool meterFileOnlyIndicator = false;
3901 869 : bool cumulativeIndicator = false;
3902 869 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3903 :
3904 869 : 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 800 : ipsc->cCurrentModuleObject = "Output:Meter:MeterFileOnly";
3910 800 : NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3911 6768 : for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
3912 :
3913 17904 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3914 5968 : ipsc->cCurrentModuleObject,
3915 : Loop,
3916 : Alphas,
3917 : NumAlpha,
3918 : Numbers,
3919 : NumNumbers,
3920 : IOStat,
3921 5968 : ipsc->lNumericFieldBlanks,
3922 5968 : ipsc->lAlphaFieldBlanks,
3923 5968 : ipsc->cAlphaFieldNames,
3924 5968 : ipsc->cNumericFieldNames);
3925 :
3926 5968 : bool meterFileOnlyIndicator = true;
3927 5968 : bool cumulativeIndicator = false;
3928 5968 : ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
3929 5968 : 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 800 : ipsc->cCurrentModuleObject = "Output:Meter:Cumulative";
3935 800 : NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3936 :
3937 801 : 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 800 : ipsc->cCurrentModuleObject = "Output:Meter:Cumulative:MeterFileOnly";
3961 800 : NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3962 804 : 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 800 : ReportMeterDetails(state);
3986 :
3987 800 : if (op->ErrorsLogged) {
3988 0 : ShowFatalError(state, "UpdateMeterReporting: Previous Meter Specification errors cause program termination.");
3989 : }
3990 :
3991 800 : op->meterValues.resize(op->meters.size(), 0.0);
3992 800 : std::fill(op->meterValues.begin(), op->meterValues.end(), 0.0);
3993 800 : } // UpdateMeterReporting()
3994 :
3995 6939 : 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 6939 : auto &op = state.dataOutputProcessor;
4015 :
4016 6939 : auto *meter = op->meters[WhichMeter];
4017 6939 : auto &period = meter->periods[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq];
4018 6939 : if (!CumulativeIndicator) {
4019 6934 : 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 6934 : if (!period.Rpt) {
4028 6929 : period.Rpt = true;
4029 6929 : if (MeterFileOnlyIndicator) {
4030 5918 : period.RptFO = true;
4031 : } else {
4032 1011 : op->freqTrackingVariables[(int)freq] = true;
4033 : }
4034 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4035 6929 : meter->indexGroup = DetermineIndexGroupFromMeterGroup(meter);
4036 6929 : WriteMeterDictionaryItem(
4037 6929 : 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 6939 : } // SetInitialMeterReportingAndOutputNames()
4061 :
4062 369464 : 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 369464 : auto const &op = state.dataOutputProcessor;
4075 :
4076 369464 : auto found = op->meterMap.find(name);
4077 738928 : return (found != op->meterMap.end()) ? found->second : -1;
4078 369464 : } // 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 13968795 : 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 13968795 : return (MeterNumber != -1) ? state.dataOutputProcessor->meters[MeterNumber]->CurTSValue : 0.0;
4104 : } // GetCurrentMeterValue()
4105 :
4106 278422174 : 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 278422174 : Real64 InstantMeterValue = 0.0;
4124 :
4125 278422174 : if (meterNum == -1) {
4126 211543442 : return InstantMeterValue;
4127 : }
4128 :
4129 66878732 : auto const &op = state.dataOutputProcessor;
4130 66878732 : auto *meter = op->meters[meterNum];
4131 :
4132 66878732 : if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
4133 1968963115 : for (int srcVarNum : meter->srcVarNums) {
4134 1902212525 : auto *var = op->outVars[srcVarNum];
4135 : // Separate the Zone variables from the HVAC variables using TimeStepType
4136 1902212525 : if (var->timeStepType != timeStepType) {
4137 949158877 : continue;
4138 : }
4139 :
4140 953053648 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4141 953053648 : assert(rVar != nullptr);
4142 : // Add to the total all of the appropriate variables
4143 953053648 : InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4144 66750590 : }
4145 :
4146 66878732 : } else if (meter->type == MeterType::CustomDec) {
4147 128142 : auto *decMeter = op->meters[meter->decMeterNum];
4148 4307992 : for (int srcVarNum : decMeter->srcVarNums) {
4149 4179850 : auto *var = op->outVars[srcVarNum];
4150 4179850 : if (var->timeStepType != timeStepType) {
4151 2089925 : continue;
4152 : }
4153 2089925 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4154 2089925 : assert(rVar != nullptr);
4155 2089925 : InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4156 128142 : }
4157 484860 : for (int srcVarNum : meter->srcVarNums) {
4158 356718 : auto *var = op->outVars[srcVarNum];
4159 356718 : if (var->timeStepType != timeStepType) {
4160 178359 : continue;
4161 : }
4162 178359 : auto *rVar = dynamic_cast<OutVarReal *>(var);
4163 178359 : assert(rVar != nullptr);
4164 178359 : InstantMeterValue -= (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
4165 128142 : }
4166 : } else {
4167 0 : assert(false);
4168 : }
4169 :
4170 66878732 : return InstantMeterValue;
4171 : } // GetInstantMeterValue()
4172 :
4173 114971684 : 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 114971684 : auto const &op = state.dataOutputProcessor;
4200 :
4201 : // Select based on variable type: integer, real, or meter
4202 114971684 : if (varType == VariableType::Invalid) { // Variable not a found variable
4203 12312733 : resultVal = 0.0;
4204 102658951 : } else if (varType == VariableType::Integer || varType == VariableType::Real) {
4205 101825042 : 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 203625594 : resultVal = (varType == VariableType::Integer) ? (double)*(dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]))->Which
4212 101800552 : : (double)*(dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]))->Which;
4213 833909 : } else if (varType == VariableType::Meter) {
4214 833909 : 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 114971684 : 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 35689 : 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 35689 : int NumVariables = 0;
4288 35689 : auto const &op = state.dataOutputProcessor;
4289 :
4290 42701920 : for (auto *var : op->outVars) {
4291 : // Pos=INDEX(RVariableTypes(Loop)%VarName,':')
4292 : // IF (ComponentName /= RVariableTypes(Loop)%VarNameUC(1:Pos-1)) CYCLE
4293 42666231 : if (var->varType != OutputProcessor::VariableType::Real) {
4294 890664 : continue;
4295 : }
4296 41775567 : if (ComponentName != var->keyUC) {
4297 41707746 : continue;
4298 : }
4299 67821 : if (!var->meterNums.empty()) {
4300 42042 : ++NumVariables;
4301 : }
4302 35689 : }
4303 35689 : return NumVariables;
4304 : } // GetNumMeteredVariables()
4305 :
4306 18534 : 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 18534 : auto &op = state.dataOutputProcessor;
4328 :
4329 18534 : NumVariables = 0;
4330 :
4331 24844313 : 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 24825779 : auto *var = op->outVars[iVar];
4336 24825779 : if (var->varType != VariableType::Real) {
4337 503765 : continue;
4338 : }
4339 24322014 : if (ComponentName != var->keyUC) {
4340 24256562 : continue;
4341 : }
4342 65452 : if (var->meterNums.empty()) {
4343 23410 : continue;
4344 : }
4345 :
4346 42042 : auto &meteredVar = meteredVars(++NumVariables);
4347 :
4348 42042 : meteredVar.num = iVar;
4349 42042 : meteredVar.varType = VariableType::Real;
4350 42042 : meteredVar.timeStepType = var->timeStepType;
4351 42042 : meteredVar.units = var->units;
4352 :
4353 42042 : meteredVar.resource = op->meters[var->meterNums[0]]->resource;
4354 42042 : meteredVar.name = var->keyColonNameUC;
4355 :
4356 42042 : bool foundEndUse = false;
4357 42042 : bool foundGroup = false;
4358 126126 : for (int meterNum : var->meterNums) {
4359 126094 : auto *meter = op->meters[meterNum];
4360 126094 : if (!foundEndUse && meter->endUseCat != EndUseCat::Invalid) {
4361 42010 : meteredVar.endUseCat = meter->endUseCat;
4362 42010 : foundEndUse = true;
4363 : }
4364 :
4365 126094 : if (!foundGroup && meter->group != Group::Invalid) {
4366 42042 : meteredVar.group = meter->group;
4367 42042 : foundGroup = true;
4368 : }
4369 :
4370 126094 : if (foundEndUse && foundGroup) {
4371 42010 : break;
4372 : }
4373 42042 : }
4374 :
4375 42042 : meteredVar.rptNum = var->ReportID;
4376 : }
4377 18534 : return NumVariables;
4378 : } // GetMeteredVariables()
4379 :
4380 58811 : 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 58811 : auto const &op = state.dataOutputProcessor;
4417 :
4418 58811 : varType = VariableType::Invalid;
4419 58811 : numKeys = 0;
4420 58811 : storeType = StoreType::Average;
4421 58811 : timeStepType = TimeStepType::Zone;
4422 58811 : units = Constant::Units::None; // Why is this None and not Invalid?
4423 :
4424 58811 : std::string nameUC = Util::makeUPPER(name);
4425 :
4426 : // Search Variable List First
4427 58811 : if (auto found = op->ddOutVarMap.find(nameUC); found != op->ddOutVarMap.end()) {
4428 1855 : auto const *ddOutVar = op->ddOutVars[found->second];
4429 1855 : varType = ddOutVar->variableType;
4430 1855 : storeType = ddOutVar->storeType;
4431 1855 : timeStepType = ddOutVar->timeStepType;
4432 1855 : units = ddOutVar->units;
4433 1855 : numKeys = ddOutVar->keyOutVarNums.size();
4434 :
4435 56956 : } 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 303 : int meterNum = found2->second;
4440 303 : numKeys = 1;
4441 303 : varType = VariableType::Meter;
4442 303 : units = op->meters[meterNum]->units;
4443 303 : storeType = StoreType::Sum;
4444 303 : 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 56653 : auto *sched = Sched::GetSchedule(state, nameUC);
4450 56653 : 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 115767 : }
4461 58811 : } // GetVariableKeyCountandType()
4462 :
4463 2158 : 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 2158 : std::string nameUC = Util::makeUPPER(varName);
4494 :
4495 : // Select based on variable type: integer, real, or meter
4496 2158 : if (varType == VariableType::Integer || varType == VariableType::Real) {
4497 1855 : auto const &op = state.dataOutputProcessor;
4498 1855 : auto found = op->ddOutVarMap.find(nameUC);
4499 1855 : if (found == op->ddOutVarMap.end()) {
4500 0 : return;
4501 : }
4502 :
4503 1855 : auto const *ddOutVar = op->ddOutVars[found->second];
4504 :
4505 1855 : if (ddOutVar->keyOutVarNums.size() > size(keyOutVarNums)) {
4506 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4507 : }
4508 :
4509 1855 : int iKey = 0;
4510 147541 : for (int keyOutVarNum : ddOutVar->keyOutVarNums) {
4511 145686 : ++iKey;
4512 145686 : keyOutVarNums(iKey) = keyOutVarNum;
4513 145686 : keyNames(iKey) = op->outVars[keyOutVarNum]->keyUC;
4514 1855 : }
4515 :
4516 4013 : } else if (varType == VariableType::Meter) { // Meter
4517 :
4518 303 : if (size(keyOutVarNums) == 0) {
4519 0 : ShowFatalError(state, "Invalid array size in GetVariableKeys");
4520 : }
4521 303 : keyOutVarNums(1) = GetMeterIndex(state, varName);
4522 303 : 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 2158 : } // GetVariableKeys()
4535 :
4536 8428 : 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 8428 : auto const &op = state.dataOutputProcessor;
4549 :
4550 8428 : std::string name = Util::makeUPPER(RepVarName);
4551 :
4552 237816 : for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
4553 229388 : if (op->reqVars[iReqVar]->name == name) {
4554 0 : return true;
4555 : }
4556 : }
4557 :
4558 8428 : if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
4559 1447 : auto const *meter = op->meters[found->second];
4560 10114 : for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
4561 8670 : if (iFreq == (int)ReportFreq::Year) {
4562 1444 : continue;
4563 : }
4564 7226 : auto const &period = meter->periods[iFreq];
4565 7226 : if (period.Rpt || period.RptFO || period.accRpt || period.accRptFO) {
4566 3 : return true;
4567 : }
4568 : }
4569 8428 : }
4570 :
4571 8425 : return false;
4572 8428 : } // ReportingThisVariable()
4573 :
4574 61 : 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 61 : auto &op = state.dataOutputProcessor;
4628 :
4629 2989 : for (int iResource = 0; iResource < (int)Constant::eResource::Num; ++iResource) {
4630 2928 : std::string meterName = format("{}:Facility", Constant::eResourceNames[iResource]);
4631 2928 : std::string meterNameUC = Util::makeUPPER(meterName);
4632 :
4633 2928 : auto found = op->meterMap.find(meterNameUC);
4634 2928 : if (found == op->meterMap.end()) {
4635 1272 : continue;
4636 : }
4637 :
4638 1656 : auto *meter = op->meters[found->second];
4639 1656 : auto &period = meter->periods[(int)freq];
4640 :
4641 : // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
4642 1656 : 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 1656 : if (!period.Rpt) {
4647 1592 : period.Rpt = true;
4648 1592 : WriteMeterDictionaryItem(state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, false);
4649 1592 : op->freqTrackingVariables[(int)freq] = true;
4650 : }
4651 5472 : }
4652 61 : } // InitPollutionMeterReporting()
4653 :
4654 800 : 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 800 : std::string VarOption1;
4670 800 : std::string VarOption2;
4671 : bool DoReport;
4672 : bool SortByName;
4673 :
4674 800 : auto const &op = state.dataOutputProcessor;
4675 800 : auto const &rf = state.dataResultsFramework->resultsFramework;
4676 :
4677 : // See if Report Variables should be turned on
4678 800 : SortByName = false;
4679 2400 : ScanForReports(state, "VariableDictionary", DoReport, _, VarOption1, VarOption2);
4680 : // IF (.not. DoReport) RETURN
4681 :
4682 800 : if (DoReport) {
4683 786 : op->ProduceReportVDD = ReportVDD::Yes;
4684 1572 : if (VarOption1 == std::string("IDF")) {
4685 268 : op->ProduceReportVDD = ReportVDD::IDF;
4686 : }
4687 786 : if (!VarOption2.empty()) {
4688 74 : if (Util::SameString(VarOption2, "Name") || Util::SameString(VarOption2, "AscendingName")) {
4689 3 : SortByName = true;
4690 : }
4691 : }
4692 : }
4693 :
4694 1600 : state.files.rdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.rdd);
4695 1600 : state.files.mdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.mdd);
4696 800 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4697 518 : print(state.files.rdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4698 518 : print(state.files.rdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
4699 :
4700 518 : print(state.files.mdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4701 518 : print(state.files.mdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
4702 282 : } else if (op->ProduceReportVDD == ReportVDD::IDF) {
4703 268 : print(state.files.rdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4704 268 : print(state.files.rdd, "! Output:Variable Objects (applicable to this run){}", '\n');
4705 :
4706 268 : print(state.files.mdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
4707 268 : print(state.files.mdd, "! Output:Meter Objects (applicable to this run){}", '\n');
4708 : }
4709 :
4710 800 : if (op->ProduceReportVDD == ReportVDD::Yes || op->ProduceReportVDD == ReportVDD::IDF) {
4711 :
4712 786 : auto miVar = op->ddOutVarMap.begin();
4713 786 : int aiVar = 0;
4714 : for (;;) {
4715 462654 : int iVar = -1;
4716 : // Too complicated to do this logic in the for loop header
4717 462654 : if (SortByName) {
4718 1222 : if (miVar == op->ddOutVarMap.end()) {
4719 3 : break;
4720 : }
4721 1219 : iVar = miVar->second;
4722 1219 : ++miVar;
4723 : } else {
4724 461432 : if (aiVar == (int)op->ddOutVars.size()) {
4725 783 : break;
4726 : }
4727 460649 : iVar = aiVar;
4728 460649 : ++aiVar;
4729 : }
4730 :
4731 461868 : auto *ddVar = op->ddOutVars[iVar];
4732 :
4733 461868 : if (ddVar->ReportedOnDDFile) {
4734 0 : continue;
4735 : }
4736 :
4737 : static constexpr std::array<std::string_view, (int)TimeStepType::Num> timeStepNamesLocal = {"Zone", "HVAC"};
4738 461868 : std::string_view timeStepName = timeStepNamesLocal[(int)ddVar->timeStepType];
4739 461868 : std::string_view storeTypeName = storeTypeNames[(int)ddVar->storeType];
4740 461868 : std::string_view varName = ddVar->name;
4741 : std::string_view unitName =
4742 461868 : (ddVar->units == Constant::Units::customEMS) ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)ddVar->units];
4743 461868 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4744 298543 : print(state.files.rdd, "{},{},{} [{}]\n", timeStepName, storeTypeName, varName, unitName);
4745 298543 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4746 : } else {
4747 163325 : print(state.files.rdd, "Output:Variable,*,{},hourly; !- {} {} [{}]\n", varName, timeStepName, storeTypeName, unitName);
4748 163325 : rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
4749 : }
4750 :
4751 461868 : ddVar->ReportedOnDDFile = true;
4752 461868 : 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 461868 : } // for (aiVar, miVar)
4771 786 : } // if (produceReportVDD)
4772 800 : state.files.rdd.close();
4773 :
4774 800 : auto miMeter = op->meterMap.begin();
4775 100064 : for (int aiMeter = 0; aiMeter < (int)op->meters.size() && miMeter != op->meterMap.end(); ++aiMeter, ++miMeter) {
4776 99264 : int iMeter = (SortByName) ? miMeter->second : aiMeter;
4777 99264 : auto *meter = op->meters[iMeter];
4778 99264 : std::string_view unitName = Constant::unitNames[(int)meter->units];
4779 99264 : if (op->ProduceReportVDD == ReportVDD::Yes) {
4780 47519 : print(state.files.mdd, "Zone,Meter,{} [{}]\n", meter->Name, unitName);
4781 47519 : rf->MDD.push_back(format("Zone,Meter,{} [{}]", meter->Name, unitName));
4782 51745 : } else if (op->ProduceReportVDD == ReportVDD::IDF) {
4783 50198 : print(state.files.mdd, "Output:Meter,{},hourly; !- [{}]\n", meter->Name, unitName);
4784 50198 : rf->MDD.push_back(format("Output:Meter,{} [{}]", meter->Name, unitName));
4785 50198 : print(state.files.mdd, "Output:Meter:Cumulative,{},hourly; !- [{}]\n", meter->Name, unitName);
4786 50198 : rf->MDD.push_back(format("Output:Meter:Cumulative,{} [{}]", meter->Name, unitName));
4787 : }
4788 : }
4789 800 : state.files.mdd.close();
4790 800 : } // ProduceRDDMDD()
4791 :
4792 6630812 : 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 6630812 : auto const &op = state.dataOutputProcessor;
4815 :
4816 6630812 : std::string nameUC = Util::makeUPPER(name);
4817 :
4818 6630812 : auto found = op->ddOutVarMap.find(nameUC);
4819 6630812 : if (found == op->ddOutVarMap.end()) {
4820 470640 : auto *ddVar = new DDOutVar();
4821 470640 : op->ddOutVars.push_back(ddVar);
4822 : // Add to map
4823 470640 : op->ddOutVarMap.insert_or_assign(nameUC, op->ddOutVars.size() - 1);
4824 :
4825 470640 : ddVar->timeStepType = timeStepType;
4826 470640 : ddVar->storeType = storeType;
4827 470640 : ddVar->variableType = variableType;
4828 470640 : ddVar->name = name;
4829 470640 : ddVar->units = units;
4830 470640 : if (!customUnitName.empty() && units == Constant::Units::customEMS) {
4831 10 : ddVar->unitNameCustomEMS = customUnitName;
4832 : }
4833 470640 : return op->ddOutVars.size() - 1;
4834 :
4835 6160172 : } else if (units == op->ddOutVars[found->second]->units) {
4836 6160166 : 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 6630812 : } // AddDDOutVar()
4869 :
4870 801 : int initErrorFile(EnergyPlusData &state)
4871 : {
4872 801 : state.files.err_stream = std::make_unique<std::ofstream>(state.files.outputErrFilePath);
4873 801 : 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 801 : return EXIT_SUCCESS;
4878 : } // initErrorFile()
4879 :
4880 : } // namespace EnergyPlus
|