Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, 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 : // ObjexxFCL Headers
49 :
50 : // EnergyPlus Headers
51 : #include <EnergyPlus/Data/EnergyPlusData.hh>
52 : #include <EnergyPlus/DataEnvironment.hh>
53 : #include <EnergyPlus/DataGlobalConstants.hh>
54 : #include <EnergyPlus/DataHVACGlobals.hh>
55 : #include <EnergyPlus/DataIPShortCuts.hh>
56 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
57 : #include <EnergyPlus/OutputProcessor.hh>
58 : #include <EnergyPlus/PollutionModule.hh>
59 : #include <EnergyPlus/ScheduleManager.hh>
60 : #include <EnergyPlus/UtilityRoutines.hh>
61 :
62 : namespace EnergyPlus::Pollution {
63 : // Module containing the pollution calculation routines
64 :
65 : // MODULE INFORMATION:
66 : // AUTHOR Richard J. Liesen (RJL)
67 : // DATE WRITTEN August 2002
68 : // MODIFIED January 17, 2004 - J Glazer - Added source energy support including schedules for source energy
69 : // January 2008 - L Lawrie - implementing schedule fields for all emission factors.
70 : // RE-ENGINEERED December 2003 RJL
71 :
72 : // PURPOSE OF THIS MODULE:
73 : // To encapsulate the data and algorithms required to
74 : // calculate the pollution, and carbon eqiuvalent for the Energy consumed
75 :
76 : // METHODOLOGY EMPLOYED:
77 : // The methodology employed is to calculate the
78 : // source pollution from building energy consumption.
79 : // PURPOSE:= Takes the Energy from the various sources and
80 : // calculates the Environmental Impact Factors.
81 : // STEP 1: We begin with the output expressing the energy
82 : // STEP 2: The energy used by types: must be converted back
83 : // to source fuel types (fossil or electricity) via User Input.
84 : // STEP 3: All energy numbers have been converted to units of MJ's or 1x10^6 Joules.
85 : // STEP 4: Environmental Impact Factors are calculated from Coefficients
86 :
87 : // MODULE VARIABLE DECLARATIONS:
88 : // Total for all of the Pollutants
89 : // Total Carbon Equivalent Components
90 : // !Fuel Types
91 : // Total Carbon Equivalent Coeffs
92 : // Purchased Efficiencies
93 :
94 : // Fuel Types used with the Pollution Factors
95 : // Facility Meter Indexes
96 : // Facility Meter Values used in Pollution Calcs
97 :
98 615229 : void CalculatePollution(EnergyPlusData &state)
99 : {
100 :
101 : // SUBROUTINE INFORMATION:
102 : // AUTHOR Richard Liesen
103 : // DATE WRITTEN August 2002
104 : // MODIFIED na
105 : // RE-ENGINEERED December 2003 RJL
106 :
107 : // PURPOSE OF THIS SUBROUTINE:
108 : // This subroutine is the main driver for the pollution calculation
109 :
110 : // METHODOLOGY EMPLOYED:
111 : // Uses the status flags to trigger events.
112 :
113 615229 : if (!state.dataPollution->PollutionReportSetup) return;
114 :
115 : // Call the Routine to Read the Energy Values from the EnergyPlus Meters
116 615229 : ReadEnergyMeters(state);
117 :
118 : // Call the routine that takes the fuel data and calculates the
119 : // Pollution for each fuel type.
120 615229 : CalcPollution(state);
121 : }
122 :
123 : // Get Input Section of the Module
124 : //******************************************************************************
125 :
126 795 : void SetupPollutionCalculations(EnergyPlusData &state)
127 : {
128 :
129 : // SUBROUTINE INFORMATION:
130 : // AUTHOR Richard Liesen
131 : // DATE WRITTEN August 2002
132 : // MODIFIED na
133 : // RE-ENGINEERED December 2003 RJL; August 2008 LKL - more standard getinput
134 :
135 : // PURPOSE OF THIS SUBROUTINE:
136 : // This subroutine is the input routines and Get routines
137 :
138 : // METHODOLOGY EMPLOYED:
139 : // Uses the status flags to trigger events.
140 :
141 : // Using/Aliasing
142 : using ScheduleManager::GetScheduleIndex;
143 :
144 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
145 : int NumPolluteRpt;
146 : int NumAlphas;
147 : int NumNums;
148 : int Loop;
149 : int IOStat;
150 795 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
151 :
152 : // First determine if the Pollution reporting has been triggered, and is not exit.
153 795 : cCurrentModuleObject = "Output:EnvironmentalImpactFactors";
154 795 : NumPolluteRpt = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
155 795 : state.dataPollution->PollutionReportSetup = true;
156 :
157 856 : for (Loop = 1; Loop <= NumPolluteRpt; ++Loop) {
158 :
159 122 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
160 : cCurrentModuleObject,
161 : Loop,
162 61 : state.dataIPShortCut->cAlphaArgs,
163 : NumAlphas,
164 61 : state.dataIPShortCut->rNumericArgs,
165 : NumNums,
166 : IOStat,
167 61 : state.dataIPShortCut->lNumericFieldBlanks,
168 61 : state.dataIPShortCut->lAlphaFieldBlanks,
169 61 : state.dataIPShortCut->cAlphaFieldNames,
170 61 : state.dataIPShortCut->cNumericFieldNames);
171 :
172 : // Call this routine in the Output Processor to setup the correct Facility energy meters that are
173 : // necessary to make sure that the Meter file is opened and written to by the OP so that time stamps
174 : // and the like are happening as expected.
175 61 : OutputProcessor::ReportFreq freq = OutputProcessor::ReportFreq::Simulation;
176 :
177 122 : if (!state.dataIPShortCut->lAlphaFieldBlanks(1) &&
178 : (freq = static_cast<OutputProcessor::ReportFreq>(
179 122 : getEnumValue(OutputProcessor::reportFreqNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(1))))) ==
180 : OutputProcessor::ReportFreq::Invalid) {
181 0 : ShowSevereError(state, format("Invalid reporting frequency {}", state.dataIPShortCut->cAlphaArgs(1)));
182 0 : continue;
183 : }
184 :
185 61 : InitPollutionMeterReporting(state, freq);
186 : }
187 795 : }
188 :
189 795 : void GetPollutionFactorInput(EnergyPlusData &state)
190 : {
191 :
192 : // SUBROUTINE INFORMATION:
193 : // AUTHOR Linda Lawrie
194 : // DATE WRITTEN August 2008
195 :
196 : // PURPOSE OF THIS SUBROUTINE:
197 : // SetupPollutionCalculation must be called after meters are initialized. This caused a problem
198 : // in runs so have added this routine to allow central get for most inputs.
199 :
200 795 : constexpr std::string_view routineName = "GetPollutionFactorInput";
201 :
202 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
203 : int NumAlphas;
204 : int NumNums;
205 : int IOStat;
206 795 : bool ErrorsFound = false;
207 :
208 795 : auto &ip = state.dataInputProcessing->inputProcessor;
209 795 : auto &ipsc = state.dataIPShortCut;
210 795 : auto &pm = state.dataPollution;
211 :
212 795 : if (!pm->GetInputFlagPollution) return; // Input already gotten
213 795 : pm->GetInputFlagPollution = false;
214 :
215 795 : ipsc->cCurrentModuleObject = "EnvironmentalImpactFactors";
216 795 : pm->NumEnvImpactFactors = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
217 :
218 795 : if (pm->NumEnvImpactFactors > 0) {
219 : // Now find and load all of the user inputs and factors.
220 152 : ip->getObjectItem(state,
221 76 : ipsc->cCurrentModuleObject,
222 : 1,
223 76 : ipsc->cAlphaArgs,
224 : NumAlphas,
225 76 : ipsc->rNumericArgs,
226 : NumNums,
227 : IOStat,
228 76 : ipsc->lNumericFieldBlanks,
229 76 : ipsc->lAlphaFieldBlanks,
230 76 : ipsc->cAlphaFieldNames,
231 76 : ipsc->cNumericFieldNames);
232 719 : } else if (pm->PollutionReportSetup) {
233 0 : ShowWarningError(state, format("{}: not entered. Values will be defaulted.", ipsc->cCurrentModuleObject));
234 : }
235 :
236 795 : pm->PurchHeatEffic = 0.3;
237 795 : pm->PurchCoolCOP = 3.0;
238 795 : pm->SteamConvEffic = 0.25;
239 795 : pm->CarbonEquivN2O = 0.0;
240 795 : pm->CarbonEquivCH4 = 0.0;
241 795 : pm->CarbonEquivCO2 = 0.0;
242 :
243 795 : if (pm->NumEnvImpactFactors > 0) {
244 : // If Heating Efficiency defined by the User is negative or zero then a default of 30% will be assigned.
245 76 : if (ipsc->rNumericArgs(1) > 0.0) {
246 76 : pm->PurchHeatEffic = ipsc->rNumericArgs(1);
247 : }
248 :
249 : // If COP defined by the User is negative or zero then a default of 3.0 will be assigned.
250 76 : if (ipsc->rNumericArgs(2) > 0.0) {
251 76 : pm->PurchCoolCOP = ipsc->rNumericArgs(2);
252 : }
253 :
254 : // If Steam Conversion Efficiency defined by the User is negative or zero then a default of 25% will be assigned.
255 76 : if (ipsc->rNumericArgs(3) > 0.0) {
256 76 : pm->SteamConvEffic = ipsc->rNumericArgs(3);
257 : }
258 :
259 : // Load the Total Carbon Equivalent Pollution Factor coefficients
260 76 : pm->CarbonEquivN2O = ipsc->rNumericArgs(4);
261 76 : pm->CarbonEquivCH4 = ipsc->rNumericArgs(5);
262 76 : pm->CarbonEquivCO2 = ipsc->rNumericArgs(6);
263 : }
264 :
265 : // Compare all of the Fuel Factors and compare to PollutionCalculationFactors List
266 795 : ipsc->cCurrentModuleObject = "FuelFactors";
267 795 : pm->NumFuelFactors = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
268 :
269 1008 : for (int Loop = 1; Loop <= state.dataPollution->NumFuelFactors; ++Loop) {
270 : // Now find and load all of the user inputs and factors.
271 426 : ip->getObjectItem(state,
272 213 : ipsc->cCurrentModuleObject,
273 : Loop,
274 213 : ipsc->cAlphaArgs,
275 : NumAlphas,
276 213 : ipsc->rNumericArgs,
277 : NumNums,
278 : IOStat,
279 213 : ipsc->lNumericFieldBlanks,
280 213 : ipsc->lAlphaFieldBlanks,
281 213 : ipsc->cAlphaFieldNames,
282 213 : ipsc->cNumericFieldNames);
283 :
284 213 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
285 :
286 213 : PollFuel pollFuel = static_cast<PollFuel>(getEnumValue(pollFuelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(1))));
287 213 : if (pollFuel == PollFuel::Invalid) {
288 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
289 0 : ErrorsFound = true;
290 0 : continue;
291 : }
292 :
293 213 : pm->pollFuelFactorList.push_back(pollFuel);
294 :
295 213 : auto &pollCoeff = pm->pollCoeffs[(int)pollFuel];
296 213 : Constant::eFuel fuel = pollFuel2fuel[(int)pollFuel];
297 :
298 213 : if (pollCoeff.used) {
299 0 : ShowWarningError(
300 0 : state, format("{}: {} already entered. Previous entry will be used.", ipsc->cCurrentModuleObject, Constant::eFuelNames[(int)fuel]));
301 0 : continue;
302 : }
303 :
304 213 : pollCoeff.used = true;
305 :
306 213 : pollCoeff.sourceCoeff = ipsc->rNumericArgs(1);
307 213 : if (!ipsc->lAlphaFieldBlanks(2)) {
308 2 : pollCoeff.sourceSchedNum = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(2));
309 2 : if (pollCoeff.sourceSchedNum == 0) {
310 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
311 0 : ErrorsFound = true;
312 2 : } else if (!ScheduleManager::CheckScheduleValueMinMax(state, pollCoeff.sourceSchedNum, true, 0.0)) {
313 0 : ShowSevereError(state,
314 0 : format("{}: {}, invalid {}=\"{}\" invalid values.",
315 0 : ipsc->cCurrentModuleObject,
316 0 : Constant::eFuelNames[(int)fuel],
317 0 : ipsc->cAlphaFieldNames(2),
318 0 : ipsc->cAlphaArgs(2)));
319 0 : ShowContinueError(state, "Schedule values must be (>=0.).");
320 0 : ErrorsFound = true;
321 : }
322 : }
323 :
324 3621 : for (int iPollutant = 0; iPollutant < (int)Pollutant::Num; ++iPollutant) {
325 3408 : pollCoeff.pollutantCoeffs[iPollutant] = ipsc->rNumericArgs(iPollutant + 2);
326 3408 : if (!ipsc->lAlphaFieldBlanks(iPollutant + 3)) {
327 :
328 0 : pollCoeff.pollutantSchedNums[iPollutant] = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(iPollutant + 3));
329 0 : if (pollCoeff.pollutantSchedNums[iPollutant] == 0) {
330 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(iPollutant + 3), ipsc->cAlphaArgs(iPollutant + 3));
331 0 : ErrorsFound = true;
332 0 : } else if (!ScheduleManager::CheckScheduleValueMinMax(state, pollCoeff.pollutantSchedNums[iPollutant], true, 0.0)) {
333 0 : ShowSevereError(state,
334 0 : format("{}: {}, invalid {}=\"{}\" invalid values.",
335 0 : ipsc->cCurrentModuleObject,
336 0 : Constant::eFuelNames[(int)fuel],
337 0 : ipsc->cAlphaFieldNames(iPollutant + 3),
338 0 : ipsc->cAlphaArgs(iPollutant + 3)));
339 0 : ShowContinueError(state, "Schedule values must be (>=0.).");
340 0 : ErrorsFound = true;
341 : }
342 : }
343 : } // for (iPollutant)
344 :
345 : } // End of the NumEnergyTypes Do Loop
346 :
347 795 : if (pm->PollutionReportSetup) { // only do this if reporting on the pollution
348 : // Need to go through all of the Fuel Types and make sure a Fuel Factor was found for each type of energy being simulated
349 : // Check for Electricity
350 0 : if (!pm->pollCoeffs[(int)PollFuel::Electricity].used && ((pm->facilityMeterNums[(int)PollFacilityMeter::Electricity] > 0) ||
351 0 : (pm->facilityMeterNums[(int)PollFacilityMeter::ElectricityProduced] > 0) ||
352 0 : (pm->facilityMeterNums[(int)PollFacilityMeter::CoolPurchased] > 0))) {
353 0 : ShowSevereError(state,
354 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for ELECTRICITY", ipsc->cCurrentModuleObject));
355 0 : ErrorsFound = true;
356 : }
357 :
358 : // Check for Natural Gas
359 0 : if (!pm->pollCoeffs[(int)PollFuel::NaturalGas].used &&
360 0 : ((pm->facilityMeterNums[(int)PollFacilityMeter::NaturalGas] > 0) || (pm->facilityMeterNums[(int)PollFacilityMeter::HeatPurchased] > 0) ||
361 0 : (pm->facilityMeterNums[(int)PollFacilityMeter::Steam] > 0))) {
362 0 : ShowSevereError(state,
363 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for NATURAL GAS", ipsc->cCurrentModuleObject));
364 0 : ErrorsFound = true;
365 : }
366 : // Check for FuelOilNo2 (Residual Oil)
367 0 : if (!pm->pollCoeffs[(int)PollFuel::FuelOil2].used && (pm->facilityMeterNums[(int)PollFacilityMeter::FuelOil2] > 0)) {
368 0 : ShowSevereError(state,
369 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for FUEL OIL #2", ipsc->cCurrentModuleObject));
370 0 : ErrorsFound = true;
371 : }
372 : // Check for FuelOilNo1 (Distillate Oil)
373 0 : if (!pm->pollCoeffs[(int)PollFuel::FuelOil1].used && (pm->facilityMeterNums[(int)PollFacilityMeter::FuelOil1] > 0)) {
374 0 : ShowSevereError(state,
375 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for FUEL OIL #1", ipsc->cCurrentModuleObject));
376 0 : ErrorsFound = true;
377 : }
378 : // Check for Coal
379 0 : if (!pm->pollCoeffs[(int)PollFuel::Coal].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Coal] > 0)) {
380 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for COAL", ipsc->cCurrentModuleObject));
381 0 : ErrorsFound = true;
382 : }
383 : // Check for Gasoline
384 0 : if (!pm->pollCoeffs[(int)PollFuel::Gasoline].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Gasoline] > 0)) {
385 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for GASOLINE", ipsc->cCurrentModuleObject));
386 0 : ErrorsFound = true;
387 : }
388 : // Check for Propane
389 0 : if (!pm->pollCoeffs[(int)PollFuel::Propane].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Propane] > 0)) {
390 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for PROPANE", ipsc->cCurrentModuleObject));
391 0 : ErrorsFound = true;
392 : }
393 : // Check for Diesel
394 0 : if (!pm->pollCoeffs[(int)PollFuel::Diesel].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Diesel] > 0)) {
395 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for DIESEL", ipsc->cCurrentModuleObject));
396 0 : ErrorsFound = true;
397 : }
398 : // Check for OtherFuel1
399 0 : if (!pm->pollCoeffs[(int)PollFuel::OtherFuel1].used && (pm->facilityMeterNums[(int)PollFacilityMeter::OtherFuel1] > 0)) {
400 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for OTHERFUEL1", ipsc->cCurrentModuleObject));
401 0 : ErrorsFound = true;
402 : }
403 : // Check for OtherFuel2
404 0 : if (!pm->pollCoeffs[(int)PollFuel::OtherFuel2].used && (pm->facilityMeterNums[(int)PollFacilityMeter::OtherFuel2] > 0)) {
405 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for OTHERFUEL2", ipsc->cCurrentModuleObject));
406 0 : ErrorsFound = true;
407 : }
408 : }
409 :
410 795 : if (ErrorsFound) {
411 0 : ShowFatalError(state, "Errors found in getting Pollution Calculation Reporting Input");
412 : }
413 : }
414 :
415 795 : void SetupPollutionMeterReporting(EnergyPlusData &state)
416 : {
417 :
418 : // SUBROUTINE INFORMATION:
419 : // AUTHOR Richard Liesen
420 : // DATE WRITTEN August 2002
421 : // MODIFIED na
422 : // RE-ENGINEERED December 2003 RJL
423 :
424 : // PURPOSE OF THIS SUBROUTINE:
425 : // This subroutine is the input routines and Get routines
426 :
427 : // METHODOLOGY EMPLOYED:
428 : // Uses the status flags to trigger events.
429 :
430 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
431 795 : auto &pm = state.dataPollution;
432 :
433 795 : if (pm->GetInputFlagPollution) {
434 795 : GetPollutionFactorInput(state);
435 795 : pm->GetInputFlagPollution = false;
436 : }
437 :
438 : // We are using this list rather than the enumeration to preserve the order in which meters are created to avoid ordering diffs.
439 1008 : for (PollFuel pollFuel : pm->pollFuelFactorList) {
440 :
441 213 : if (!pm->pollCoeffs[(int)pollFuel].used) continue;
442 :
443 213 : auto &pollComp = pm->pollComps[(int)pollFuel2pollFuelComponent[(int)pollFuel]];
444 :
445 213 : Constant::eFuel fuel = pollFuel2fuel[(int)pollFuel];
446 :
447 213 : constexpr std::array<OutputProcessor::EndUseCat, (int)Constant::eFuel::Num> fuel2sovEndUseCat = {
448 : OutputProcessor::EndUseCat::ElectricityEmissions,
449 : OutputProcessor::EndUseCat::NaturalGasEmissions,
450 : OutputProcessor::EndUseCat::GasolineEmissions,
451 : OutputProcessor::EndUseCat::DieselEmissions,
452 : OutputProcessor::EndUseCat::CoalEmissions,
453 : OutputProcessor::EndUseCat::PropaneEmissions,
454 : OutputProcessor::EndUseCat::FuelOilNo1Emissions,
455 : OutputProcessor::EndUseCat::FuelOilNo2Emissions,
456 : OutputProcessor::EndUseCat::OtherFuel1Emissions,
457 : OutputProcessor::EndUseCat::OtherFuel2Emissions,
458 : OutputProcessor::EndUseCat::Invalid,
459 : OutputProcessor::EndUseCat::Invalid,
460 : OutputProcessor::EndUseCat::Invalid,
461 : OutputProcessor::EndUseCat::Invalid,
462 : OutputProcessor::EndUseCat::Invalid // used for OtherEquipment object
463 : };
464 :
465 : // Need to check whether this fuel is used?
466 852 : SetupOutputVariable(state,
467 426 : format("Environmental Impact {} Source Energy", Constant::eFuelNames[(int)fuel]),
468 : Constant::Units::J,
469 213 : pollComp.sourceVal,
470 : OutputProcessor::TimeStepType::System,
471 : OutputProcessor::StoreType::Sum,
472 : "Site",
473 : Constant::eResource::Source,
474 : OutputProcessor::Group::Invalid,
475 213 : fuel2sovEndUseCat[(int)fuel]);
476 :
477 3621 : for (int iPollutant = 0; iPollutant < (int)Pollutant::Num; ++iPollutant) {
478 13632 : SetupOutputVariable(state,
479 6816 : format("Environmental Impact {} {}", Constant::eFuelNames[(int)fuel], poll2outVarStrs[iPollutant]),
480 3408 : pollUnits[iPollutant],
481 3408 : pollComp.pollutantVals[iPollutant],
482 : OutputProcessor::TimeStepType::System,
483 : OutputProcessor::StoreType::Sum,
484 : "Site",
485 3408 : poll2Resource[iPollutant],
486 : OutputProcessor::Group::Invalid,
487 3408 : fuel2sovEndUseCat[(int)fuel]);
488 : }
489 :
490 213 : if (fuel == Constant::eFuel::Electricity) {
491 : // Setup ElectricityPurchased and ElectricitySold variables
492 : // Doing this here as opposed to outside the outer loop to preserve meter order and reduce ordering diffs
493 174 : SetupOutputVariable(state,
494 : "Environmental Impact Purchased Electricity Source Energy",
495 : Constant::Units::J,
496 87 : pm->pollComps[(int)PollFuelComponent::ElectricityPurchased].sourceVal,
497 : OutputProcessor::TimeStepType::System,
498 : OutputProcessor::StoreType::Sum,
499 : "Site",
500 : Constant::eResource::Source,
501 : OutputProcessor::Group::Invalid,
502 : OutputProcessor::EndUseCat::PurchasedElectricityEmissions);
503 174 : SetupOutputVariable(state,
504 : "Environmental Impact Surplus Sold Electricity Source",
505 : Constant::Units::J,
506 87 : pm->pollComps[(int)PollFuelComponent::ElectricitySurplusSold].sourceVal,
507 : OutputProcessor::TimeStepType::System,
508 : OutputProcessor::StoreType::Sum,
509 : "Site",
510 : Constant::eResource::Source,
511 : OutputProcessor::Group::Invalid,
512 : OutputProcessor::EndUseCat::SoldElectricityEmissions);
513 : }
514 :
515 795 : } // End of the NumEnergyTypes Do Loop
516 :
517 : // And Total Carbon Equivalent variables
518 1590 : SetupOutputVariable(state,
519 : "Environmental Impact Total N2O Emissions Carbon Equivalent Mass",
520 : Constant::Units::kg,
521 795 : pm->TotCarbonEquivFromN2O,
522 : OutputProcessor::TimeStepType::System,
523 : OutputProcessor::StoreType::Sum,
524 : "Site",
525 : Constant::eResource::CarbonEquivalent,
526 : OutputProcessor::Group::Invalid,
527 : OutputProcessor::EndUseCat::CarbonEquivalentEmissions);
528 1590 : SetupOutputVariable(state,
529 : "Environmental Impact Total CH4 Emissions Carbon Equivalent Mass",
530 : Constant::Units::kg,
531 795 : pm->TotCarbonEquivFromCH4,
532 : OutputProcessor::TimeStepType::System,
533 : OutputProcessor::StoreType::Sum,
534 : "Site",
535 : Constant::eResource::CarbonEquivalent,
536 : OutputProcessor::Group::Invalid,
537 : OutputProcessor::EndUseCat::CarbonEquivalentEmissions);
538 1590 : SetupOutputVariable(state,
539 : "Environmental Impact Total CO2 Emissions Carbon Equivalent Mass",
540 : Constant::Units::kg,
541 795 : pm->TotCarbonEquivFromCO2,
542 : OutputProcessor::TimeStepType::System,
543 : OutputProcessor::StoreType::Sum,
544 : "Site",
545 : Constant::eResource::CarbonEquivalent,
546 : OutputProcessor::Group::Invalid,
547 : OutputProcessor::EndUseCat::CarbonEquivalentEmissions);
548 :
549 : // Connect pollution meters to energy meters
550 13515 : for (int iMeter = 0; iMeter < (int)PollFacilityMeter::Num; ++iMeter) {
551 12720 : pm->facilityMeterNums[iMeter] = GetMeterIndex(state, Util::makeUPPER(pollFacilityMeterNames[iMeter]));
552 : }
553 795 : }
554 :
555 795 : void CheckPollutionMeterReporting(EnergyPlusData &state)
556 : {
557 :
558 : // SUBROUTINE INFORMATION:
559 : // AUTHOR Linda Lawrie
560 : // DATE WRITTEN October 2008
561 :
562 : // in progress (what is in progress?)
563 :
564 795 : auto const &pm = state.dataPollution;
565 :
566 795 : if (pm->NumFuelFactors == 0 || pm->NumEnvImpactFactors == 0) {
567 1440 : if (ReportingThisVariable(state, "Environmental Impact Total N2O Emissions Carbon Equivalent Mass") ||
568 1440 : ReportingThisVariable(state, "Environmental Impact Total CH4 Emissions Carbon Equivalent Mass") ||
569 1440 : ReportingThisVariable(state, "Environmental Impact Total CO2 Emissions Carbon Equivalent Mass") ||
570 3597 : ReportingThisVariable(state, "Carbon Equivalent:Facility") ||
571 1437 : ReportingThisVariable(state, "CarbonEquivalentEmissions:Carbon Equivalent")) {
572 3 : ShowWarningError(
573 : state, "GetPollutionFactorInput: Requested reporting for Carbon Equivalent Pollution, but insufficient information is entered.");
574 3 : ShowContinueError(
575 : state, "Both \"FuelFactors\" and \"EnvironmentalImpactFactors\" must be entered or the displayed carbon pollution will all be zero.");
576 : }
577 : }
578 795 : }
579 :
580 : // End of Get Input subroutines for the Pollution Module
581 : //******************************************************************************
582 :
583 615229 : void CalcPollution(EnergyPlusData &state)
584 : {
585 : // SUBROUTINE INFORMATION:
586 : // AUTHOR Richard Liesen
587 : // DATE WRITTEN 1998
588 : // MODIFIED na
589 : // RE-ENGINEERED December 2003 RJL
590 :
591 : // Then the amount of Pollution produced by each fuel type is
592 : // calculated in kgs.
593 : // Input units for the coefficients is not standard and needs to be converted here.
594 : // Most of the units are g/MJ, however water is in L/MJ and low level nuclear water is m3/MJ
595 : // so only the energy has to be converted from J to MJ.
596 :
597 : // For each pollution/fuel type, Schedule values are allowed. Thus, calculations are bundled.
598 615229 : auto &pm = state.dataPollution;
599 :
600 10458893 : for (int iPoll = 0; iPoll < (int)Pollutant::Num; ++iPoll) {
601 9843664 : pm->pollutantVals[iPoll] = 0.0;
602 :
603 108280304 : for (int iPollFuel = 0; iPollFuel < (int)PollFuel::Num; ++iPollFuel) {
604 98436640 : auto &pollCoeff = pm->pollCoeffs[iPollFuel];
605 98436640 : PollFuelComponent pollFuelComp = pollFuel2pollFuelComponent[iPollFuel];
606 98436640 : auto &pollComp = pm->pollComps[(int)pollFuelComp];
607 :
608 98436640 : if (pollCoeff.used) {
609 2115248 : pollComp.pollutantVals[iPoll] = 0.0;
610 2115248 : Real64 pollutantVal = pollCoeff.pollutantCoeffs[iPoll];
611 :
612 : // Why are these two the exceptions?
613 2115248 : if (iPoll != (int)Pollutant::Water && iPoll != (int)Pollutant::NuclearLow) pollutantVal *= 0.001;
614 :
615 2115248 : if (pollCoeff.pollutantSchedNums[iPoll] != 0) {
616 0 : pollutantVal *= ScheduleManager::GetCurrentScheduleValue(state, pollCoeff.pollutantSchedNums[iPoll]);
617 : }
618 2115248 : pollComp.pollutantVals[iPoll] = pm->facilityMeterFuelComponentVals[(int)pollFuelComp] * 1.0e-6 * pollutantVal;
619 : }
620 :
621 98436640 : pm->pollutantVals[iPoll] += pollComp.pollutantVals[iPoll];
622 : } // for (iPollFactor)
623 : } // for (iPoll)
624 :
625 615229 : pm->TotCarbonEquivFromN2O = pm->pollutantVals[(int)Pollutant::N2O] * pm->CarbonEquivN2O;
626 615229 : pm->TotCarbonEquivFromCH4 = pm->pollutantVals[(int)Pollutant::CH4] * pm->CarbonEquivCH4;
627 615229 : pm->TotCarbonEquivFromCO2 = pm->pollutantVals[(int)Pollutant::CO2] * pm->CarbonEquivCO2;
628 :
629 615229 : auto const &pollCoeffElec = pm->pollCoeffs[(int)PollFuel::Electricity];
630 615229 : auto &pollCompElec = pm->pollComps[(int)PollFuelComponent::Electricity];
631 615229 : auto &pollCompElecPurchased = pm->pollComps[(int)PollFuelComponent::ElectricityPurchased];
632 615229 : auto &pollCompElecSurplusSold = pm->pollComps[(int)PollFuelComponent::ElectricitySurplusSold];
633 :
634 615229 : pollCompElec.sourceVal = pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] * pollCoeffElec.sourceCoeff;
635 615229 : pollCompElecPurchased.sourceVal = pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricityPurchased] * pollCoeffElec.sourceCoeff;
636 615229 : pollCompElecSurplusSold.sourceVal =
637 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricitySurplusSold] * pollCoeffElec.sourceCoeff;
638 :
639 615229 : if (pollCoeffElec.sourceSchedNum != 0) {
640 227 : Real64 pollCoeffElecSchedVal = ScheduleManager::GetCurrentScheduleValue(state, pollCoeffElec.sourceSchedNum);
641 227 : pollCompElec.sourceVal *= pollCoeffElecSchedVal;
642 227 : pollCompElecPurchased.sourceVal *= pollCoeffElecSchedVal;
643 227 : pollCompElecSurplusSold.sourceVal *= pollCoeffElecSchedVal;
644 : }
645 :
646 : // does not include district heating or steam
647 615229 : auto const &pollCoeffGas = pm->pollCoeffs[(int)PollFuel::NaturalGas];
648 615229 : auto &pollCompGas = pm->pollComps[(int)PollFuelComponent::NaturalGas];
649 615229 : pollCompGas.sourceVal = pm->facilityMeterVals[(int)PollFacilityMeter::NaturalGas] * pollCoeffGas.sourceCoeff;
650 615229 : if (pollCoeffGas.sourceSchedNum != 0) {
651 227 : pollCompGas.sourceVal *= ScheduleManager::GetCurrentScheduleValue(state, pollCoeffGas.sourceSchedNum);
652 : }
653 :
654 5537061 : for (PollFuel pollFuel : {PollFuel::FuelOil1,
655 : PollFuel::FuelOil2,
656 : PollFuel::Diesel,
657 : PollFuel::Gasoline,
658 : PollFuel::Propane,
659 : PollFuel::Coal,
660 : PollFuel::OtherFuel1,
661 6152290 : PollFuel::OtherFuel2}) {
662 4921832 : auto const &pollCoeff = pm->pollCoeffs[(int)pollFuel];
663 4921832 : PollFuelComponent pollFuelComponent = pollFuel2pollFuelComponent[(int)pollFuel];
664 4921832 : auto &pollComp = pm->pollComps[(int)pollFuelComponent];
665 :
666 4921832 : pollComp.sourceVal = pm->facilityMeterFuelComponentVals[(int)pollFuelComponent] * pollCoeff.sourceCoeff;
667 4921832 : if (pollCoeff.sourceSchedNum != 0) {
668 0 : pollComp.sourceVal *= ScheduleManager::GetCurrentScheduleValue(state, pollCoeff.sourceSchedNum);
669 : }
670 : } // for (pollFuelComponent)
671 615229 : } // CalcPollution()
672 :
673 615229 : void ReadEnergyMeters(EnergyPlusData &state)
674 : {
675 : // SUBROUTINE INFORMATION:
676 : // AUTHOR Richard Liesen
677 : // DATE WRITTEN 1998
678 : // MODIFIED na
679 : // RE-ENGINEERED December 2003 RJL
680 :
681 : // PURPOSE OF THIS SUBROUTINE:
682 : // Read Energy Results from the meters
683 : // This routine reads the meters for the energy used
684 :
685 : // Using/Aliasing
686 615229 : Real64 FracTimeStepZone = state.dataHVACGlobal->FracTimeStepZone;
687 615229 : auto &pm = state.dataPollution;
688 :
689 10458893 : for (int iMeter = 0; iMeter < (int)PollFacilityMeter::Num; ++iMeter) {
690 9843664 : pm->facilityMeterVals[iMeter] =
691 9843664 : GetInstantMeterValue(state, pm->facilityMeterNums[iMeter], OutputProcessor::TimeStepType::Zone) * FracTimeStepZone +
692 9843664 : GetInstantMeterValue(state, pm->facilityMeterNums[iMeter], OutputProcessor::TimeStepType::System);
693 : }
694 :
695 : // Now these fuel types have to be sorted and summed into categories that we have pollution factors for.
696 : // The Off-Site Electricity is the total needed by the facility minus the amount generated on-site.
697 : // The on-site pollutants will end up being other fuel types used by the generators.
698 : // If the difference between the 2 electric quantities is <0.0 then it will be zero for that time step.
699 : // We will also add the District Cooling here with a rough conversion from Energy using the User
700 : // defined COP.
701 :
702 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] =
703 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::Electricity] - pm->facilityMeterVals[(int)PollFacilityMeter::ElectricityProduced] +
704 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::CoolPurchased] / pm->PurchCoolCOP;
705 :
706 615229 : if (pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] < 0.0)
707 2043 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] = 0.0;
708 :
709 : // The Natural Gas fuel type will be summed from the meters with the District Heating using an efficiency.
710 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::NaturalGas] =
711 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::NaturalGas] +
712 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::HeatPurchased] / pm->PurchHeatEffic +
713 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::Steam] / pm->SteamConvEffic;
714 :
715 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::FuelOil1] = pm->facilityMeterVals[(int)PollFacilityMeter::FuelOil1];
716 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::FuelOil2] = pm->facilityMeterVals[(int)PollFacilityMeter::FuelOil2];
717 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Gasoline] = pm->facilityMeterVals[(int)PollFacilityMeter::Gasoline];
718 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Propane] = pm->facilityMeterVals[(int)PollFacilityMeter::Propane];
719 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Coal] = pm->facilityMeterVals[(int)PollFacilityMeter::Coal];
720 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Diesel] = pm->facilityMeterVals[(int)PollFacilityMeter::Diesel];
721 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::OtherFuel1] = pm->facilityMeterVals[(int)PollFacilityMeter::OtherFuel1];
722 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::OtherFuel2] = pm->facilityMeterVals[(int)PollFacilityMeter::OtherFuel2];
723 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricityPurchased] =
724 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::ElectricityPurchased];
725 615229 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricitySurplusSold] =
726 615229 : pm->facilityMeterVals[(int)PollFacilityMeter::ElectricitySurplusSold];
727 615229 : }
728 :
729 : // *****************************************************************************
730 : // Utility Routines to allow access to data inside this module.
731 : // *****************************************************************************
732 :
733 10322 : void GetFuelFactorInfo(EnergyPlusData &state,
734 : Constant::eFuel fuel, // input fuel name (standard from Tabular reports)
735 : bool &fuelFactorUsed, // return value true if user has entered this fuel
736 : Real64 &fuelSourceFactor, // if used, the source factor
737 : bool &fuelFactorScheduleUsed, // if true, schedules for this fuel are used
738 : int &ffScheduleIndex // if schedules for this fuel are used, return schedule index
739 : )
740 : {
741 :
742 : // SUBROUTINE INFORMATION:
743 : // AUTHOR Linda Lawrie
744 : // DATE WRITTEN July 2008
745 :
746 : // PURPOSE OF THIS SUBROUTINE:
747 : // This routine allows access to data inside this module from other modules (specifically the
748 : // output tabular reports.
749 10322 : auto &pm = state.dataPollution;
750 :
751 10322 : if (pm->GetInputFlagPollution) {
752 0 : GetPollutionFactorInput(state);
753 0 : pm->GetInputFlagPollution = false;
754 : }
755 :
756 10322 : fuelFactorUsed = false;
757 10322 : fuelSourceFactor = 0.0;
758 10322 : fuelFactorScheduleUsed = false;
759 10322 : ffScheduleIndex = 0;
760 :
761 10322 : PollFuel pollFuel = fuel2pollFuel[(int)fuel];
762 10322 : auto const &pollCoeff = pm->pollCoeffs[(int)pollFuel];
763 :
764 10322 : if (pollCoeff.used) {
765 451 : fuelFactorUsed = true;
766 451 : fuelSourceFactor = pollCoeff.sourceCoeff;
767 451 : if (pollCoeff.sourceSchedNum == 0) {
768 446 : fuelFactorScheduleUsed = false;
769 : } else {
770 5 : fuelFactorScheduleUsed = true;
771 5 : ffScheduleIndex = pollCoeff.sourceSchedNum;
772 : }
773 : } else {
774 9871 : fuelSourceFactor = pollFuelFactors[(int)pollFuel];
775 : }
776 :
777 10322 : if (fuel == Constant::eFuel::DistrictHeatingWater) {
778 794 : fuelSourceFactor /= pm->PurchHeatEffic;
779 9528 : } else if (fuel == Constant::eFuel::DistrictCooling) {
780 794 : fuelSourceFactor /= pm->PurchCoolCOP;
781 8734 : } else if (fuel == Constant::eFuel::DistrictHeatingSteam) {
782 794 : fuelSourceFactor = 0.3 / pm->SteamConvEffic;
783 : }
784 10322 : }
785 :
786 794 : void GetEnvironmentalImpactFactorInfo(EnergyPlusData &state,
787 : Real64 &efficiencyDistrictHeatingWater, // if entered, the efficiency of District Heating Water
788 : Real64 &efficiencyDistrictCooling, // if entered, the efficiency of District Cooling
789 : Real64 &sourceFactorDistrictHeatingSteam // if entered, the source factor for Dictrict Heating Steam
790 : )
791 : {
792 :
793 : // SUBROUTINE INFORMATION:
794 : // AUTHOR Linda Lawrie
795 : // DATE WRITTEN August 2008
796 :
797 : // PURPOSE OF THIS SUBROUTINE:
798 : // This routine allows access to data inside this module from other modules (specifically the
799 : // output tabular reports.
800 :
801 794 : auto const &pm = state.dataPollution;
802 794 : if (pm->GetInputFlagPollution) {
803 0 : GetPollutionFactorInput(state);
804 0 : pm->GetInputFlagPollution = false;
805 : }
806 :
807 794 : if (pm->NumEnvImpactFactors > 0) {
808 75 : efficiencyDistrictHeatingWater = pm->PurchHeatEffic;
809 75 : sourceFactorDistrictHeatingSteam = pm->SteamConvEffic;
810 75 : efficiencyDistrictCooling = pm->PurchCoolCOP;
811 : }
812 794 : }
813 :
814 : } // namespace EnergyPlus::Pollution
|