Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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 <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Fmath.hh>
53 : #include <ObjexxFCL/string.functions.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/CostEstimateManager.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataGlobalConstants.hh>
59 : #include <EnergyPlus/DataIPShortCuts.hh>
60 : #include <EnergyPlus/DisplayRoutines.hh>
61 : #include <EnergyPlus/EconomicLifeCycleCost.hh>
62 : #include <EnergyPlus/EconomicTariff.hh>
63 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
64 : #include <EnergyPlus/OutputReportTabular.hh>
65 : #include <EnergyPlus/ResultsFramework.hh>
66 : #include <EnergyPlus/SQLiteProcedures.hh>
67 : #include <EnergyPlus/UtilityRoutines.hh>
68 :
69 : namespace EnergyPlus::EconomicLifeCycleCost {
70 :
71 : // Module containing the routines dealing with the EconomicLifeCycleCost
72 :
73 : // MODULE INFORMATION:
74 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
75 : // DATE WRITTEN May 2010
76 : // MODIFIED na
77 : // RE-ENGINEERED na
78 :
79 : // PURPOSE OF THIS MODULE:
80 : // To compute life-cycle cost measures such as present value based
81 : // on input provided by the user as well as calculated energy costs.
82 :
83 : // METHODOLOGY EMPLOYED:
84 : // Uses NIST Handbook 135 "Life-Cycle Costing Manual for the Federal
85 : // Energy Management Program" for most computations.
86 :
87 : // REFERENCES:
88 : // To compute the net present value for all costs entered in the
89 : // LifeCycleCosts objects, the algorithms from NIST Handbook 135
90 : // "Life-Cycle Costing Manual for the Federal Energy Management
91 : // Program" will be used as the primary source. Supplemental sources
92 : // of algorithms will be derived from ASTM E833-09a "Standard
93 : // Terminology of Building Economics", ASTM E917-05 "Standard
94 : // Practice for Measuring Life-Cycle Cost of Buildings and Building
95 : // Systems", and "Engineering Economic Analysis, Ninth Edition", by
96 : // Donald Newnan, Ted Eschenback, and Jerome Lavelle.
97 :
98 : // OTHER NOTES:
99 : // na
100 :
101 : // Using/Aliasing
102 : using namespace DataGlobalConstants;
103 :
104 : // Functions
105 :
106 769 : void GetInputForLifeCycleCost(EnergyPlusData &state)
107 : {
108 : // SUBROUTINE INFORMATION:
109 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
110 : // DATE WRITTEN May 2010
111 : // MODIFIED na
112 : // RE-ENGINEERED na
113 :
114 : // PURPOSE OF THIS SUBROUTINE:
115 : // Read the input file for "LifeCycleCost:Parameters" object.
116 :
117 : // Using/Aliasing
118 : using OutputReportTabular::AddTOCEntry;
119 :
120 769 : auto &elcc(state.dataEconLifeCycleCost);
121 :
122 769 : if (elcc->GetInput_GetLifeCycleCostInput) {
123 769 : GetInputLifeCycleCostParameters(state);
124 769 : GetInputLifeCycleCostRecurringCosts(state);
125 769 : GetInputLifeCycleCostNonrecurringCost(state);
126 769 : GetInputLifeCycleCostUsePriceEscalation(state);
127 769 : GetInputLifeCycleCostUseAdjustment(state);
128 769 : elcc->GetInput_GetLifeCycleCostInput = false;
129 : }
130 769 : }
131 :
132 769 : void ComputeLifeCycleCostAndReport(EnergyPlusData &state)
133 : {
134 : // SUBROUTINE INFORMATION:
135 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
136 : // DATE WRITTEN May 2010
137 : // MODIFIED na
138 : // RE-ENGINEERED na
139 :
140 : // PURPOSE OF THIS SUBROUTINE:
141 : // Perform the life cycle cost computations and write report.
142 :
143 769 : if (state.dataEconLifeCycleCost->LCCparamPresent) {
144 1 : DisplayString(state, "Computing Life Cycle Costs and Reporting");
145 1 : ExpressAsCashFlows(state);
146 1 : ComputePresentValue(state);
147 1 : ComputeEscalatedEnergyCosts(state);
148 1 : ComputeTaxAndDepreciation(state);
149 1 : WriteTabularLifeCycleCostReport(state);
150 : }
151 769 : }
152 :
153 : //======================================================================================================================
154 : //======================================================================================================================
155 :
156 : // GET INPUT ROUTINES
157 :
158 : //======================================================================================================================
159 : //======================================================================================================================
160 :
161 769 : void GetInputLifeCycleCostParameters(EnergyPlusData &state)
162 : {
163 : // SUBROUTINE INFORMATION:
164 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
165 : // DATE WRITTEN May 2010
166 :
167 : // PURPOSE OF THIS SUBROUTINE:
168 : // Read the input file for "LifeCycleCost:Parameters" object.
169 :
170 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
171 :
172 : int jFld; // loop counter
173 : int NumFields; // Total number of elements
174 : int NumAlphas; // Number of elements in the alpha array
175 : int NumNums; // Number of elements in the numeric array
176 1538 : Array1D_string AlphaArray; // character string data
177 1538 : Array1D<Real64> NumArray; // numeric data
178 : int IOStat; // IO Status when calling get input subroutine
179 1538 : std::string CurrentModuleObject; // for ease in renaming.
180 : int NumObj; // count of objects
181 :
182 769 : CurrentModuleObject = "LifeCycleCost:Parameters";
183 769 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
184 769 : NumArray.allocate(NumNums);
185 769 : AlphaArray.allocate(NumAlphas);
186 769 : NumObj = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
187 :
188 769 : auto &elcc(state.dataEconLifeCycleCost);
189 :
190 769 : if (NumObj == 0) {
191 768 : elcc->LCCparamPresent = false;
192 1 : } else if (NumObj == 1) {
193 1 : elcc->LCCparamPresent = true;
194 5 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
195 : CurrentModuleObject,
196 : 1,
197 : AlphaArray,
198 : NumAlphas,
199 : NumArray,
200 : NumNums,
201 : IOStat,
202 1 : state.dataIPShortCut->lNumericFieldBlanks,
203 1 : state.dataIPShortCut->lAlphaFieldBlanks,
204 1 : state.dataIPShortCut->cAlphaFieldNames,
205 1 : state.dataIPShortCut->cNumericFieldNames);
206 : // check to make sure none of the values are another life cycle cost object
207 7 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
208 6 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
209 0 : ShowWarningError(state,
210 0 : "In " + CurrentModuleObject + " named " + AlphaArray(1) +
211 : " a field was found containing LifeCycleCost: which may indicate a missing comma.");
212 : }
213 : }
214 : // start to extract values from input array into appropriate fields
215 : // A1, \field Name
216 : // \required-field
217 : // \type alpha
218 1 : elcc->LCCname = AlphaArray(1);
219 : // A2, \field Discounting Convention
220 : // \type choice
221 : // \key EndOfYear
222 : // \key MidYear
223 : // \key BeginningOfYear
224 : // \default EndOfYear
225 1 : elcc->discountConvention = static_cast<DiscConv>(getEnumerationValue(DiscConvNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(2))));
226 1 : if (elcc->discountConvention == DiscConv::Invalid) {
227 0 : elcc->discountConvention = DiscConv::EndOfYear;
228 0 : ShowWarningError(state,
229 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + "=\"" + AlphaArray(2) +
230 : "\". EndOfYear will be used.");
231 : }
232 : // A3, \field Inflation Approach
233 : // \type choice
234 : // \key ConstantDollar
235 : // \key CurrentDollar
236 : // \default ConstantDollar
237 1 : elcc->inflationApproach = static_cast<InflAppr>(getEnumerationValue(InflApprNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(3))));
238 1 : if (elcc->inflationApproach == InflAppr::Invalid) {
239 0 : elcc->inflationApproach = InflAppr::ConstantDollar;
240 0 : ShowWarningError(state,
241 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + "=\"" + AlphaArray(3) +
242 : "\". ConstantDollar will be used.");
243 : }
244 : // N1, \field Real Discount Rate
245 : // \type real
246 1 : elcc->realDiscountRate = NumArray(1);
247 1 : if ((elcc->inflationApproach == InflAppr::ConstantDollar) && state.dataIPShortCut->lNumericFieldBlanks(1)) {
248 0 : ShowWarningError(state,
249 0 : CurrentModuleObject + ": Invalid for field " + state.dataIPShortCut->cNumericFieldNames(1) +
250 : " to be blank when ConstantDollar analysis is be used.");
251 : }
252 1 : if ((elcc->realDiscountRate > 0.30) || (elcc->realDiscountRate < -0.30)) {
253 0 : ShowWarningError(state,
254 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(1) +
255 : ". This value is the decimal value not a percentage so most values are between 0.02 and 0.15. ");
256 : }
257 : // N2, \field Nominal Discount Rate
258 : // \type real
259 1 : elcc->nominalDiscountRate = NumArray(2);
260 1 : if ((elcc->inflationApproach == InflAppr::CurrentDollar) && state.dataIPShortCut->lNumericFieldBlanks(2)) {
261 0 : ShowWarningError(state,
262 0 : CurrentModuleObject + ": Invalid for field " + state.dataIPShortCut->cNumericFieldNames(2) +
263 : " to be blank when CurrentDollar analysis is be used.");
264 : }
265 1 : if ((elcc->nominalDiscountRate > 0.30) || (elcc->nominalDiscountRate < -0.30)) {
266 0 : ShowWarningError(state,
267 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(2) +
268 : ". This value is the decimal value not a percentage so most values are between 0.02 and 0.15. ");
269 : }
270 : // N3, \field Inflation
271 : // \type real
272 1 : elcc->inflation = NumArray(3);
273 1 : if ((elcc->inflationApproach == InflAppr::ConstantDollar) && (!state.dataIPShortCut->lNumericFieldBlanks(3))) {
274 0 : ShowWarningError(state,
275 0 : CurrentModuleObject + ": Invalid for field " + state.dataIPShortCut->cNumericFieldNames(3) +
276 : " contain a value when ConstantDollar analysis is be used.");
277 : }
278 1 : if ((elcc->inflation > 0.30) || (elcc->inflation < -0.30)) {
279 0 : ShowWarningError(state,
280 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(3) +
281 : ". This value is the decimal value not a percentage so most values are between 0.02 and 0.15. ");
282 : }
283 : // A4, \field Base Date Month
284 : // \type choice
285 : // \key January
286 : // \key February
287 : // \key March
288 : // \key April
289 : // \key May
290 : // \key June
291 : // \key July
292 : // \key August
293 : // \key September
294 : // \key October
295 : // \key November
296 : // \key December
297 : // \default January
298 1 : elcc->baseDateMonth = getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(4)));
299 1 : if (elcc->baseDateMonth == -1) {
300 0 : elcc->baseDateMonth = 0;
301 0 : ShowWarningError(state,
302 0 : CurrentModuleObject + ": Invalid month entered in field " + state.dataIPShortCut->cAlphaFieldNames(4) +
303 0 : ". Using January instead of \"" + AlphaArray(4) + "\"");
304 : }
305 : // N4, \field Base Date Year
306 : // \type integer
307 : // \minimum 1900
308 : // \maximum 2100
309 1 : elcc->baseDateYear = int(NumArray(4));
310 1 : if (elcc->baseDateYear > 2100) {
311 0 : ShowWarningError(state,
312 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(4) +
313 : ". Value greater than 2100 yet it is representing a year. ");
314 : }
315 1 : if (elcc->baseDateYear < 1900) {
316 0 : ShowWarningError(state,
317 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(4) +
318 : ". Value less than 1900 yet it is representing a year. ");
319 : }
320 : // A5, \field Service Date Month
321 : // \type choice
322 : // \key January
323 : // \key February
324 : // \key March
325 : // \key April
326 : // \key May
327 : // \key June
328 : // \key July
329 : // \key August
330 : // \key September
331 : // \key October
332 : // \key November
333 : // \key December
334 : // \default January
335 1 : elcc->serviceDateMonth = getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(5)));
336 1 : if (elcc->serviceDateMonth == -1) {
337 0 : elcc->serviceDateMonth = 0;
338 0 : ShowWarningError(state,
339 0 : CurrentModuleObject + ": Invalid month entered in field " + state.dataIPShortCut->cAlphaFieldNames(5) +
340 0 : ". Using January instead of \"" + AlphaArray(5) + "\"");
341 : }
342 : // N5, \field Service Date Year
343 : // \type integer
344 : // \minimum 1900
345 : // \maximum 2100
346 1 : elcc->serviceDateYear = int(NumArray(5));
347 1 : if (elcc->serviceDateYear > 2100) {
348 0 : ShowWarningError(state,
349 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(5) +
350 : ". Value greater than 2100 yet it is representing a year. ");
351 : }
352 1 : if (elcc->serviceDateYear < 1900) {
353 0 : ShowWarningError(state,
354 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(5) +
355 : ". Value less than 1900 yet it is representing a year. ");
356 : }
357 : // N6, \field Length of Study Period in Years
358 : // \type integer
359 : // \minimum 1
360 : // \maximum 100
361 1 : elcc->lengthStudyYears = int(NumArray(6));
362 1 : if (elcc->lengthStudyYears > 100) {
363 0 : ShowWarningError(state,
364 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(6) +
365 : ". A value greater than 100 is not reasonable for an economic evaluation. ");
366 : }
367 1 : if (elcc->lengthStudyYears < 1) {
368 0 : ShowWarningError(state,
369 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(6) +
370 : ". A value less than 1 is not reasonable for an economic evaluation. ");
371 : }
372 1 : elcc->lengthStudyTotalMonths = elcc->lengthStudyYears * 12;
373 : // N7, \field Tax rate
374 : // \type real
375 : // \minimum 0.0
376 1 : elcc->taxRate = NumArray(7);
377 1 : if (elcc->taxRate < 0.0 && (!state.dataIPShortCut->lNumericFieldBlanks(7))) {
378 0 : ShowWarningError(state,
379 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(10) +
380 : ". A value less than 0 is not reasonable for a tax rate. ");
381 : }
382 : // A6; \field Depreciation Method
383 : // \type choice
384 : // \key ModifiedAcceleratedCostRecoverySystem-3year
385 : // \key ModifiedAcceleratedCostRecoverySystem-5year
386 : // \key ModifiedAcceleratedCostRecoverySystem-7year
387 : // \key ModifiedAcceleratedCostRecoverySystem-10year
388 : // \key ModifiedAcceleratedCostRecoverySystem-15year
389 : // \key ModifiedAcceleratedCostRecoverySystem-20year
390 : // \key StraightLine-27year
391 : // \key StraightLine-31year
392 : // \key StraightLine-39year
393 : // \key StraightLine-40year
394 : // \key None
395 : // \default None
396 1 : elcc->depreciationMethod = static_cast<DeprMethod>(getEnumerationValue(DeprMethodNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(6))));
397 1 : if (elcc->depreciationMethod == DeprMethod::Invalid) {
398 0 : elcc->depreciationMethod = DeprMethod::None;
399 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(6)) {
400 0 : ShowWarningError(state,
401 0 : CurrentModuleObject + ": The input field " + state.dataIPShortCut->cAlphaFieldNames(6) +
402 : "is blank. \"None\" will be used.");
403 : } else {
404 0 : ShowWarningError(state,
405 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(6) + "=\"" + AlphaArray(6) +
406 : R"(". "None" will be used.)");
407 : }
408 : }
409 : // compute derived variables
410 1 : elcc->lastDateYear = elcc->baseDateYear + elcc->lengthStudyYears - 1;
411 : } else {
412 0 : ShowWarningError(state, CurrentModuleObject + ": Only one instance of this object is allowed. No life-cycle cost reports will be generated.");
413 0 : elcc->LCCparamPresent = false;
414 : }
415 769 : }
416 :
417 769 : void GetInputLifeCycleCostRecurringCosts(EnergyPlusData &state)
418 : {
419 : // SUBROUTINE INFORMATION:
420 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
421 : // DATE WRITTEN May 2010
422 :
423 : // PURPOSE OF THIS SUBROUTINE:
424 : // Read the input file for "LifeCycleCost:RecurringCosts" object.
425 :
426 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
427 :
428 : int iInObj; // loop index variable for reading in objects
429 : int jFld; // loop counter
430 : int NumFields; // Total number of elements
431 : int NumAlphas; // Number of elements in the alpha array
432 : int NumNums; // Number of elements in the numeric array
433 770 : Array1D_string AlphaArray; // character string data
434 770 : Array1D<Real64> NumArray; // numeric data
435 : int IOStat; // IO Status when calling get input subroutine
436 770 : std::string CurrentModuleObject; // for ease in renaming.
437 :
438 769 : auto &elcc(state.dataEconLifeCycleCost);
439 :
440 769 : if (!elcc->LCCparamPresent) return;
441 1 : CurrentModuleObject = "LifeCycleCost:RecurringCosts";
442 1 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
443 1 : NumArray.allocate(NumNums);
444 1 : AlphaArray.allocate(NumAlphas);
445 1 : elcc->numRecurringCosts = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
446 1 : elcc->RecurringCosts.resize(elcc->numRecurringCosts);
447 2 : for (iInObj = 0; iInObj < elcc->numRecurringCosts; ++iInObj) {
448 5 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
449 : CurrentModuleObject,
450 : iInObj + 1, // since this index needs to start from 1
451 : AlphaArray,
452 : NumAlphas,
453 : NumArray,
454 : NumNums,
455 : IOStat,
456 1 : state.dataIPShortCut->lNumericFieldBlanks,
457 1 : state.dataIPShortCut->lAlphaFieldBlanks,
458 1 : state.dataIPShortCut->cAlphaFieldNames,
459 1 : state.dataIPShortCut->cNumericFieldNames);
460 : // check to make sure none of the values are another life cycle cost object
461 4 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
462 3 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
463 0 : ShowWarningError(state,
464 0 : "In " + CurrentModuleObject + " named " + AlphaArray(1) +
465 : " a field was found containing LifeCycleCost: which may indicate a missing comma.");
466 : }
467 : }
468 : // start to extract values from input array into appropriate fields
469 : // A1, \field Name
470 : // \required-field
471 : // \type alpha
472 1 : elcc->RecurringCosts[iInObj].name = AlphaArray(1);
473 : // A2, \field Category
474 : // \type choice
475 : // \key Maintenance
476 : // \key Repair
477 : // \key Operation
478 : // \key Replacement
479 : // \key MinorOverhaul
480 : // \key MajorOverhaul
481 : // \key OtherOperational
482 : // \default Maintenance
483 1 : elcc->RecurringCosts[iInObj].category =
484 2 : static_cast<CostCategory>(getEnumerationValue(CostCategoryNamesUCNoSpace, UtilityRoutines::MakeUPPERCase(AlphaArray(2))));
485 1 : bool isNotRecurringCost = BITF_TEST_NONE(BITF(elcc->RecurringCosts[iInObj].category),
486 : BITF(CostCategory::Maintenance) | BITF(CostCategory::Repair) | BITF(CostCategory::Operation) |
487 : BITF(CostCategory::Replacement) | BITF(CostCategory::MinorOverhaul) |
488 : BITF(CostCategory::MajorOverhaul) | BITF(CostCategory::OtherOperational));
489 1 : if (isNotRecurringCost) {
490 0 : elcc->RecurringCosts[iInObj].category = CostCategory::Maintenance;
491 0 : ShowWarningError(state,
492 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + "=\"" + AlphaArray(2) +
493 : "\". The category of Maintenance will be used.");
494 : }
495 : // N1, \field Cost
496 : // \type real
497 1 : elcc->RecurringCosts[iInObj].cost = NumArray(1);
498 : // A3, \field Start of Costs
499 : // \type choice
500 : // \key ServicePeriod
501 : // \key BasePeriod
502 : // \default ServicePeriod
503 1 : elcc->RecurringCosts[iInObj].startOfCosts =
504 2 : static_cast<StartCosts>(getEnumerationValue(StartCostNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(3))));
505 1 : if (elcc->RecurringCosts[iInObj].startOfCosts == StartCosts::Invalid) {
506 0 : elcc->RecurringCosts[iInObj].startOfCosts = StartCosts::ServicePeriod;
507 0 : ShowWarningError(state,
508 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + "=\"" + AlphaArray(3) +
509 : "\". The start of the service period will be used.");
510 : }
511 : // N2, \field Years from Start
512 : // \type integer
513 : // \minimum 0
514 : // \maximum 100
515 1 : elcc->RecurringCosts[iInObj].yearsFromStart = int(NumArray(2));
516 1 : if (elcc->RecurringCosts[iInObj].yearsFromStart > 100) {
517 0 : ShowWarningError(
518 : state,
519 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(2) +
520 : ". This value is the number of years from the start so a value greater than 100 is not reasonable for an economic evaluation. ");
521 : }
522 1 : if (elcc->RecurringCosts[iInObj].yearsFromStart < 0) {
523 0 : ShowWarningError(
524 : state,
525 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(2) +
526 : ". This value is the number of years from the start so a value less than 0 is not reasonable for an economic evaluation. ");
527 : }
528 : // N3, \field Months from Start
529 : // \type integer
530 : // \minimum 0
531 : // \maximum 1200
532 1 : elcc->RecurringCosts[iInObj].monthsFromStart = int(NumArray(3));
533 1 : if (elcc->RecurringCosts[iInObj].monthsFromStart > 1200) {
534 0 : ShowWarningError(state,
535 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(3) +
536 : ". This value is the number of months from the start so a value greater than 1200 is not reasonable for an "
537 : "economic evaluation. ");
538 : }
539 1 : if (elcc->RecurringCosts[iInObj].monthsFromStart < 0) {
540 0 : ShowWarningError(
541 : state,
542 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(3) +
543 : ". This value is the number of months from the start so a value less than 0 is not reasonable for an economic evaluation. ");
544 : }
545 : // N4, \field Repeat Period Years
546 : // \type integer
547 : // \minimum 1
548 : // \maximum 100
549 1 : elcc->RecurringCosts[iInObj].repeatPeriodYears = int(NumArray(4));
550 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodYears > 100) {
551 0 : ShowWarningError(state,
552 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(4) +
553 : ". This value is the number of years between occurrences of the cost so a value greater than 100 is not reasonable "
554 : "for an economic evaluation. ");
555 : }
556 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodYears < 1) {
557 0 : ShowWarningError(state,
558 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(4) +
559 : ". This value is the number of years between occurrences of the cost so a value less than 1 is not reasonable for "
560 : "an economic evaluation. ");
561 : }
562 : // N5, \field Repeat Period Months
563 : // \type integer
564 : // \minimum 0
565 : // \maximum 1200
566 1 : elcc->RecurringCosts[iInObj].repeatPeriodMonths = int(NumArray(5));
567 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodMonths > 1200) {
568 0 : ShowWarningError(state,
569 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(5) +
570 : ". This value is the number of months between occurrences of the cost so a value greater than 1200 is not "
571 : "reasonable for an economic evaluation. ");
572 : }
573 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodMonths < 0) {
574 0 : ShowWarningError(state,
575 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(5) +
576 : ". This value is the number of months between occurrences of the cost so a value less than 0 is not reasonable for "
577 : "an economic evaluation. ");
578 : }
579 1 : if ((elcc->RecurringCosts[iInObj].repeatPeriodMonths == 0) && (elcc->RecurringCosts[iInObj].repeatPeriodYears == 0)) {
580 0 : ShowWarningError(state,
581 0 : CurrentModuleObject + ": Invalid value in fields " + state.dataIPShortCut->cNumericFieldNames(5) + " and " +
582 0 : state.dataIPShortCut->cNumericFieldNames(4) + ". The repeat period must not be zero months and zero years. ");
583 : }
584 : // N6; \field Annual escalation rate
585 : // \type real
586 1 : elcc->RecurringCosts[iInObj].annualEscalationRate = int(NumArray(6));
587 1 : if (elcc->RecurringCosts[iInObj].annualEscalationRate > 0.30) {
588 0 : ShowWarningError(state,
589 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(6) +
590 : ". This value is the decimal value for the annual escalation so most values are between 0.02 and 0.15. ");
591 : }
592 1 : if (elcc->RecurringCosts[iInObj].annualEscalationRate < -0.30) {
593 0 : ShowWarningError(state,
594 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(6) +
595 : ". This value is the decimal value for the annual escalation so most values are between 0.02 and 0.15. ");
596 : }
597 : // express the years and months fields in total months
598 1 : elcc->RecurringCosts[iInObj].totalMonthsFromStart =
599 1 : elcc->RecurringCosts[iInObj].yearsFromStart * 12 + elcc->RecurringCosts[iInObj].monthsFromStart;
600 1 : elcc->RecurringCosts[iInObj].totalRepeatPeriodMonths =
601 1 : elcc->RecurringCosts[iInObj].repeatPeriodYears * 12 + elcc->RecurringCosts[iInObj].repeatPeriodMonths;
602 : }
603 : }
604 :
605 769 : void GetInputLifeCycleCostNonrecurringCost(EnergyPlusData &state)
606 : {
607 : // SUBROUTINE INFORMATION:
608 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
609 : // DATE WRITTEN May 2010
610 :
611 : // PURPOSE OF THIS SUBROUTINE:
612 : // Read the input file for "LifeCycleCost:NonrecurringCost" object.
613 :
614 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
615 :
616 : int iInObj; // loop index variable for reading in objects
617 : int jFld; // loop counter
618 : int NumFields; // Total number of elements
619 : int NumAlphas; // Number of elements in the alpha array
620 : int NumNums; // Number of elements in the numeric array
621 770 : Array1D_string AlphaArray; // character string data
622 770 : Array1D<Real64> NumArray; // numeric data
623 : int IOStat; // IO Status when calling get input subroutine
624 770 : std::string CurrentModuleObject; // for ease in renaming.
625 : int numComponentCostLineItems; // number of ComponentCost:LineItem objects
626 :
627 769 : auto &elcc(state.dataEconLifeCycleCost);
628 :
629 769 : if (!elcc->LCCparamPresent) return;
630 1 : CurrentModuleObject = "LifeCycleCost:NonrecurringCost";
631 1 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
632 1 : NumArray.allocate(NumNums);
633 1 : AlphaArray.allocate(NumAlphas);
634 1 : elcc->numNonrecurringCost = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
635 1 : numComponentCostLineItems = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ComponentCost:LineItem");
636 1 : if (numComponentCostLineItems > 0) { // leave room for component cost total
637 0 : elcc->NonrecurringCost.resize(elcc->numNonrecurringCost + 1); // add a place for CostEstimate total
638 : } else {
639 1 : elcc->NonrecurringCost.resize(elcc->numNonrecurringCost);
640 : }
641 6 : for (iInObj = 0; iInObj < elcc->numNonrecurringCost; ++iInObj) {
642 25 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
643 : CurrentModuleObject,
644 : iInObj + 1, // since this index needs to start from 1
645 : AlphaArray,
646 : NumAlphas,
647 : NumArray,
648 : NumNums,
649 : IOStat,
650 5 : state.dataIPShortCut->lNumericFieldBlanks,
651 5 : state.dataIPShortCut->lAlphaFieldBlanks,
652 5 : state.dataIPShortCut->cAlphaFieldNames,
653 5 : state.dataIPShortCut->cNumericFieldNames);
654 : // check to make sure none of the values are another life cycle cost object
655 20 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
656 15 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
657 0 : ShowWarningError(state,
658 0 : "In " + CurrentModuleObject + " named " + AlphaArray(1) +
659 : " a field was found containing LifeCycleCost: which may indicate a missing comma.");
660 : }
661 : }
662 : // start to extract values from input array into appropriate fields
663 : // A1, \field Name
664 : // \required-field
665 : // \type alpha
666 5 : elcc->NonrecurringCost[iInObj].name = AlphaArray(1);
667 : // A2, \field Category
668 : // \type choice
669 : // \key Construction
670 : // \key Salvage
671 : // \key OtherCapital
672 : // \default Construction
673 5 : elcc->NonrecurringCost[iInObj].category =
674 10 : static_cast<CostCategory>(getEnumerationValue(CostCategoryNamesUCNoSpace, UtilityRoutines::MakeUPPERCase(AlphaArray(2))));
675 : bool isNotNonRecurringCost =
676 5 : BITF_TEST_NONE(BITF(elcc->NonrecurringCost[iInObj].category),
677 : BITF(CostCategory::Construction) | BITF(CostCategory::Salvage) | BITF(CostCategory::OtherCapital));
678 5 : if (isNotNonRecurringCost) {
679 0 : elcc->NonrecurringCost[iInObj].category = CostCategory::Construction;
680 0 : ShowWarningError(state,
681 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + "=\"" + AlphaArray(2) +
682 : "\". The category of Construction will be used.");
683 : }
684 : // N1, \field Cost
685 : // \type real
686 5 : elcc->NonrecurringCost[iInObj].cost = NumArray(1);
687 : // A3, \field Start of Costs
688 : // \type choice
689 : // \key ServicePeriod
690 : // \key BasePeriod
691 : // \default ServicePeriod
692 5 : elcc->NonrecurringCost[iInObj].startOfCosts =
693 10 : static_cast<StartCosts>(getEnumerationValue(StartCostNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(3))));
694 5 : if (elcc->NonrecurringCost[iInObj].startOfCosts == StartCosts::Invalid) {
695 0 : elcc->NonrecurringCost[iInObj].startOfCosts = StartCosts::ServicePeriod;
696 0 : ShowWarningError(state,
697 0 : CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + "=\"" + AlphaArray(3) +
698 : "\". The start of the service period will be used.");
699 : }
700 : // N2, \field Years from Start
701 : // \type integer
702 : // \minimum 0
703 : // \maximum 100
704 5 : elcc->NonrecurringCost[iInObj].yearsFromStart = int(NumArray(2));
705 5 : if (elcc->NonrecurringCost[iInObj].yearsFromStart > 100) {
706 0 : ShowWarningError(
707 : state,
708 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(2) +
709 : ". This value is the number of years from the start so a value greater than 100 is not reasonable for an economic evaluation. ");
710 : }
711 5 : if (elcc->NonrecurringCost[iInObj].yearsFromStart < 0) {
712 0 : ShowWarningError(
713 : state,
714 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(2) +
715 : ". This value is the number of years from the start so a value less than 0 is not reasonable for an economic evaluation. ");
716 : }
717 : // N3; \field Months from Start
718 : // \type integer
719 : // \minimum 0
720 : // \maximum 11
721 5 : elcc->NonrecurringCost[iInObj].monthsFromStart = int(NumArray(3));
722 5 : if (elcc->NonrecurringCost[iInObj].monthsFromStart > 1200) {
723 0 : ShowWarningError(state,
724 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(3) +
725 : ". This value is the number of months from the start so a value greater than 1200 is not reasonable for an "
726 : "economic evaluation. ");
727 : }
728 5 : if (elcc->NonrecurringCost[iInObj].monthsFromStart < 0) {
729 0 : ShowWarningError(
730 : state,
731 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(3) +
732 : ". This value is the number of months from the start so a value less than 0 is not reasonable for an economic evaluation. ");
733 : }
734 : // express the years and months fields in total months
735 5 : elcc->NonrecurringCost[iInObj].totalMonthsFromStart =
736 5 : elcc->NonrecurringCost[iInObj].yearsFromStart * 12 + elcc->NonrecurringCost[iInObj].monthsFromStart;
737 : }
738 : }
739 :
740 769 : void GetInputLifeCycleCostUsePriceEscalation(EnergyPlusData &state)
741 : {
742 : // SUBROUTINE INFORMATION:
743 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
744 : // DATE WRITTEN May 2010
745 :
746 : // PURPOSE OF THIS SUBROUTINE:
747 : // Read the input file for "LifeCycleCost:UsePriceEscalation" object.
748 :
749 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
750 :
751 : int iInObj; // loop index variable for reading in objects
752 : int jFld; // loop counter
753 : int jYear; // loop counter
754 : int NumFields; // Total number of elements
755 : int NumAlphas; // Number of elements in the alpha array
756 : int NumNums; // Number of elements in the numeric array
757 770 : Array1D_string AlphaArray; // character string data
758 770 : Array1D<Real64> NumArray; // numeric data
759 : int IOStat; // IO Status when calling get input subroutine
760 770 : std::string CurrentModuleObject; // for ease in renaming.
761 :
762 769 : auto &elcc(state.dataEconLifeCycleCost);
763 :
764 769 : if (!elcc->LCCparamPresent) return;
765 1 : CurrentModuleObject = "LifeCycleCost:UsePriceEscalation";
766 1 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
767 1 : NumArray.allocate(NumNums);
768 1 : AlphaArray.allocate(NumAlphas);
769 1 : elcc->numUsePriceEscalation = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
770 1 : elcc->UsePriceEscalation.allocate(elcc->numUsePriceEscalation);
771 4 : for (iInObj = 1; iInObj <= elcc->numUsePriceEscalation; ++iInObj) {
772 3 : elcc->UsePriceEscalation(iInObj).Escalation.allocate(elcc->lengthStudyYears);
773 : }
774 1 : if (elcc->numUsePriceEscalation > 0) {
775 4 : for (iInObj = 1; iInObj <= elcc->numUsePriceEscalation; ++iInObj) {
776 15 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
777 : CurrentModuleObject,
778 : iInObj,
779 : AlphaArray,
780 : NumAlphas,
781 : NumArray,
782 : NumNums,
783 : IOStat,
784 3 : state.dataIPShortCut->lNumericFieldBlanks,
785 3 : state.dataIPShortCut->lAlphaFieldBlanks,
786 3 : state.dataIPShortCut->cAlphaFieldNames,
787 3 : state.dataIPShortCut->cNumericFieldNames);
788 : // check to make sure none of the values are another life cycle cost object
789 12 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
790 9 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
791 0 : ShowWarningError(state,
792 0 : "In " + CurrentModuleObject + " named " + AlphaArray(1) +
793 : " a field was found containing LifeCycleCost: which may indicate a missing comma.");
794 : }
795 : }
796 : // start to extract values from input array into appropriate fields
797 : // A1, \field Name
798 : // \required-field
799 : // \type alpha
800 3 : elcc->UsePriceEscalation(iInObj).name = AlphaArray(1);
801 : // A2, \field Resource
802 : // \required-field
803 : // \type choice
804 : // \key Electricity
805 : // \key NaturalGas
806 : // \key Steam
807 : // \key Gasoline
808 : // \key Diesel
809 : // \key Coal
810 : // \key FuelOilNo1
811 : // \key FuelOilNo2
812 : // \key Propane
813 : // \key Water
814 : // \key OtherFuel1
815 : // \key OtherFuel2
816 3 : elcc->UsePriceEscalation(iInObj).resource = AssignResourceTypeNum(AlphaArray(2)); // use function from DataGlobalConstants
817 3 : if (NumAlphas > 3) {
818 0 : ShowWarningError(state, "In " + CurrentModuleObject + " contains more alpha fields than expected.");
819 : }
820 : // N1, \field Escalation Start Year
821 : // \type integer
822 : // \minimum 1900
823 : // \maximum 2100
824 3 : elcc->UsePriceEscalation(iInObj).escalationStartYear = int(NumArray(1));
825 3 : if (elcc->UsePriceEscalation(iInObj).escalationStartYear > 2100) {
826 0 : ShowWarningError(state,
827 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(1) +
828 : ". Value greater than 2100 yet it is representing a year. ");
829 : }
830 3 : if (elcc->UsePriceEscalation(iInObj).escalationStartYear < 1900) {
831 0 : ShowWarningError(state,
832 0 : CurrentModuleObject + ": Invalid value in field " + state.dataIPShortCut->cNumericFieldNames(1) +
833 : ". Value less than 1900 yet it is representing a year. ");
834 : }
835 : // A3, \field Escalation Start Month
836 : // \type choice
837 : // \key January
838 : // \key February
839 : // \key March
840 : // \key April
841 : // \key May
842 : // \key June
843 : // \key July
844 : // \key August
845 : // \key September
846 : // \key October
847 : // \key November
848 : // \key December
849 : // \default January
850 3 : elcc->UsePriceEscalation(iInObj).escalationStartMonth =
851 6 : getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(3)));
852 3 : if (elcc->UsePriceEscalation(iInObj).escalationStartMonth == -1) {
853 0 : elcc->UsePriceEscalation(iInObj).escalationStartMonth = 0;
854 0 : ShowWarningError(state,
855 0 : CurrentModuleObject + ": Invalid month entered in field " + state.dataIPShortCut->cAlphaFieldNames(3) +
856 0 : ". Using January instead of \"" + AlphaArray(3) + "\"");
857 : }
858 : // N2, \field Year 1 Escalation
859 : // \type real
860 : // \begin-extensible
861 : // The array is from the baseDateYear until baseDateYear + lengthStudyYears
862 : // Set the array to default to 1.0
863 69 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
864 66 : elcc->UsePriceEscalation(iInObj).Escalation(jYear) = 1.0;
865 : }
866 : // Since the years in the UsePriceEscalation may not match up with the baseDateYear and
867 : // the lenghtStudyYears, need to make adjustments when reading in the values to align
868 : // with the baseDateYear (the first item in all yearly arrays)
869 3 : elcc->UsePriceEscalation_escStartYear = elcc->UsePriceEscalation(iInObj).escalationStartYear;
870 3 : elcc->UsePriceEscalation_escNumYears = NumNums - 1;
871 3 : elcc->UsePriceEscalation_escEndYear = elcc->UsePriceEscalation_escStartYear + elcc->UsePriceEscalation_escNumYears - 1;
872 3 : elcc->UsePriceEscalation_earlierEndYear = min(elcc->UsePriceEscalation_escEndYear, elcc->lastDateYear); // pick the earlier ending date
873 3 : elcc->UsePriceEscalation_laterStartYear = max(elcc->UsePriceEscalation_escStartYear, elcc->baseDateYear); // pick the later starting date
874 69 : for (jYear = elcc->UsePriceEscalation_laterStartYear; jYear <= elcc->UsePriceEscalation_earlierEndYear; ++jYear) {
875 66 : elcc->UsePriceEscalation_curFld = 2 + jYear - elcc->UsePriceEscalation_escStartYear;
876 66 : elcc->UsePriceEscalation_curEsc = 1 + jYear - elcc->baseDateYear;
877 66 : if ((elcc->UsePriceEscalation_curFld <= NumNums) && (elcc->UsePriceEscalation_curFld >= 1)) {
878 66 : if ((elcc->UsePriceEscalation_curEsc <= elcc->lengthStudyYears) && (elcc->UsePriceEscalation_curEsc >= 1)) {
879 66 : elcc->UsePriceEscalation(iInObj).Escalation(elcc->UsePriceEscalation_curEsc) = NumArray(elcc->UsePriceEscalation_curFld);
880 : }
881 : }
882 : }
883 : }
884 : }
885 : }
886 :
887 769 : void GetInputLifeCycleCostUseAdjustment(EnergyPlusData &state)
888 : {
889 : // SUBROUTINE INFORMATION:
890 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
891 : // DATE WRITTEN May 2010
892 :
893 : // PURPOSE OF THIS SUBROUTINE:
894 : // Read the input file for "LifeCycleCost:UseAdjustment" object.
895 :
896 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
897 :
898 : int iInObj; // loop index variable for reading in objects
899 : int jFld; // loop counter
900 : int jYear; // loop counter
901 : int NumFields; // Total number of elements
902 : int NumAlphas; // Number of elements in the alpha array
903 : int NumNums; // Number of elements in the numeric array
904 770 : Array1D_string AlphaArray; // character string data
905 770 : Array1D<Real64> NumArray; // numeric data
906 : int IOStat; // IO Status when calling get input subroutine
907 770 : std::string CurrentModuleObject; // for ease in renaming.
908 : int numFldsToUse;
909 :
910 769 : auto &elcc(state.dataEconLifeCycleCost);
911 :
912 769 : if (!elcc->LCCparamPresent) return;
913 1 : CurrentModuleObject = "LifeCycleCost:UseAdjustment";
914 1 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
915 1 : NumArray.allocate(NumNums);
916 1 : AlphaArray.allocate(NumAlphas);
917 1 : elcc->numUseAdjustment = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
918 1 : elcc->UseAdjustment.allocate(elcc->numUseAdjustment);
919 2 : for (iInObj = 1; iInObj <= elcc->numUseAdjustment; ++iInObj) {
920 1 : elcc->UseAdjustment(iInObj).Adjustment.allocate(elcc->lengthStudyYears);
921 : }
922 1 : if (elcc->numUseAdjustment > 0) {
923 2 : for (iInObj = 1; iInObj <= elcc->numUseAdjustment; ++iInObj) {
924 5 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
925 : CurrentModuleObject,
926 : iInObj,
927 : AlphaArray,
928 : NumAlphas,
929 : NumArray,
930 : NumNums,
931 : IOStat,
932 1 : state.dataIPShortCut->lNumericFieldBlanks,
933 1 : state.dataIPShortCut->lAlphaFieldBlanks,
934 1 : state.dataIPShortCut->cAlphaFieldNames,
935 1 : state.dataIPShortCut->cNumericFieldNames);
936 : // check to make sure none of the values are another life cycle cost object
937 3 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
938 2 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
939 0 : ShowWarningError(state,
940 0 : "In " + CurrentModuleObject + " named " + AlphaArray(1) +
941 : " a field was found containing LifeCycleCost: which may indicate a missing comma.");
942 : }
943 : }
944 : // start to extract values from input array into appropriate fields
945 : // A1, \field Name
946 : // \required-field
947 : // \type alpha
948 1 : elcc->UseAdjustment(iInObj).name = AlphaArray(1);
949 : // A2, \field Resource
950 : // \required-field
951 : // \type choice
952 : // \key Electricity
953 : // \key NaturalGas
954 : // \key Steam
955 : // \key Gasoline
956 : // \key Diesel
957 : // \key Coal
958 : // \key FuelOilNo1
959 : // \key FuelOilNo2
960 : // \key Propane
961 : // \key Water
962 : // \key OtherFuel1
963 : // \key OtherFuel2
964 1 : elcc->UseAdjustment(iInObj).resource = AssignResourceTypeNum(AlphaArray(2)); // use function from DataGlobalConstants
965 1 : if (NumAlphas > 2) {
966 0 : ShowWarningError(state, "In " + CurrentModuleObject + " contains more alpha fields than expected.");
967 : }
968 : // N1, \field Year 1 Multiplier
969 : // \type real
970 : // \begin-extensible
971 : // Set the array to default to 1.0
972 23 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
973 22 : elcc->UseAdjustment(iInObj).Adjustment(jYear) = 1.0;
974 : }
975 1 : numFldsToUse = min(NumNums, elcc->lengthStudyYears);
976 2 : for (jYear = 1; jYear <= numFldsToUse; ++jYear) {
977 1 : elcc->UseAdjustment(iInObj).Adjustment(jYear) = NumArray(jYear);
978 : }
979 : }
980 : }
981 : }
982 :
983 : //======================================================================================================================
984 : //======================================================================================================================
985 :
986 : // COMPUTATION ROUTINES
987 :
988 : //======================================================================================================================
989 : //======================================================================================================================
990 :
991 1 : void ExpressAsCashFlows(EnergyPlusData &state)
992 : {
993 : // SUBROUTINE INFORMATION:
994 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
995 : // DATE WRITTEN July 2010
996 : // MODIFIED na
997 : // RE-ENGINEERED na
998 :
999 : // PURPOSE OF THIS SUBROUTINE:
1000 : // Convert all recurring and nonrecurring costs into cash flows
1001 : // used in calculations and reporting.
1002 :
1003 : // METHODOLOGY EMPLOYED:
1004 :
1005 : // REFERENCES:
1006 : // na
1007 :
1008 : // USE STATEMENTS:
1009 :
1010 : // Using/Aliasing
1011 : using EconomicTariff::GetMonthlyCostForResource;
1012 :
1013 : // Locals
1014 : // SUBROUTINE ARGUMENT DEFINITIONS:
1015 : // na
1016 :
1017 : // SUBROUTINE PARAMETER DEFINITIONS:
1018 : // na
1019 :
1020 : // INTERFACE BLOCK SPECIFICATIONS
1021 : // na
1022 :
1023 : // DERIVED TYPE DEFINITIONS
1024 : // na
1025 :
1026 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1027 : int iCashFlow;
1028 : int jCost;
1029 : int jAdj;
1030 : int kYear;
1031 : int offset;
1032 : int month; // number of months since base date
1033 : int firstMonth;
1034 : int monthsBaseToService;
1035 :
1036 2 : std::map<int, std::map<DataGlobalConstants::ResourceType, Real64>> resourceCosts;
1037 13 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1038 24 : std::map<DataGlobalConstants::ResourceType, Real64> monthMap;
1039 576 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1040 564 : monthMap.insert(std::pair<DataGlobalConstants::ResourceType, Real64>(iResource, 0.0));
1041 : }
1042 12 : resourceCosts.insert(std::pair<int, std::map<DataGlobalConstants::ResourceType, Real64>>(jMonth, monthMap));
1043 : }
1044 :
1045 2 : Array1D<Real64> curResourceCosts(12);
1046 :
1047 2 : std::map<DataGlobalConstants::ResourceType, bool> resourceCostNotZero;
1048 48 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1049 47 : resourceCostNotZero.insert(std::pair<DataGlobalConstants::ResourceType, bool>(iResource, false));
1050 : }
1051 :
1052 2 : std::map<DataGlobalConstants::ResourceType, Real64> resourceCostAnnual;
1053 48 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1054 47 : resourceCostAnnual.insert(std::pair<DataGlobalConstants::ResourceType, Real64>(iResource, 0.0));
1055 : }
1056 :
1057 : Real64 annualCost;
1058 : int found;
1059 : CostCategory curCategory;
1060 2 : Array1D<Real64> monthlyInflationFactor;
1061 : Real64 inflationPerMonth;
1062 : int iLoop;
1063 :
1064 1 : auto &elcc(state.dataEconLifeCycleCost);
1065 :
1066 : // compute months from 1900 for base and service period
1067 1 : elcc->ExpressAsCashFlows_baseMonths1900 =
1068 1 : (elcc->baseDateYear - 1900) * 12 + (elcc->baseDateMonth + 1); // elcc->baseDateMonth + 1 to account for baseDateMonth starting at 0
1069 1 : elcc->ExpressAsCashFlows_serviceMonths1900 =
1070 1 : (elcc->serviceDateYear - 1900) * 12 + elcc->serviceDateMonth + 1; // elcc->serviceDateMonth + 1 to account for serviceDateMonth starting at 0
1071 1 : monthsBaseToService = elcc->ExpressAsCashFlows_serviceMonths1900 - elcc->ExpressAsCashFlows_baseMonths1900;
1072 : // if ComponentCost:LineItem exist, the grand total of all costs are another non-recurring cost
1073 1 : if (state.dataCostEstimateManager->CurntBldg.GrandTotal >
1074 : 0.0) { // from DataCostEstimate and computed in WriteCompCostTable within OutputReportTabular
1075 0 : ++elcc->numNonrecurringCost;
1076 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].name = "Total of ComponentCost:*";
1077 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].lineItem = "";
1078 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].category = CostCategory::Construction;
1079 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].cost = state.dataCostEstimateManager->CurntBldg.GrandTotal;
1080 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].startOfCosts = StartCosts::BasePeriod;
1081 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].yearsFromStart = 0;
1082 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].monthsFromStart = 0;
1083 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].totalMonthsFromStart = 0;
1084 : }
1085 :
1086 : // gather costs from EconomicTariff for each end use
1087 1 : elcc->numResourcesUsed = 0;
1088 48 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1089 47 : GetMonthlyCostForResource(state, iResource, curResourceCosts);
1090 47 : annualCost = 0.0;
1091 611 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1092 564 : resourceCosts.at(jMonth).at(iResource) = curResourceCosts(jMonth);
1093 564 : annualCost += resourceCosts.at(jMonth).at(iResource);
1094 : }
1095 47 : if (annualCost != 0.0) {
1096 1 : ++elcc->numResourcesUsed;
1097 1 : resourceCostNotZero.at(iResource) = true;
1098 : } else {
1099 46 : resourceCostNotZero.at(iResource) = false;
1100 : }
1101 47 : resourceCostAnnual.at(iResource) = annualCost;
1102 : }
1103 : // allocate the escalated energy cost arrays
1104 23 : for (int year = 1; year <= elcc->lengthStudyYears; ++year) {
1105 44 : std::map<DataGlobalConstants::ResourceType, Real64> yearMap;
1106 1056 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1107 1034 : yearMap.insert(std::pair<DataGlobalConstants::ResourceType, Real64>(iResource, 0.0));
1108 : }
1109 22 : elcc->EscalatedEnergy.insert(std::pair<int, std::map<DataGlobalConstants::ResourceType, Real64>>(year, yearMap));
1110 : }
1111 :
1112 1 : elcc->EscalatedTotEnergy.allocate(elcc->lengthStudyYears);
1113 1 : elcc->EscalatedTotEnergy = 0.0;
1114 :
1115 : // pre-compute the inflation factors for each year
1116 1 : monthlyInflationFactor.allocate(elcc->lengthStudyTotalMonths);
1117 1 : if (elcc->inflationApproach == InflAppr::ConstantDollar) {
1118 1 : monthlyInflationFactor = 1.0; // not really used but just in case
1119 0 : } else if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1120 : // to allocate an interest rate (in this case inflation) cannot just use 1/12
1121 : // for the monthly value since it will be slightly wrong. Instead use inverse of
1122 : // formula from Newnan (4-32) which is r = m x (ia + 1)^(1/m) - 1)
1123 0 : inflationPerMonth = std::pow(elcc->inflation + 1.0, 1.0 / 12.0) - 1;
1124 0 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1125 0 : monthlyInflationFactor(jMonth) = std::pow(1.0 + inflationPerMonth, jMonth - 1);
1126 : }
1127 : }
1128 :
1129 1 : elcc->numCashFlow = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed;
1130 : // Cashflow array order:
1131 : // 1 cost categories
1132 : // 2 recurring costs
1133 : // 3 nonrecurring costs
1134 : // 4 resource costs
1135 1 : elcc->CashFlow.resize(elcc->numCashFlow);
1136 24 : for (iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1137 23 : elcc->CashFlow[iCashFlow].mnAmount.allocate(elcc->lengthStudyTotalMonths);
1138 23 : elcc->CashFlow[iCashFlow].yrAmount.allocate(elcc->lengthStudyYears);
1139 23 : elcc->CashFlow[iCashFlow].yrPresVal.allocate(elcc->lengthStudyYears);
1140 23 : elcc->CashFlow[iCashFlow].mnAmount = 0.0; // zero all cash flow values
1141 23 : elcc->CashFlow[iCashFlow].yrAmount = 0.0; // zero all cash flow values
1142 23 : elcc->CashFlow[iCashFlow].yrPresVal = 0.0; // zero all present values
1143 : }
1144 : // Put nonrecurring costs into cashflows
1145 1 : offset = CostCategory::Num + elcc->numRecurringCosts;
1146 6 : for (jCost = 0; jCost < elcc->numNonrecurringCost; ++jCost) {
1147 5 : elcc->CashFlow[offset + jCost].name = elcc->NonrecurringCost[jCost].name;
1148 5 : elcc->CashFlow[offset + jCost].SourceKind = SourceKindType::Nonrecurring;
1149 5 : elcc->CashFlow[offset + jCost].Category = elcc->NonrecurringCost[jCost].category;
1150 5 : elcc->CashFlow[offset + jCost].orginalCost = elcc->NonrecurringCost[jCost].cost;
1151 5 : elcc->CashFlow[offset + jCost].mnAmount = 0.0;
1152 5 : if (elcc->NonrecurringCost[jCost].startOfCosts == StartCosts::ServicePeriod) {
1153 0 : month = elcc->NonrecurringCost[jCost].totalMonthsFromStart + monthsBaseToService + 1;
1154 5 : } else if (elcc->NonrecurringCost[jCost].startOfCosts == StartCosts::BasePeriod) {
1155 5 : month = elcc->NonrecurringCost[jCost].totalMonthsFromStart + 1;
1156 : }
1157 5 : if ((month >= 1) && (month <= elcc->lengthStudyTotalMonths)) {
1158 5 : elcc->CashFlow[offset + jCost].mnAmount(month) = elcc->NonrecurringCost[jCost].cost * monthlyInflationFactor(month);
1159 : } else {
1160 0 : ShowWarningError(state,
1161 0 : "For life cycle costing a nonrecurring cost named " + elcc->NonrecurringCost[jCost].name +
1162 : " contains a cost which is not within the study period.");
1163 : }
1164 : }
1165 : // Put recurring costs into cashflows
1166 1 : offset = CostCategory::Num;
1167 2 : for (jCost = 0; jCost < elcc->numRecurringCosts; ++jCost) {
1168 1 : elcc->CashFlow[offset + jCost].name = elcc->RecurringCosts[jCost].name;
1169 1 : elcc->CashFlow[offset + jCost].SourceKind = SourceKindType::Recurring;
1170 1 : elcc->CashFlow[offset + jCost].Category = elcc->RecurringCosts[jCost].category;
1171 1 : elcc->CashFlow[offset + jCost].orginalCost = elcc->RecurringCosts[jCost].cost;
1172 1 : if (elcc->RecurringCosts[jCost].startOfCosts == StartCosts::ServicePeriod) {
1173 1 : firstMonth = elcc->RecurringCosts[jCost].totalMonthsFromStart + monthsBaseToService + 1;
1174 0 : } else if (elcc->RecurringCosts[jCost].startOfCosts == StartCosts::BasePeriod) {
1175 0 : firstMonth = elcc->RecurringCosts[jCost].totalMonthsFromStart + 1;
1176 : }
1177 1 : if ((firstMonth >= 1) && (firstMonth <= elcc->lengthStudyTotalMonths)) {
1178 1 : month = firstMonth;
1179 1 : if (elcc->RecurringCosts[jCost].totalRepeatPeriodMonths >= 1) {
1180 20 : for (iLoop = 0; iLoop < 10000; ++iLoop) { // add a limit to the loop to prevent runaway condition
1181 20 : elcc->CashFlow[offset + jCost].mnAmount(month) = elcc->RecurringCosts[jCost].cost * monthlyInflationFactor(month);
1182 20 : month += elcc->RecurringCosts[jCost].totalRepeatPeriodMonths;
1183 20 : if (month > elcc->lengthStudyTotalMonths) break;
1184 : }
1185 : }
1186 : } else {
1187 0 : ShowWarningError(state,
1188 0 : "For life cycle costing the recurring cost named " + elcc->RecurringCosts[jCost].name +
1189 : " has the first year of the costs that is not within the study period.");
1190 : }
1191 : }
1192 : // Put resource costs into cashflows
1193 : // the first cash flow for resources should be after the categories, recurring and nonrecurring costs
1194 1 : int cashFlowCounter = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost - 1; // Since CashFlow starts at 0
1195 48 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1196 47 : if (resourceCostNotZero.at(iResource)) {
1197 1 : ++cashFlowCounter;
1198 :
1199 1 : switch (iResource) {
1200 0 : case DataGlobalConstants::ResourceType::Water:
1201 : case DataGlobalConstants::ResourceType::OnSiteWater:
1202 : case DataGlobalConstants::ResourceType::MainsWater:
1203 : case DataGlobalConstants::ResourceType::RainWater:
1204 : case DataGlobalConstants::ResourceType::WellWater:
1205 : case DataGlobalConstants::ResourceType::Condensate:
1206 0 : elcc->CashFlow[cashFlowCounter].Category = CostCategory::Water;
1207 0 : break;
1208 1 : case DataGlobalConstants::ResourceType::Electricity:
1209 : case DataGlobalConstants::ResourceType::Natural_Gas:
1210 : case DataGlobalConstants::ResourceType::Gasoline:
1211 : case DataGlobalConstants::ResourceType::Diesel:
1212 : case DataGlobalConstants::ResourceType::Coal:
1213 : case DataGlobalConstants::ResourceType::FuelOil_1:
1214 : case DataGlobalConstants::ResourceType::FuelOil_2:
1215 : case DataGlobalConstants::ResourceType::Propane:
1216 : case DataGlobalConstants::ResourceType::EnergyTransfer:
1217 : case DataGlobalConstants::ResourceType::Steam:
1218 : case DataGlobalConstants::ResourceType::DistrictCooling:
1219 : case DataGlobalConstants::ResourceType::DistrictHeating:
1220 : case DataGlobalConstants::ResourceType::ElectricityProduced:
1221 : case DataGlobalConstants::ResourceType::ElectricityPurchased:
1222 : case DataGlobalConstants::ResourceType::ElectricityNet:
1223 : case DataGlobalConstants::ResourceType::SolarWater:
1224 : case DataGlobalConstants::ResourceType::SolarAir:
1225 1 : elcc->CashFlow[cashFlowCounter].Category = CostCategory::Energy;
1226 1 : break;
1227 0 : default:
1228 0 : elcc->CashFlow[cashFlowCounter].Category = CostCategory::Operation;
1229 : }
1230 :
1231 1 : elcc->CashFlow[cashFlowCounter].Resource = iResource;
1232 1 : elcc->CashFlow[cashFlowCounter].SourceKind = SourceKindType::Resource;
1233 1 : elcc->CashFlow[cashFlowCounter].name = GetResourceTypeChar(iResource);
1234 1 : if (cashFlowCounter <= elcc->numCashFlow) {
1235 : // put the monthly energy costs into the cashflow prior to adjustments
1236 : // energy costs (a.k.a. resource costs) start at the start of service and repeat
1237 : // until the end of the study total
1238 13 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1239 12 : elcc->CashFlow[cashFlowCounter].mnAmount(monthsBaseToService + jMonth) = resourceCosts.at(jMonth).at(iResource);
1240 : }
1241 1 : elcc->CashFlow[cashFlowCounter].orginalCost = resourceCostAnnual.at(iResource);
1242 229 : for (int jMonth = monthsBaseToService + 13; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1243 : // use the cost from a year earlier
1244 228 : elcc->CashFlow[cashFlowCounter].mnAmount(jMonth) = elcc->CashFlow[cashFlowCounter].mnAmount(jMonth - 12);
1245 : }
1246 : // add in the impact of inflation
1247 265 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1248 264 : elcc->CashFlow[cashFlowCounter].mnAmount(jMonth) *= monthlyInflationFactor(jMonth);
1249 : }
1250 : // now factor in adjustments
1251 : // need to find the correct adjustment to use for the current resource
1252 1 : found = 0;
1253 2 : for (jAdj = 1; jAdj <= elcc->numUseAdjustment; ++jAdj) {
1254 1 : if (elcc->UseAdjustment(jAdj).resource == iResource) {
1255 0 : found = jAdj;
1256 0 : break;
1257 : }
1258 : }
1259 : // if any adjustments were found for that resource apply the multiplier
1260 1 : if (found != 0) {
1261 0 : for (kYear = 1; kYear <= elcc->lengthStudyYears;
1262 : ++kYear) { // if service period is later than base period then this will go too far
1263 0 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1264 0 : month = (kYear - 1) * 12 + jMonth;
1265 0 : if (month > elcc->lengthStudyTotalMonths) break;
1266 0 : elcc->CashFlow[cashFlowCounter].mnAmount(month) *= elcc->UseAdjustment(found).Adjustment(kYear);
1267 : }
1268 : }
1269 : }
1270 : }
1271 : }
1272 : }
1273 : // put cashflows into categories
1274 17 : for (jCost = 0; jCost < CostCategory::Num; ++jCost) {
1275 16 : elcc->CashFlow[jCost].Category = static_cast<CostCategory>(jCost); // make each category the type indicated
1276 16 : elcc->CashFlow[jCost].SourceKind = SourceKindType::Sum;
1277 : }
1278 : // add the cashflows by category
1279 9 : for (jCost = CostCategory::Num - 1; jCost < elcc->numCashFlow; ++jCost) {
1280 8 : curCategory = elcc->CashFlow[jCost].Category;
1281 8 : if ((curCategory < CostCategory::Num) && (curCategory >= 0)) {
1282 2120 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1283 2112 : elcc->CashFlow[curCategory].mnAmount(jMonth) += elcc->CashFlow[jCost].mnAmount(jMonth);
1284 : }
1285 : }
1286 : }
1287 : // create total categories
1288 265 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1289 264 : elcc->CashFlow[CostCategory::TotEnergy].mnAmount(jMonth) = elcc->CashFlow[CostCategory::Energy].mnAmount(jMonth);
1290 264 : elcc->CashFlow[CostCategory::TotOper].mnAmount(jMonth) =
1291 528 : elcc->CashFlow[CostCategory::Maintenance].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Repair].mnAmount(jMonth) +
1292 792 : elcc->CashFlow[CostCategory::Operation].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Replacement].mnAmount(jMonth) +
1293 792 : elcc->CashFlow[CostCategory::MinorOverhaul].mnAmount(jMonth) + elcc->CashFlow[CostCategory::MajorOverhaul].mnAmount(jMonth) +
1294 792 : elcc->CashFlow[CostCategory::OtherOperational].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Water].mnAmount(jMonth) +
1295 264 : elcc->CashFlow[CostCategory::Energy].mnAmount(jMonth);
1296 792 : elcc->CashFlow[CostCategory::TotCaptl].mnAmount(jMonth) = elcc->CashFlow[CostCategory::Construction].mnAmount(jMonth) +
1297 528 : elcc->CashFlow[CostCategory::Salvage].mnAmount(jMonth) +
1298 264 : elcc->CashFlow[CostCategory::OtherCapital].mnAmount(jMonth);
1299 264 : elcc->CashFlow[CostCategory::TotGrand].mnAmount(jMonth) =
1300 264 : elcc->CashFlow[CostCategory::TotOper].mnAmount(jMonth) + elcc->CashFlow[CostCategory::TotCaptl].mnAmount(jMonth);
1301 : }
1302 : // convert all monthly cashflows into yearly cashflows
1303 24 : for (jCost = 0; jCost < elcc->numCashFlow; ++jCost) {
1304 529 : for (kYear = 1; kYear <= elcc->lengthStudyYears; ++kYear) {
1305 506 : annualCost = 0.0;
1306 6578 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1307 6072 : month = (kYear - 1) * 12 + jMonth;
1308 6072 : if (month <= elcc->lengthStudyTotalMonths) {
1309 6072 : annualCost += elcc->CashFlow[jCost].mnAmount(month);
1310 : }
1311 : }
1312 506 : elcc->CashFlow[jCost].yrAmount(kYear) = annualCost;
1313 : }
1314 : }
1315 : // generate a warning if resource referenced was not used
1316 4 : for (int nUsePriceEsc = 1; nUsePriceEsc <= elcc->numUsePriceEscalation; ++nUsePriceEsc) {
1317 3 : auto curResource = elcc->UsePriceEscalation(nUsePriceEsc).resource;
1318 3 : if (!resourceCostNotZero.at(curResource) && state.dataGlobal->DoWeathSim) {
1319 0 : ShowWarningError(state,
1320 0 : "The resource referenced by LifeCycleCost:UsePriceEscalation= \"" + elcc->UsePriceEscalation(nUsePriceEsc).name +
1321 : "\" has no energy cost. ");
1322 0 : ShowContinueError(state, "... It is likely that the wrong resource is used. The resource should match the meter used in Utility:Tariff.");
1323 : }
1324 : }
1325 1 : }
1326 :
1327 1 : void ComputeEscalatedEnergyCosts(EnergyPlusData &state)
1328 : {
1329 : // J. Glazer - August 2019
1330 : int nUsePriceEsc;
1331 :
1332 1 : auto &elcc(state.dataEconLifeCycleCost);
1333 :
1334 24 : for (int iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1335 23 : if (elcc->CashFlow[iCashFlow].pvKind == PrValKind::Energy) {
1336 : // make sure this is not water
1337 1 : auto curResource = elcc->CashFlow[iCashFlow].Resource;
1338 2 : if (elcc->CashFlow[iCashFlow].Resource == DataGlobalConstants::ResourceType::Water ||
1339 1 : (elcc->CashFlow[iCashFlow].Resource >= DataGlobalConstants::ResourceType::OnSiteWater &&
1340 0 : elcc->CashFlow[iCashFlow].Resource <= DataGlobalConstants::ResourceType::Condensate)) {
1341 0 : continue;
1342 : }
1343 1 : if ((curResource != DataGlobalConstants::ResourceType::None)) {
1344 1 : int found = 0;
1345 3 : for (nUsePriceEsc = 1; nUsePriceEsc <= elcc->numUsePriceEscalation; ++nUsePriceEsc) {
1346 3 : if (elcc->UsePriceEscalation(nUsePriceEsc).resource == curResource) {
1347 1 : found = nUsePriceEsc;
1348 1 : break;
1349 : }
1350 : }
1351 1 : if (found > 0) {
1352 23 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1353 22 : elcc->EscalatedEnergy.at(jYear).at(curResource) =
1354 22 : elcc->CashFlow[iCashFlow].yrAmount(jYear) * elcc->UsePriceEscalation(found).Escalation(jYear);
1355 : }
1356 : } else { // if no escalation than just store the original energy cost
1357 0 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1358 0 : elcc->EscalatedEnergy.at(jYear).at(curResource) = elcc->CashFlow[iCashFlow].yrAmount(jYear);
1359 : }
1360 : }
1361 : }
1362 : }
1363 : }
1364 48 : for (auto kResource : state.dataGlobalConst->AllResourceTypes) {
1365 1081 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1366 1034 : elcc->EscalatedTotEnergy(jYear) += elcc->EscalatedEnergy.at(jYear).at(kResource);
1367 : }
1368 : }
1369 1 : }
1370 :
1371 1 : void ComputePresentValue(EnergyPlusData &state)
1372 : {
1373 : // SUBROUTINE INFORMATION:
1374 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1375 : // DATE WRITTEN August 2010
1376 : // MODIFIED na
1377 : // RE-ENGINEERED na
1378 :
1379 : // PURPOSE OF THIS SUBROUTINE:
1380 : // For each cashflow, compute the present value
1381 :
1382 : // METHODOLOGY EMPLOYED:
1383 :
1384 : // REFERENCES:
1385 : // na
1386 :
1387 : // USE STATEMENTS:
1388 :
1389 : // Locals
1390 : // SUBROUTINE ARGUMENT DEFINITIONS:
1391 : // na
1392 :
1393 : // SUBROUTINE PARAMETER DEFINITIONS:
1394 : // na
1395 :
1396 : // INTERFACE BLOCK SPECIFICATIONS
1397 : // na
1398 :
1399 : // DERIVED TYPE DEFINITIONS
1400 : // na
1401 :
1402 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1403 : Real64 totalPV;
1404 : int curCategory;
1405 : Real64 curDiscountRate;
1406 : int iCashFlow;
1407 : int jYear;
1408 : int nUsePriceEsc;
1409 : Real64 effectiveYear;
1410 :
1411 1 : auto &elcc(state.dataEconLifeCycleCost);
1412 :
1413 : // identify how each cashflow should be treated
1414 24 : for (iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1415 23 : switch (elcc->CashFlow[iCashFlow].SourceKind) {
1416 1 : case SourceKindType::Resource: {
1417 : // only for real fuels purchased such as electricity, natural gas, etc..
1418 2 : if ((elcc->CashFlow[iCashFlow].Resource >= DataGlobalConstants::ResourceType::Electricity) &&
1419 1 : (elcc->CashFlow[iCashFlow].Resource <= DataGlobalConstants::ResourceType::ElectricitySurplusSold)) {
1420 1 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::Energy;
1421 : } else {
1422 0 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::NonEnergy;
1423 : }
1424 1 : break;
1425 : }
1426 6 : case SourceKindType::Recurring:
1427 : case SourceKindType::Nonrecurring: {
1428 6 : if (elcc->CashFlow[iCashFlow].Category == CostCategory::Energy) {
1429 0 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::Energy;
1430 : } else {
1431 6 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::NonEnergy;
1432 : }
1433 6 : break;
1434 : }
1435 16 : case SourceKindType::Sum:
1436 : default: {
1437 16 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::NotComputed;
1438 16 : break;
1439 : }
1440 : }
1441 : }
1442 : // compute the Single Present Value factors based on the discount rate
1443 1 : elcc->SPV.allocate(elcc->lengthStudyYears);
1444 23 : for (int year = 1; year <= elcc->lengthStudyYears; ++year) {
1445 44 : std::map<DataGlobalConstants::ResourceType, Real64> yearMap;
1446 1056 : for (auto iResource : state.dataGlobalConst->AllResourceTypes) {
1447 1034 : yearMap.insert(std::pair<DataGlobalConstants::ResourceType, Real64>(iResource, 0.0));
1448 : }
1449 22 : elcc->energySPV.insert(std::pair<int, std::map<DataGlobalConstants::ResourceType, Real64>>(year, yearMap));
1450 : }
1451 :
1452 : // Depending if using Constant or Current Dollar analysis
1453 : // use the appropriate discount rate
1454 1 : if (elcc->inflationApproach == InflAppr::ConstantDollar) {
1455 1 : curDiscountRate = elcc->realDiscountRate;
1456 0 : } else if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1457 0 : curDiscountRate = elcc->nominalDiscountRate;
1458 : }
1459 : // compute single present values based on real discount rates
1460 1 : constexpr std::array<Real64, static_cast<int>(DiscConv::Num)> DiscConv2EffectiveYearAdjustment = {1.0, 0.5, 0.0};
1461 23 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1462 : // NIST 155 D.2.1.1 - Single Present Value (SPV) formula
1463 22 : effectiveYear = double(jYear) - DiscConv2EffectiveYearAdjustment[static_cast<int>(elcc->discountConvention)];
1464 22 : elcc->SPV(jYear) = 1.0 / std::pow(1.0 + curDiscountRate, effectiveYear);
1465 : }
1466 : // use SPV as default values for all energy types
1467 23 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1468 1056 : for (auto kResource : state.dataGlobalConst->AllResourceTypes) {
1469 1034 : elcc->energySPV.at(jYear).at(kResource) = elcc->SPV(jYear);
1470 : }
1471 : }
1472 : // loop through the resources and if they match a UseEscalation use those values instead
1473 4 : for (nUsePriceEsc = 1; nUsePriceEsc <= elcc->numUsePriceEscalation; ++nUsePriceEsc) {
1474 3 : auto curResource = elcc->UsePriceEscalation(nUsePriceEsc).resource;
1475 3 : if (curResource != DataGlobalConstants::ResourceType::None) {
1476 69 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1477 : // the following is based on UPV* formula from NIST 135 supplement but is for a single year
1478 66 : effectiveYear = double(jYear) - DiscConv2EffectiveYearAdjustment[static_cast<int>(elcc->discountConvention)];
1479 66 : elcc->energySPV.at(jYear).at(curResource) =
1480 66 : elcc->UsePriceEscalation(nUsePriceEsc).Escalation(jYear) / std::pow(1.0 + curDiscountRate, effectiveYear);
1481 : }
1482 : }
1483 : }
1484 24 : for (iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1485 23 : switch (elcc->CashFlow[iCashFlow].pvKind) {
1486 6 : case PrValKind::NonEnergy: {
1487 6 : totalPV = 0.0;
1488 138 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1489 132 : elcc->CashFlow[iCashFlow].yrPresVal(jYear) = elcc->CashFlow[iCashFlow].yrAmount(jYear) * elcc->SPV(jYear);
1490 132 : totalPV += elcc->CashFlow[iCashFlow].yrPresVal(jYear);
1491 : }
1492 6 : elcc->CashFlow[iCashFlow].presentValue = totalPV;
1493 6 : break;
1494 : }
1495 1 : case PrValKind::Energy: {
1496 1 : auto curResource = elcc->CashFlow[iCashFlow].Resource;
1497 1 : if (curResource != DataGlobalConstants::ResourceType::None) {
1498 1 : totalPV = 0.0;
1499 23 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1500 22 : elcc->CashFlow[iCashFlow].yrPresVal(jYear) =
1501 22 : elcc->CashFlow[iCashFlow].yrAmount(jYear) * elcc->energySPV.at(jYear).at(curResource);
1502 22 : totalPV += elcc->CashFlow[iCashFlow].yrPresVal(jYear);
1503 : }
1504 1 : elcc->CashFlow[iCashFlow].presentValue = totalPV;
1505 : }
1506 1 : break;
1507 : }
1508 : // case iPrValKind::NotComputed: included in default
1509 16 : default:
1510 16 : break; // do nothing
1511 : }
1512 : }
1513 : // sum by category
1514 17 : for (int i = 0; i < CostCategory::Num; ++i) {
1515 16 : elcc->CashFlow[i].presentValue = 0; // initialize value to zero before summing in next for loop
1516 : }
1517 8 : for (iCashFlow = CostCategory::Num; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1518 7 : curCategory = elcc->CashFlow[iCashFlow].Category;
1519 7 : if ((curCategory < CostCategory::Num) && (curCategory >= 0)) {
1520 7 : elcc->CashFlow[curCategory].presentValue += elcc->CashFlow[iCashFlow].presentValue;
1521 161 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1522 154 : elcc->CashFlow[curCategory].yrPresVal(jYear) += elcc->CashFlow[iCashFlow].yrPresVal(jYear);
1523 : }
1524 : }
1525 : }
1526 : // create total categories
1527 1 : elcc->CashFlow[CostCategory::TotEnergy].presentValue = elcc->CashFlow[CostCategory::Energy].presentValue;
1528 1 : elcc->CashFlow[CostCategory::TotOper].presentValue =
1529 2 : elcc->CashFlow[CostCategory::Maintenance].presentValue + elcc->CashFlow[CostCategory::Repair].presentValue +
1530 3 : elcc->CashFlow[CostCategory::Operation].presentValue + elcc->CashFlow[CostCategory::Replacement].presentValue +
1531 3 : elcc->CashFlow[CostCategory::MinorOverhaul].presentValue + elcc->CashFlow[CostCategory::MajorOverhaul].presentValue +
1532 3 : elcc->CashFlow[CostCategory::OtherOperational].presentValue + elcc->CashFlow[CostCategory::Water].presentValue +
1533 1 : elcc->CashFlow[CostCategory::Energy].presentValue;
1534 3 : elcc->CashFlow[CostCategory::TotCaptl].presentValue = elcc->CashFlow[CostCategory::Construction].presentValue +
1535 2 : elcc->CashFlow[CostCategory::Salvage].presentValue +
1536 1 : elcc->CashFlow[CostCategory::OtherCapital].presentValue;
1537 1 : elcc->CashFlow[CostCategory::TotGrand].presentValue =
1538 1 : elcc->CashFlow[CostCategory::TotOper].presentValue + elcc->CashFlow[CostCategory::TotCaptl].presentValue;
1539 23 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1540 22 : elcc->CashFlow[CostCategory::TotEnergy].yrPresVal(jYear) = elcc->CashFlow[CostCategory::Energy].yrPresVal(jYear);
1541 22 : elcc->CashFlow[CostCategory::TotOper].yrPresVal(jYear) =
1542 44 : elcc->CashFlow[CostCategory::Maintenance].yrPresVal(jYear) + elcc->CashFlow[CostCategory::Repair].yrPresVal(jYear) +
1543 66 : elcc->CashFlow[CostCategory::Operation].yrPresVal(jYear) + elcc->CashFlow[CostCategory::Replacement].yrPresVal(jYear) +
1544 66 : elcc->CashFlow[CostCategory::MinorOverhaul].yrPresVal(jYear) + elcc->CashFlow[CostCategory::MajorOverhaul].yrPresVal(jYear) +
1545 66 : elcc->CashFlow[CostCategory::OtherOperational].yrPresVal(jYear) + elcc->CashFlow[CostCategory::Water].yrPresVal(jYear) +
1546 22 : elcc->CashFlow[CostCategory::Energy].yrPresVal(jYear);
1547 66 : elcc->CashFlow[CostCategory::TotCaptl].yrPresVal(jYear) = elcc->CashFlow[CostCategory::Construction].yrPresVal(jYear) +
1548 44 : elcc->CashFlow[CostCategory::Salvage].yrPresVal(jYear) +
1549 22 : elcc->CashFlow[CostCategory::OtherCapital].yrPresVal(jYear);
1550 22 : elcc->CashFlow[CostCategory::TotGrand].yrPresVal(jYear) =
1551 22 : elcc->CashFlow[CostCategory::TotOper].yrPresVal(jYear) + elcc->CashFlow[CostCategory::TotCaptl].yrPresVal(jYear);
1552 : }
1553 1 : }
1554 :
1555 1 : void ComputeTaxAndDepreciation(EnergyPlusData &state)
1556 : {
1557 : // SUBROUTINE INFORMATION:
1558 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1559 : // DATE WRITTEN August 2010
1560 : // MODIFIED na
1561 : // RE-ENGINEERED na
1562 :
1563 : // PURPOSE OF THIS SUBROUTINE:
1564 : // Compute the present value after factoring in taxes
1565 : // and depreciation.
1566 :
1567 : // METHODOLOGY EMPLOYED:
1568 :
1569 : // REFERENCES:
1570 : // na
1571 :
1572 : // USE STATEMENTS:
1573 :
1574 : // Locals
1575 : // SUBROUTINE ARGUMENT DEFINITIONS:
1576 : // na
1577 :
1578 : // SUBROUTINE PARAMETER DEFINITIONS:
1579 :
1580 : // INTERFACE BLOCK SPECIFICATIONS
1581 : // na
1582 :
1583 : // DERIVED TYPE DEFINITIONS
1584 : // na
1585 :
1586 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1587 : Real64 curCapital;
1588 : int curDepYear;
1589 : int iYear;
1590 : int jYear;
1591 :
1592 1 : auto &elcc(state.dataEconLifeCycleCost);
1593 :
1594 1 : elcc->DepreciatedCapital.allocate(elcc->lengthStudyYears);
1595 1 : elcc->TaxableIncome.allocate(elcc->lengthStudyYears);
1596 1 : elcc->Taxes.allocate(elcc->lengthStudyYears);
1597 1 : elcc->AfterTaxCashFlow.allocate(elcc->lengthStudyYears);
1598 1 : elcc->AfterTaxPresentValue.allocate(elcc->lengthStudyYears);
1599 :
1600 : // Depreciation factors are based on IRS Publication 946 for 2009 "How to Depreciate Property"
1601 : // The MACRS valus are based on Modified Accelerated Cost Recovery System GDS for 3, 5, 7, 10 year
1602 : // property are based on 200% depreciation method shown in Appendix A using half year. 15 and 20 are
1603 : // based on 150% (Chart 1). For Straight Line depreciation GDS is used for 27 years (actually 27.5)
1604 : // 31 years (actually 31.5 years) and 39 years using mid month. For 40 years ADS is used (chart 2)
1605 : // Table A-1 is used for 3, 4, 5, 10, 15 and 20 years. Table A-6 is for 27 years. Table A-7 for 31 years.
1606 : // Table A-7a for 39 years. Table A-13 for 40 years. These years are a classification of property
1607 : // and should not be confused with the length of the study. For 27 years, 31 years, 39 years and 40 years
1608 : // the June value was used.
1609 :
1610 : // convert construction costs (not salvage) into depreciation
1611 1 : elcc->DepreciatedCapital = 0.0; // set all years to zero
1612 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1613 22 : curCapital = elcc->CashFlow[CostCategory::Construction].yrAmount(iYear) + elcc->CashFlow[CostCategory::OtherCapital].yrAmount(iYear);
1614 924 : for (jYear = 0; jYear < SizeDepr; ++jYear) {
1615 902 : curDepYear = iYear + jYear; // start depreciating with the year that the capital was shown and go to years following
1616 902 : if (curDepYear <= elcc->lengthStudyYears) {
1617 253 : elcc->DepreciatedCapital(curDepYear) +=
1618 253 : curCapital * (DepreciationPercentTable[static_cast<int>(elcc->depreciationMethod)][jYear] / 100);
1619 : }
1620 : }
1621 : }
1622 : // Using Newnan pg 3880
1623 : // before-tax cash flow
1624 : // depreciation
1625 : // taxable income (before-tax cash flow - depreciation)
1626 : // income taxes (taxable income x incremental tax rate)
1627 : // after-tax cash flow (before-tax cash flow - income taxes)
1628 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1629 22 : elcc->TaxableIncome(iYear) = elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear) - elcc->DepreciatedCapital(iYear);
1630 22 : elcc->Taxes(iYear) = elcc->TaxableIncome(iYear) * elcc->taxRate;
1631 22 : elcc->AfterTaxCashFlow(iYear) = elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear) - elcc->Taxes(iYear);
1632 : // the present value after taxes is pretax present value minus the present value of the taxes
1633 22 : elcc->AfterTaxPresentValue(iYear) = elcc->CashFlow[CostCategory::TotGrand].yrPresVal(iYear) - elcc->Taxes(iYear) * elcc->SPV(iYear);
1634 : }
1635 1 : }
1636 :
1637 : //======================================================================================================================
1638 : //======================================================================================================================
1639 :
1640 : // OUTPUT ROUTINES
1641 :
1642 : //======================================================================================================================
1643 : //======================================================================================================================
1644 :
1645 1 : void WriteTabularLifeCycleCostReport(EnergyPlusData &state)
1646 : {
1647 : // SUBROUTINE INFORMATION:
1648 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1649 : // DATE WRITTEN June 2010
1650 : // MODIFIED na
1651 : // RE-ENGINEERED na
1652 :
1653 : // PURPOSE OF THIS SUBROUTINE:
1654 : // Write the output report related to life-cycle costing
1655 : // to the tabular output file.
1656 :
1657 : // METHODOLOGY EMPLOYED:
1658 :
1659 : // REFERENCES:
1660 : // na
1661 :
1662 : // Using/Aliasing
1663 : using OutputReportTabular::RealToStr;
1664 : using OutputReportTabular::WriteReportHeaders;
1665 : using OutputReportTabular::WriteSubtitle;
1666 : using OutputReportTabular::WriteTable;
1667 :
1668 : // Locals
1669 : // SUBROUTINE ARGUMENT DEFINITIONS:
1670 : // na
1671 :
1672 : // SUBROUTINE PARAMETER DEFINITIONS:
1673 : // na
1674 :
1675 : // INTERFACE BLOCK SPECIFICATIONS
1676 : // na
1677 :
1678 : // DERIVED TYPE DEFINITIONS
1679 : // na
1680 :
1681 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1682 : // all arrays are in the format: (row, column)
1683 2 : Array1D_string columnHead;
1684 2 : Array1D_int columnWidth;
1685 2 : Array1D_string rowHead;
1686 2 : Array2D_string tableBody;
1687 :
1688 : int numColumns;
1689 : int iYear;
1690 : int jObj;
1691 : int kMonth;
1692 : int curCashFlow;
1693 : int numRows;
1694 : int offset;
1695 : int numYears;
1696 : Real64 totalPV;
1697 :
1698 1 : auto &elcc(state.dataEconLifeCycleCost);
1699 :
1700 1 : if (elcc->LCCparamPresent && state.dataOutRptTab->displayLifeCycleCostReport) {
1701 : //---------------------------------
1702 : // Life-Cycle Cost Verification and Results Report
1703 : //---------------------------------
1704 1 : WriteReportHeaders(state, "Life-Cycle Cost Report", "Entire Facility", OutputProcessor::StoreType::Averaged);
1705 : //---- Life-Cycle Cost Parameters
1706 1 : rowHead.allocate(11);
1707 1 : columnHead.allocate(1);
1708 1 : columnWidth.allocate(1);
1709 1 : tableBody.allocate(1, 11);
1710 1 : tableBody = "";
1711 1 : rowHead(1) = "Name";
1712 1 : rowHead(2) = "Discounting Convention";
1713 1 : rowHead(3) = "Inflation Approach";
1714 1 : rowHead(4) = "Real Discount Rate";
1715 1 : rowHead(5) = "Nominal Discount Rate";
1716 1 : rowHead(6) = "Inflation";
1717 1 : rowHead(7) = "Base Date";
1718 1 : rowHead(8) = "Service Date";
1719 1 : rowHead(9) = "Length of Study Period in Years";
1720 1 : rowHead(10) = "Tax rate";
1721 1 : rowHead(11) = "Depreciation Method";
1722 1 : columnHead(1) = "Value";
1723 :
1724 1 : tableBody(1, 1) = elcc->LCCname;
1725 1 : tableBody(1, 2) = DiscConvNames[static_cast<int>(elcc->discountConvention)];
1726 1 : tableBody(1, 3) = InflApprNames[static_cast<int>(elcc->inflationApproach)];
1727 1 : if (elcc->inflationApproach == InflAppr::ConstantDollar) {
1728 1 : tableBody(1, 4) = RealToStr(elcc->realDiscountRate, 4);
1729 : } else {
1730 0 : tableBody(1, 4) = "-- N/A --";
1731 : }
1732 1 : if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1733 0 : tableBody(1, 5) = RealToStr(elcc->nominalDiscountRate, 4);
1734 : } else {
1735 1 : tableBody(1, 5) = "-- N/A --";
1736 : }
1737 1 : if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1738 0 : tableBody(1, 6) = RealToStr(elcc->inflation, 4);
1739 : } else {
1740 1 : tableBody(1, 6) = "-- N/A --";
1741 : }
1742 1 : tableBody(1, 7) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear);
1743 1 : tableBody(1, 8) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->serviceDateMonth)], elcc->serviceDateYear);
1744 1 : tableBody(1, 9) = fmt::to_string(elcc->lengthStudyYears);
1745 1 : tableBody(1, 10) = RealToStr(elcc->taxRate, 4);
1746 1 : tableBody(1, 11) = DeprMethodNames[static_cast<int>(elcc->depreciationMethod)];
1747 :
1748 1 : columnWidth = 14; // array assignment - same for all columns
1749 1 : WriteSubtitle(state, "Life-Cycle Cost Parameters");
1750 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1751 1 : if (state.dataSQLiteProcedures->sqlite) {
1752 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1753 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Life-Cycle Cost Parameters");
1754 : }
1755 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1756 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1757 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Life-Cycle Cost Parameters");
1758 : }
1759 :
1760 1 : columnHead.deallocate();
1761 1 : rowHead.deallocate();
1762 1 : columnWidth.deallocate();
1763 1 : tableBody.deallocate();
1764 : //---- Use Price Escalation
1765 1 : numColumns = max(1, elcc->numUsePriceEscalation);
1766 1 : rowHead.allocate(elcc->lengthStudyYears + 2);
1767 1 : columnHead.allocate(numColumns);
1768 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1769 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears + 2);
1770 1 : tableBody = "";
1771 1 : columnHead = "none";
1772 1 : rowHead(1) = "Resource";
1773 1 : rowHead(2) = "Start Date";
1774 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1775 22 : rowHead(iYear + 2) = fmt::to_string(iYear);
1776 : }
1777 4 : for (jObj = 1; jObj <= elcc->numUsePriceEscalation; ++jObj) { // loop through objects not columns to add names
1778 3 : columnHead(jObj) = elcc->UsePriceEscalation(jObj).name;
1779 3 : tableBody(jObj, 1) = GetResourceTypeChar(elcc->UsePriceEscalation(jObj).resource);
1780 6 : tableBody(jObj, 2) = format("{} {}",
1781 3 : UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->UsePriceEscalation(jObj).escalationStartMonth)],
1782 6 : elcc->UsePriceEscalation(jObj).escalationStartYear);
1783 : }
1784 4 : for (jObj = 1; jObj <= elcc->numUsePriceEscalation; ++jObj) {
1785 69 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1786 66 : tableBody(jObj, iYear + 2) = RealToStr(elcc->UsePriceEscalation(jObj).Escalation(iYear), 6);
1787 : }
1788 : }
1789 1 : WriteSubtitle(state, "Use Price Escalation");
1790 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1791 1 : if (state.dataSQLiteProcedures->sqlite) {
1792 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1793 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Price Escalation");
1794 : }
1795 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1796 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1797 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Price Escalation");
1798 : }
1799 1 : columnHead.deallocate();
1800 1 : rowHead.deallocate();
1801 1 : columnWidth.deallocate();
1802 1 : tableBody.deallocate();
1803 : //---- Use Adjustment
1804 1 : if (elcc->numUseAdjustment >= 1) { // only create table if objects used
1805 1 : numColumns = max(1, elcc->numUseAdjustment);
1806 1 : numYears = elcc->lengthStudyYears - (elcc->serviceDateYear - elcc->baseDateYear);
1807 1 : rowHead.allocate(numYears + 1);
1808 1 : columnHead.allocate(numColumns);
1809 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1810 1 : tableBody.allocate(numColumns, numYears + 1);
1811 1 : tableBody = "";
1812 1 : columnHead = "none";
1813 1 : rowHead(1) = "";
1814 21 : for (iYear = 1; iYear <= numYears; ++iYear) {
1815 20 : rowHead(iYear + 1) =
1816 40 : format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->serviceDateMonth)], elcc->serviceDateYear + iYear - 1);
1817 : }
1818 2 : for (jObj = 1; jObj <= elcc->numUseAdjustment; ++jObj) { // loop through objects not columns to add names
1819 1 : columnHead(jObj) = elcc->UseAdjustment(jObj).name;
1820 1 : tableBody(jObj, 1) = GetResourceTypeChar(elcc->UseAdjustment(jObj).resource);
1821 : }
1822 2 : for (jObj = 1; jObj <= elcc->numUseAdjustment; ++jObj) {
1823 21 : for (iYear = 1; iYear <= numYears; ++iYear) {
1824 20 : tableBody(jObj, iYear + 1) = RealToStr(elcc->UseAdjustment(jObj).Adjustment(iYear), 6);
1825 : }
1826 : }
1827 1 : WriteSubtitle(state, "Use Adjustment");
1828 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1829 1 : if (state.dataSQLiteProcedures->sqlite) {
1830 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1831 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Adjustment");
1832 : }
1833 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1834 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1835 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Adjustment");
1836 : }
1837 1 : columnHead.deallocate();
1838 1 : rowHead.deallocate();
1839 1 : columnWidth.deallocate();
1840 1 : tableBody.deallocate();
1841 : }
1842 : //---- Cash Flow for Recurring and Nonrecurring Costs
1843 1 : numColumns = max(1, elcc->numRecurringCosts + elcc->numNonrecurringCost);
1844 1 : rowHead.allocate(elcc->lengthStudyYears + 1);
1845 1 : columnHead.allocate(numColumns);
1846 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1847 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears + 1);
1848 1 : tableBody = "";
1849 1 : rowHead(1) = "";
1850 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1851 22 : rowHead(iYear + 1) =
1852 44 : format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1853 : }
1854 7 : for (jObj = 0; jObj < (elcc->numRecurringCosts + elcc->numNonrecurringCost); ++jObj) {
1855 6 : curCashFlow = CostCategory::Num + jObj;
1856 6 : columnHead(jObj + 1) = elcc->CashFlow[curCashFlow].name;
1857 :
1858 6 : tableBody(jObj + 1, 1) = SourceKindTypeNames[static_cast<int>(elcc->CashFlow[curCashFlow].SourceKind)];
1859 :
1860 138 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1861 132 : tableBody(jObj + 1, iYear + 1) = RealToStr(elcc->CashFlow[curCashFlow].yrAmount(iYear), 2);
1862 : }
1863 : }
1864 1 : WriteSubtitle(state, "Cash Flow for Recurring and Nonrecurring Costs (Without Escalation)");
1865 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1866 1 : if (state.dataSQLiteProcedures->sqlite) {
1867 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(tableBody,
1868 : rowHead,
1869 : columnHead,
1870 : "Life-Cycle Cost Report",
1871 : "Entire Facility",
1872 : "Cash Flow for Recurring and Nonrecurring Costs (Without Escalation)");
1873 : }
1874 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1875 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1876 : tableBody,
1877 : rowHead,
1878 : columnHead,
1879 : "Life-Cycle Cost Report",
1880 : "Entire Facility",
1881 : "Cash Flow for Recurring and Nonrecurring Costs (Without Escalation)");
1882 : }
1883 1 : columnHead.deallocate();
1884 1 : rowHead.deallocate();
1885 1 : columnWidth.deallocate();
1886 1 : tableBody.deallocate();
1887 : //---- Energy and Water Cost Cash Flows (Without Escalation)
1888 1 : numColumns = max(1, elcc->numResourcesUsed + 1);
1889 1 : rowHead.allocate(elcc->lengthStudyYears);
1890 1 : columnHead.allocate(numColumns);
1891 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1892 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears);
1893 1 : tableBody = "";
1894 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1895 22 : rowHead(iYear) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1896 : }
1897 2 : for (jObj = 0; jObj < elcc->numResourcesUsed; ++jObj) {
1898 1 : curCashFlow = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost + jObj;
1899 1 : columnHead(jObj + 1) = elcc->CashFlow[curCashFlow].name;
1900 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1901 22 : tableBody(jObj + 1, iYear) = RealToStr(elcc->CashFlow[curCashFlow].yrAmount(iYear), 2);
1902 : }
1903 : }
1904 1 : columnHead(numColumns) = Total;
1905 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1906 22 : tableBody(jObj + 1, iYear) =
1907 44 : RealToStr(elcc->CashFlow[CostCategory::TotEnergy].yrAmount(iYear) + elcc->CashFlow[CostCategory::Water].yrAmount(iYear), 2);
1908 : }
1909 1 : WriteSubtitle(state, "Energy and Water Cost Cash Flows (Without Escalation)");
1910 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1911 1 : if (state.dataSQLiteProcedures->sqlite) {
1912 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1913 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (Without Escalation)");
1914 : }
1915 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1916 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1917 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (Without Escalation)");
1918 : }
1919 1 : columnHead.deallocate();
1920 1 : rowHead.deallocate();
1921 1 : columnWidth.deallocate();
1922 1 : tableBody.deallocate();
1923 : //---- Energy and Water Cost Cash Flows (With Escalation)
1924 1 : numColumns = max(1, elcc->numResourcesUsed + 1);
1925 1 : rowHead.allocate(elcc->lengthStudyYears);
1926 1 : columnHead.allocate(numColumns);
1927 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1928 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears);
1929 1 : tableBody = "";
1930 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1931 22 : rowHead(iYear) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1932 : }
1933 2 : for (jObj = 0; jObj < elcc->numResourcesUsed; ++jObj) {
1934 1 : curCashFlow = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost + jObj;
1935 1 : columnHead(jObj + 1) = elcc->CashFlow[curCashFlow].name;
1936 1 : auto curResource = elcc->CashFlow[curCashFlow].Resource;
1937 1 : if (elcc->CashFlow[curCashFlow].Resource != DataGlobalConstants::ResourceType::Water) {
1938 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1939 22 : tableBody(jObj + 1, iYear) = RealToStr(elcc->EscalatedEnergy.at(iYear).at(curResource), 2);
1940 : }
1941 : } else { // for water just use the original cashflow since not involved in escalation
1942 0 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1943 0 : tableBody(jObj + 1, iYear) = RealToStr(elcc->CashFlow[curCashFlow].yrAmount(iYear), 2);
1944 : }
1945 : }
1946 : }
1947 1 : columnHead(numColumns) = Total;
1948 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1949 22 : tableBody(jObj + 1, iYear) = RealToStr(elcc->EscalatedTotEnergy(iYear) + elcc->CashFlow[CostCategory::Water].yrAmount(iYear), 2);
1950 : }
1951 1 : WriteSubtitle(state, "Energy and Water Cost Cash Flows (With Escalation)");
1952 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1953 1 : if (state.dataSQLiteProcedures->sqlite) {
1954 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1955 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (With Escalation)");
1956 : }
1957 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1958 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1959 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (With Escalation)");
1960 : }
1961 1 : columnHead.deallocate();
1962 1 : rowHead.deallocate();
1963 1 : columnWidth.deallocate();
1964 1 : tableBody.deallocate();
1965 :
1966 : //---- Capital Cash Flow by Category
1967 1 : rowHead.allocate(elcc->lengthStudyYears);
1968 1 : columnHead.allocate(4);
1969 1 : columnWidth.allocate(4);
1970 1 : columnWidth = 14; // array assignment - same for all columns
1971 1 : tableBody.allocate(4, elcc->lengthStudyYears);
1972 1 : tableBody = "";
1973 4 : for (int CostCategory = CostCategory::Construction, tableColumnIndex = 1; CostCategory <= CostCategory::OtherCapital;
1974 : ++tableColumnIndex, ++CostCategory) {
1975 3 : columnHead(tableColumnIndex) = CostCategoryNamesNoSpace[static_cast<int>(CostCategory)];
1976 : }
1977 1 : columnHead(4) = Total;
1978 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1979 22 : rowHead(iYear) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1980 110 : for (int CostCategory = CostCategory::Construction, tableColumnIndex = 1; CostCategory <= CostCategory::TotCaptl;
1981 : ++tableColumnIndex, ++CostCategory) {
1982 88 : tableBody(tableColumnIndex, iYear) = RealToStr(elcc->CashFlow[CostCategory].yrAmount(iYear), 2);
1983 : }
1984 : }
1985 1 : WriteSubtitle(state, "Capital Cash Flow by Category (Without Escalation)");
1986 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1987 1 : if (state.dataSQLiteProcedures->sqlite) {
1988 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1989 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Capital Cash Flow by Category (Without Escalation)");
1990 : }
1991 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1992 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1993 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Capital Cash Flow by Category (Without Escalation)");
1994 : }
1995 1 : columnHead.deallocate();
1996 1 : rowHead.deallocate();
1997 1 : columnWidth.deallocate();
1998 1 : tableBody.deallocate();
1999 : //---- Operating Cash Flow by Category (Without Escalation)
2000 1 : rowHead.allocate(elcc->lengthStudyYears);
2001 1 : columnHead.allocate(10);
2002 1 : columnWidth.allocate(10);
2003 1 : columnWidth = 14; // array assignment - same for all columns
2004 1 : tableBody.allocate(10, elcc->lengthStudyYears);
2005 1 : tableBody = "";
2006 10 : for (int CostCategory = CostCategory::Maintenance, tableColumnIndex = 1; CostCategory <= CostCategory::Energy;
2007 : ++tableColumnIndex, ++CostCategory) {
2008 9 : columnHead(tableColumnIndex) = CostCategoryNamesNoSpace[CostCategory];
2009 : }
2010 1 : columnHead(10) = Total;
2011 :
2012 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2013 22 : rowHead(iYear) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2014 242 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotOper; ++CashFlowCostCategory) {
2015 220 : tableBody(CashFlowCostCategory + 1, iYear) = RealToStr(elcc->CashFlow[CashFlowCostCategory].yrAmount(iYear), 2);
2016 : }
2017 : }
2018 1 : WriteSubtitle(state, "Operating Cash Flow by Category (Without Escalation)");
2019 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2020 1 : if (state.dataSQLiteProcedures->sqlite) {
2021 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2022 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (Without Escalation)");
2023 : }
2024 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2025 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2026 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (Without Escalation)");
2027 : }
2028 1 : columnHead.deallocate();
2029 1 : rowHead.deallocate();
2030 1 : columnWidth.deallocate();
2031 1 : tableBody.deallocate();
2032 : //---- Operating Cash Flow by Category (With Escalation)
2033 1 : rowHead.allocate(elcc->lengthStudyYears);
2034 1 : columnHead.allocate(10);
2035 1 : columnWidth.allocate(10);
2036 1 : columnWidth = 14; // array assignment - same for all columns
2037 1 : tableBody.allocate(10, elcc->lengthStudyYears);
2038 1 : tableBody = "";
2039 10 : for (int CostCategory = CostCategory::Maintenance, tableColumnIndex = 1; CostCategory <= CostCategory::Energy;
2040 : ++tableColumnIndex, ++CostCategory) {
2041 9 : columnHead(tableColumnIndex) = CostCategoryNamesNoSpace[CostCategory];
2042 : }
2043 1 : columnHead(10) = Total;
2044 :
2045 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2046 22 : rowHead(iYear) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2047 198 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::Water; ++CashFlowCostCategory) {
2048 176 : tableBody(CashFlowCostCategory + 1, iYear) = RealToStr(elcc->CashFlow[CashFlowCostCategory].yrAmount(iYear), 2);
2049 : }
2050 22 : tableBody(9, iYear) = RealToStr(elcc->EscalatedTotEnergy(iYear), 2);
2051 22 : Real64 yearly_total_cost = elcc->CashFlow[CostCategory::TotOper].yrAmount(iYear) + elcc->EscalatedTotEnergy(iYear) -
2052 22 : elcc->CashFlow[CostCategory::TotEnergy].yrAmount(iYear);
2053 22 : tableBody(10, iYear) = RealToStr(yearly_total_cost, 2);
2054 : }
2055 1 : WriteSubtitle(state, "Operating Cash Flow by Category (With Escalation)");
2056 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2057 1 : if (state.dataSQLiteProcedures->sqlite) {
2058 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2059 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (With Escalation)");
2060 : }
2061 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2062 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2063 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (With Escalation)");
2064 : }
2065 1 : columnHead.deallocate();
2066 1 : rowHead.deallocate();
2067 1 : columnWidth.deallocate();
2068 1 : tableBody.deallocate();
2069 : //---- DEBUG ONLY - Monthly Cash Flows
2070 1 : bool showMonthlyCashFlows = false;
2071 1 : if (showMonthlyCashFlows) {
2072 0 : rowHead.allocate(elcc->lengthStudyTotalMonths);
2073 0 : columnHead.allocate(elcc->numCashFlow);
2074 0 : columnWidth.allocate(elcc->numCashFlow);
2075 0 : tableBody.allocate(elcc->numCashFlow, elcc->lengthStudyTotalMonths);
2076 0 : tableBody = "";
2077 0 : columnHead(1) = "mnt";
2078 0 : columnHead(2) = "rpr";
2079 0 : columnHead(3) = "opr";
2080 0 : columnHead(4) = "repl";
2081 0 : columnHead(5) = "mOvhl";
2082 0 : columnHead(6) = "MOvhl";
2083 0 : columnHead(7) = "oOpr";
2084 0 : columnHead(8) = "cons";
2085 0 : columnHead(9) = "slvg";
2086 0 : columnHead(10) = "oCap";
2087 0 : columnHead(11) = "H2O";
2088 0 : columnHead(12) = "ene";
2089 0 : columnHead(13) = "tEne";
2090 0 : columnHead(14) = "tOpr";
2091 0 : columnHead(15) = "tCap";
2092 0 : columnHead(16) = "Totl";
2093 0 : for (jObj = CostCategory::Num; jObj < elcc->numCashFlow; ++jObj) {
2094 0 : columnHead(jObj + 1) = elcc->CashFlow[jObj].name;
2095 : }
2096 0 : for (kMonth = 1; kMonth <= elcc->lengthStudyTotalMonths; ++kMonth) {
2097 0 : rowHead(kMonth) = format("{} {}",
2098 0 : UtilityRoutines::MonthNamesCC[static_cast<int>(1 + (kMonth + elcc->baseDateMonth - 2) % 12) - 1],
2099 0 : elcc->baseDateYear + int((kMonth - 1) / 12));
2100 : }
2101 0 : for (kMonth = 1; kMonth <= elcc->lengthStudyTotalMonths; ++kMonth) {
2102 0 : for (jObj = 0; jObj < elcc->numCashFlow; ++jObj) {
2103 0 : tableBody(jObj + 1, kMonth) = RealToStr(elcc->CashFlow[jObj].mnAmount(kMonth), 2);
2104 : }
2105 : }
2106 0 : WriteSubtitle(state, "DEBUG ONLY - Monthly Cash Flows");
2107 0 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2108 0 : if (state.dataSQLiteProcedures->sqlite) {
2109 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2110 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "DEBUG ONLY - Monthly Cash Flows");
2111 : }
2112 0 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2113 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2114 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "DEBUG ONLY - Monthly Cash Flows");
2115 : }
2116 0 : columnHead.deallocate();
2117 0 : rowHead.deallocate();
2118 0 : columnWidth.deallocate();
2119 0 : tableBody.deallocate();
2120 : }
2121 : //---- Monthly Total Cash Flow
2122 1 : rowHead.allocate(elcc->lengthStudyYears);
2123 1 : columnHead.allocate(12);
2124 1 : columnWidth.allocate(12);
2125 1 : columnWidth = 14; // array assignment - same for all columns
2126 1 : tableBody.allocate(12, elcc->lengthStudyYears);
2127 1 : tableBody = "";
2128 13 : for (kMonth = 1; kMonth <= 12; ++kMonth) {
2129 12 : columnHead(kMonth) = UtilityRoutines::MonthNamesCC[static_cast<int>(kMonth - 1)];
2130 : }
2131 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2132 22 : rowHead(iYear) = fmt::to_string(elcc->baseDateYear + iYear - 1);
2133 : }
2134 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2135 286 : for (kMonth = 1; kMonth <= 12; ++kMonth) {
2136 264 : tableBody(kMonth, iYear) = RealToStr(elcc->CashFlow[CostCategory::TotGrand].mnAmount((iYear - 1) * 12 + kMonth), 2);
2137 : }
2138 : }
2139 1 : WriteSubtitle(state, "Monthly Total Cash Flow (Without Escalation)");
2140 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2141 1 : if (state.dataSQLiteProcedures->sqlite) {
2142 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2143 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Monthly Total Cash Flow (Without Escalation)");
2144 : }
2145 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2146 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2147 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Monthly Total Cash Flow (Without Escalation)");
2148 : }
2149 1 : columnHead.deallocate();
2150 1 : rowHead.deallocate();
2151 1 : columnWidth.deallocate();
2152 1 : tableBody.deallocate();
2153 : //---- Present Value for Recurring, Nonrecurring and Energy Costs
2154 1 : numRows = max(1, elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed);
2155 1 : rowHead.allocate(numRows + 1);
2156 1 : columnHead.allocate(5);
2157 1 : columnWidth.allocate(5);
2158 1 : columnWidth = 14; // array assignment - same for all columns
2159 1 : tableBody.allocate(5, numRows + 1);
2160 1 : tableBody = "";
2161 1 : columnHead(1) = "Category";
2162 1 : columnHead(2) = "Kind";
2163 1 : columnHead(3) = "Cost";
2164 1 : columnHead(4) = "Present Value";
2165 1 : columnHead(5) = "Present Value Factor";
2166 1 : totalPV = 0.0;
2167 1 : rowHead(numRows + 1) = TotalUC;
2168 8 : for (jObj = 0; jObj < (elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed); ++jObj) {
2169 7 : offset = CostCategory::Num;
2170 7 : rowHead(jObj + 1) = elcc->CashFlow[offset + jObj].name;
2171 7 : switch (elcc->CashFlow[offset + jObj].Category) {
2172 7 : case CostCategory::Maintenance:
2173 : case CostCategory::Repair:
2174 : case CostCategory::Operation:
2175 : case CostCategory::Replacement:
2176 : case CostCategory::MinorOverhaul:
2177 : case CostCategory::MajorOverhaul:
2178 : case CostCategory::OtherOperational:
2179 : case CostCategory::Construction:
2180 : case CostCategory::Salvage:
2181 : case CostCategory::OtherCapital:
2182 : case CostCategory::Water:
2183 : case CostCategory::Energy: {
2184 7 : tableBody(1, jObj + 1) = CostCategoryNames[static_cast<int>(elcc->CashFlow[offset + jObj].Category)];
2185 7 : break;
2186 : }
2187 0 : default:
2188 0 : tableBody(1, jObj + 1) = "-";
2189 0 : break;
2190 : }
2191 7 : switch (elcc->CashFlow[offset + jObj].SourceKind) {
2192 6 : case SourceKindType::Nonrecurring:
2193 : case SourceKindType::Recurring: {
2194 6 : tableBody(2, jObj + 1) = SourceKindTypeNames[static_cast<int>(elcc->CashFlow[offset + jObj].SourceKind)];
2195 6 : break;
2196 : }
2197 1 : case SourceKindType::Resource: {
2198 1 : if (elcc->CashFlow[offset + jObj].Category == CostCategory::Water) {
2199 0 : tableBody(2, jObj + 1) = ResourceCostCategoryNames[static_cast<int>(ResourceCostCategory::Water)];
2200 : } else {
2201 1 : tableBody(2, jObj + 1) = ResourceCostCategoryNames[static_cast<int>(ResourceCostCategory::Energy)];
2202 : }
2203 1 : break;
2204 : }
2205 0 : default: {
2206 0 : tableBody(2, jObj + 1) = "-";
2207 0 : break;
2208 : }
2209 : }
2210 7 : tableBody(3, jObj + 1) = RealToStr(elcc->CashFlow[offset + jObj].orginalCost, 2);
2211 7 : tableBody(4, jObj + 1) = RealToStr(elcc->CashFlow[offset + jObj].presentValue, 2);
2212 7 : totalPV += elcc->CashFlow[offset + jObj].presentValue;
2213 7 : if (elcc->CashFlow[offset + jObj].orginalCost != 0.0) {
2214 7 : tableBody(5, jObj + 1) = RealToStr(elcc->CashFlow[offset + jObj].presentValue / elcc->CashFlow[offset + jObj].orginalCost, 4);
2215 : } else {
2216 0 : tableBody(5, jObj + 1) = "-";
2217 : }
2218 : }
2219 1 : tableBody(4, numRows + 1) = RealToStr(totalPV, 2);
2220 1 : WriteSubtitle(state, "Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)");
2221 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2222 1 : if (state.dataSQLiteProcedures->sqlite) {
2223 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2224 : tableBody,
2225 : rowHead,
2226 : columnHead,
2227 : "Life-Cycle Cost Report",
2228 : "Entire Facility",
2229 : "Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)");
2230 : }
2231 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2232 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2233 : tableBody,
2234 : rowHead,
2235 : columnHead,
2236 : "Life-Cycle Cost Report",
2237 : "Entire Facility",
2238 : "Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)");
2239 : }
2240 1 : columnHead.deallocate();
2241 1 : rowHead.deallocate();
2242 1 : columnWidth.deallocate();
2243 1 : tableBody.deallocate();
2244 : //---- Present Value by Category
2245 1 : rowHead.allocate(16);
2246 1 : columnHead.allocate(1);
2247 1 : columnWidth.allocate(1);
2248 1 : columnWidth = 14; // array assignment - same for all columns
2249 1 : tableBody.allocate(1, 16);
2250 1 : tableBody = "";
2251 17 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotGrand; ++CashFlowCostCategory) {
2252 16 : rowHead(CashFlowCostCategory + 1) = CostCategoryNames[CashFlowCostCategory];
2253 : }
2254 1 : columnHead(1) = "Present Value";
2255 :
2256 17 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotGrand; ++CashFlowCostCategory) {
2257 16 : tableBody(1, CashFlowCostCategory + 1) = RealToStr(elcc->CashFlow[CashFlowCostCategory].presentValue, 2);
2258 : }
2259 :
2260 1 : WriteSubtitle(state, "Present Value by Category");
2261 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2262 1 : if (state.dataSQLiteProcedures->sqlite) {
2263 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2264 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Category");
2265 : }
2266 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2267 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2268 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Category");
2269 : }
2270 1 : columnHead.deallocate();
2271 1 : rowHead.deallocate();
2272 1 : columnWidth.deallocate();
2273 1 : tableBody.deallocate();
2274 : //---- Present Value by Year
2275 1 : rowHead.allocate(elcc->lengthStudyYears + 1);
2276 1 : columnHead.allocate(3);
2277 1 : columnWidth.allocate(3);
2278 1 : columnWidth = 14; // array assignment - same for all columns
2279 1 : tableBody.allocate(3, elcc->lengthStudyYears + 1);
2280 1 : tableBody = "";
2281 1 : columnHead(1) = "Total Cost (Without Escalation)";
2282 1 : columnHead(2) = "Total Cost (With Escalation)";
2283 1 : columnHead(3) = "Present Value of Costs";
2284 :
2285 1 : totalPV = 0.0;
2286 23 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2287 22 : rowHead(iYear) = format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2288 22 : tableBody(1, iYear) = RealToStr(elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear), 2);
2289 : // adjust for escalated energy costs
2290 22 : Real64 yearly_total_cost = elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear) + elcc->EscalatedTotEnergy(iYear) -
2291 22 : elcc->CashFlow[CostCategory::TotEnergy].yrAmount(iYear);
2292 22 : tableBody(2, iYear) = RealToStr(yearly_total_cost, 2);
2293 22 : tableBody(3, iYear) = RealToStr(elcc->CashFlow[CostCategory::TotGrand].yrPresVal(iYear), 2);
2294 22 : totalPV += elcc->CashFlow[CostCategory::TotGrand].yrPresVal(iYear);
2295 : }
2296 :
2297 1 : rowHead(elcc->lengthStudyYears + 1) = TotalUC;
2298 1 : tableBody(3, elcc->lengthStudyYears + 1) = RealToStr(totalPV, 2);
2299 :
2300 1 : WriteSubtitle(state, "Present Value by Year");
2301 1 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2302 1 : if (state.dataSQLiteProcedures->sqlite) {
2303 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2304 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Year");
2305 : }
2306 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2307 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2308 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Year");
2309 : }
2310 1 : columnHead.deallocate();
2311 1 : rowHead.deallocate();
2312 1 : columnWidth.deallocate();
2313 1 : tableBody.deallocate();
2314 : //---- After Tax Estimate
2315 1 : if (elcc->taxRate != 0.0) {
2316 0 : rowHead.allocate(elcc->lengthStudyYears + 1);
2317 0 : columnHead.allocate(5);
2318 0 : columnWidth.allocate(5);
2319 0 : columnWidth = 14; // array assignment - same for all columns
2320 0 : tableBody.allocate(5, elcc->lengthStudyYears + 1);
2321 0 : tableBody = "";
2322 0 : columnHead(1) = "Depreciated Capital";
2323 0 : columnHead(2) = "Taxable Income";
2324 0 : columnHead(3) = "Income Taxes";
2325 0 : columnHead(4) = "After Tax Cash Flow";
2326 0 : columnHead(5) = "After Tax Present Value";
2327 :
2328 0 : totalPV = 0.0;
2329 0 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2330 0 : rowHead(iYear) =
2331 0 : format("{} {}", UtilityRoutines::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2332 0 : tableBody(1, iYear) = RealToStr(elcc->DepreciatedCapital(iYear), 2);
2333 0 : tableBody(2, iYear) = RealToStr(elcc->TaxableIncome(iYear), 2);
2334 0 : tableBody(3, iYear) = RealToStr(elcc->Taxes(iYear), 2);
2335 0 : tableBody(4, iYear) = RealToStr(elcc->AfterTaxCashFlow(iYear), 2);
2336 0 : tableBody(5, iYear) = RealToStr(elcc->AfterTaxPresentValue(iYear), 2);
2337 0 : totalPV += elcc->AfterTaxPresentValue(iYear);
2338 : }
2339 :
2340 0 : rowHead(elcc->lengthStudyYears + 1) = TotalUC;
2341 0 : tableBody(5, elcc->lengthStudyYears + 1) = RealToStr(totalPV, 2);
2342 :
2343 0 : WriteSubtitle(state, "After Tax Estimate");
2344 0 : WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2345 0 : if (state.dataSQLiteProcedures->sqlite) {
2346 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2347 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "After Tax Estimate");
2348 : }
2349 0 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2350 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2351 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "After Tax Estimate");
2352 : }
2353 0 : columnHead.deallocate();
2354 0 : rowHead.deallocate();
2355 0 : columnWidth.deallocate();
2356 0 : tableBody.deallocate();
2357 : }
2358 : }
2359 1 : }
2360 :
2361 2313 : } // namespace EnergyPlus::EconomicLifeCycleCost
|