Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // 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 19866 : 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 19866 : if (!state.dataPollution->PollutionReportSetup) {
114 0 : return;
115 : }
116 :
117 : // Call the Routine to Read the Energy Values from the EnergyPlus Meters
118 19866 : ReadEnergyMeters(state);
119 :
120 : // Call the routine that takes the fuel data and calculates the
121 : // Pollution for each fuel type.
122 19866 : CalcPollution(state);
123 : }
124 :
125 : // Get Input Section of the Module
126 : //******************************************************************************
127 :
128 73 : void SetupPollutionCalculations(EnergyPlusData &state)
129 : {
130 :
131 : // SUBROUTINE INFORMATION:
132 : // AUTHOR Richard Liesen
133 : // DATE WRITTEN August 2002
134 : // MODIFIED na
135 : // RE-ENGINEERED December 2003 RJL; August 2008 LKL - more standard getinput
136 :
137 : // PURPOSE OF THIS SUBROUTINE:
138 : // This subroutine is the input routines and Get routines
139 :
140 : // METHODOLOGY EMPLOYED:
141 : // Uses the status flags to trigger events.
142 :
143 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
144 : int NumPolluteRpt;
145 : int NumAlphas;
146 : int NumNums;
147 : int Loop;
148 : int IOStat;
149 73 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
150 :
151 : // First determine if the Pollution reporting has been triggered, and is not exit.
152 73 : cCurrentModuleObject = "Output:EnvironmentalImpactFactors";
153 73 : NumPolluteRpt = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
154 73 : state.dataPollution->PollutionReportSetup = true;
155 :
156 73 : for (Loop = 1; Loop <= NumPolluteRpt; ++Loop) {
157 :
158 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
159 : cCurrentModuleObject,
160 : Loop,
161 0 : state.dataIPShortCut->cAlphaArgs,
162 : NumAlphas,
163 0 : state.dataIPShortCut->rNumericArgs,
164 : NumNums,
165 : IOStat,
166 0 : state.dataIPShortCut->lNumericFieldBlanks,
167 0 : state.dataIPShortCut->lAlphaFieldBlanks,
168 0 : state.dataIPShortCut->cAlphaFieldNames,
169 0 : state.dataIPShortCut->cNumericFieldNames);
170 :
171 : // Call this routine in the Output Processor to setup the correct Facility energy meters that are
172 : // necessary to make sure that the Meter file is opened and written to by the OP so that time stamps
173 : // and the like are happening as expected.
174 0 : OutputProcessor::ReportFreq freq = OutputProcessor::ReportFreq::Simulation;
175 :
176 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(1) &&
177 : (freq = static_cast<OutputProcessor::ReportFreq>(
178 0 : getEnumValue(OutputProcessor::reportFreqNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(1))))) ==
179 : OutputProcessor::ReportFreq::Invalid) {
180 0 : ShowSevereError(state, format("Invalid reporting frequency {}", state.dataIPShortCut->cAlphaArgs(1)));
181 0 : continue;
182 : }
183 :
184 0 : InitPollutionMeterReporting(state, freq);
185 : }
186 73 : }
187 :
188 78 : void GetPollutionFactorInput(EnergyPlusData &state)
189 : {
190 :
191 : // SUBROUTINE INFORMATION:
192 : // AUTHOR Linda Lawrie
193 : // DATE WRITTEN August 2008
194 :
195 : // PURPOSE OF THIS SUBROUTINE:
196 : // SetupPollutionCalculation must be called after meters are initialized. This caused a problem
197 : // in runs so have added this routine to allow central get for most inputs.
198 :
199 78 : constexpr std::string_view routineName = "GetPollutionFactorInput";
200 :
201 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
202 : int NumAlphas;
203 : int NumNums;
204 : int IOStat;
205 78 : bool ErrorsFound = false;
206 :
207 78 : auto &ip = state.dataInputProcessing->inputProcessor;
208 78 : auto &ipsc = state.dataIPShortCut;
209 78 : auto &pm = state.dataPollution;
210 :
211 78 : if (!pm->GetInputFlagPollution) {
212 0 : return; // Input already gotten
213 : }
214 78 : pm->GetInputFlagPollution = false;
215 :
216 78 : ipsc->cCurrentModuleObject = "EnvironmentalImpactFactors";
217 78 : pm->NumEnvImpactFactors = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
218 :
219 78 : if (pm->NumEnvImpactFactors > 0) {
220 : // Now find and load all of the user inputs and factors.
221 4 : ip->getObjectItem(state,
222 2 : ipsc->cCurrentModuleObject,
223 : 1,
224 2 : ipsc->cAlphaArgs,
225 : NumAlphas,
226 2 : ipsc->rNumericArgs,
227 : NumNums,
228 : IOStat,
229 2 : ipsc->lNumericFieldBlanks,
230 2 : ipsc->lAlphaFieldBlanks,
231 2 : ipsc->cAlphaFieldNames,
232 2 : ipsc->cNumericFieldNames);
233 76 : } else if (pm->PollutionReportSetup) {
234 0 : ShowWarningError(state, format("{}: not entered. Values will be defaulted.", ipsc->cCurrentModuleObject));
235 : }
236 :
237 78 : pm->PurchHeatEffic = 0.3;
238 78 : pm->PurchCoolCOP = 3.0;
239 78 : pm->SteamConvEffic = 0.25;
240 78 : pm->CarbonEquivN2O = 0.0;
241 78 : pm->CarbonEquivCH4 = 0.0;
242 78 : pm->CarbonEquivCO2 = 0.0;
243 :
244 78 : if (pm->NumEnvImpactFactors > 0) {
245 : // If Heating Efficiency defined by the User is negative or zero then a default of 30% will be assigned.
246 2 : if (ipsc->rNumericArgs(1) > 0.0) {
247 2 : pm->PurchHeatEffic = ipsc->rNumericArgs(1);
248 : }
249 :
250 : // If COP defined by the User is negative or zero then a default of 3.0 will be assigned.
251 2 : if (ipsc->rNumericArgs(2) > 0.0) {
252 2 : pm->PurchCoolCOP = ipsc->rNumericArgs(2);
253 : }
254 :
255 : // If Steam Conversion Efficiency defined by the User is negative or zero then a default of 25% will be assigned.
256 2 : if (ipsc->rNumericArgs(3) > 0.0) {
257 2 : pm->SteamConvEffic = ipsc->rNumericArgs(3);
258 : }
259 :
260 : // Load the Total Carbon Equivalent Pollution Factor coefficients
261 2 : pm->CarbonEquivN2O = ipsc->rNumericArgs(4);
262 2 : pm->CarbonEquivCH4 = ipsc->rNumericArgs(5);
263 2 : pm->CarbonEquivCO2 = ipsc->rNumericArgs(6);
264 : }
265 :
266 : // Compare all of the Fuel Factors and compare to PollutionCalculationFactors List
267 78 : ipsc->cCurrentModuleObject = "FuelFactors";
268 78 : pm->NumFuelFactors = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
269 :
270 88 : for (int Loop = 1; Loop <= state.dataPollution->NumFuelFactors; ++Loop) {
271 : // Now find and load all of the user inputs and factors.
272 20 : ip->getObjectItem(state,
273 10 : ipsc->cCurrentModuleObject,
274 : Loop,
275 10 : ipsc->cAlphaArgs,
276 : NumAlphas,
277 10 : ipsc->rNumericArgs,
278 : NumNums,
279 : IOStat,
280 10 : ipsc->lNumericFieldBlanks,
281 10 : ipsc->lAlphaFieldBlanks,
282 10 : ipsc->cAlphaFieldNames,
283 10 : ipsc->cNumericFieldNames);
284 :
285 10 : ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
286 :
287 10 : PollFuel pollFuel = static_cast<PollFuel>(getEnumValue(pollFuelNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(1))));
288 10 : if (pollFuel == PollFuel::Invalid) {
289 0 : ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1));
290 0 : ErrorsFound = true;
291 0 : continue;
292 : }
293 :
294 10 : pm->pollFuelFactorList.push_back(pollFuel);
295 :
296 10 : auto &pollCoeff = pm->pollCoeffs[(int)pollFuel];
297 10 : Constant::eFuel fuel = pollFuel2fuel[(int)pollFuel];
298 :
299 10 : if (pollCoeff.used) {
300 0 : ShowWarningError(
301 0 : state, format("{}: {} already entered. Previous entry will be used.", ipsc->cCurrentModuleObject, Constant::eFuelNames[(int)fuel]));
302 0 : continue;
303 : }
304 :
305 10 : pollCoeff.used = true;
306 :
307 10 : pollCoeff.sourceCoeff = ipsc->rNumericArgs(1);
308 10 : if (ipsc->lAlphaFieldBlanks(2)) {
309 0 : } else if ((pollCoeff.sourceSched = Sched::GetSchedule(state, ipsc->cAlphaArgs(2))) == nullptr) {
310 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
311 0 : ErrorsFound = true;
312 0 : } else if (!pollCoeff.sourceSched->checkMinVal(state, Clusive::In, 0.0)) {
313 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2), Clusive::In, 0.0);
314 0 : ErrorsFound = true;
315 : }
316 :
317 170 : for (int iPollutant = 0; iPollutant < (int)Pollutant::Num; ++iPollutant) {
318 160 : pollCoeff.pollutantCoeffs[iPollutant] = ipsc->rNumericArgs(iPollutant + 2);
319 160 : if (ipsc->lAlphaFieldBlanks(iPollutant + 3)) {
320 0 : } else if ((pollCoeff.pollutantScheds[iPollutant] = Sched::GetSchedule(state, ipsc->cAlphaArgs(iPollutant + 3))) == nullptr) {
321 0 : ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(iPollutant + 3), ipsc->cAlphaArgs(iPollutant + 3));
322 0 : ErrorsFound = true;
323 0 : } else if (!pollCoeff.pollutantScheds[iPollutant]->checkMinVal(state, Clusive::In, 0.0)) {
324 0 : Sched::ShowSevereBadMin(state, eoh, ipsc->cAlphaFieldNames(iPollutant + 3), ipsc->cAlphaArgs(iPollutant + 3), Clusive::In, 0.0);
325 0 : ErrorsFound = true;
326 : }
327 : } // for (iPollutant)
328 :
329 : } // End of the NumEnergyTypes Do Loop
330 :
331 78 : if (pm->PollutionReportSetup) { // only do this if reporting on the pollution
332 : // Need to go through all of the Fuel Types and make sure a Fuel Factor was found for each type of energy being simulated
333 : // Check for Electricity
334 0 : if (!pm->pollCoeffs[(int)PollFuel::Electricity].used && ((pm->facilityMeterNums[(int)PollFacilityMeter::Electricity] > 0) ||
335 0 : (pm->facilityMeterNums[(int)PollFacilityMeter::ElectricityProduced] > 0) ||
336 0 : (pm->facilityMeterNums[(int)PollFacilityMeter::CoolPurchased] > 0))) {
337 0 : ShowSevereError(state,
338 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for ELECTRICITY", ipsc->cCurrentModuleObject));
339 0 : ErrorsFound = true;
340 : }
341 :
342 : // Check for Natural Gas
343 0 : if (!pm->pollCoeffs[(int)PollFuel::NaturalGas].used &&
344 0 : ((pm->facilityMeterNums[(int)PollFacilityMeter::NaturalGas] > 0) || (pm->facilityMeterNums[(int)PollFacilityMeter::HeatPurchased] > 0) ||
345 0 : (pm->facilityMeterNums[(int)PollFacilityMeter::Steam] > 0))) {
346 0 : ShowSevereError(state,
347 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for NATURAL GAS", ipsc->cCurrentModuleObject));
348 0 : ErrorsFound = true;
349 : }
350 : // Check for FuelOilNo2 (Residual Oil)
351 0 : if (!pm->pollCoeffs[(int)PollFuel::FuelOil2].used && (pm->facilityMeterNums[(int)PollFacilityMeter::FuelOil2] > 0)) {
352 0 : ShowSevereError(state,
353 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for FUEL OIL #2", ipsc->cCurrentModuleObject));
354 0 : ErrorsFound = true;
355 : }
356 : // Check for FuelOilNo1 (Distillate Oil)
357 0 : if (!pm->pollCoeffs[(int)PollFuel::FuelOil1].used && (pm->facilityMeterNums[(int)PollFacilityMeter::FuelOil1] > 0)) {
358 0 : ShowSevereError(state,
359 0 : format("{} Not Found or Fuel not specified For Pollution Calculation for FUEL OIL #1", ipsc->cCurrentModuleObject));
360 0 : ErrorsFound = true;
361 : }
362 : // Check for Coal
363 0 : if (!pm->pollCoeffs[(int)PollFuel::Coal].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Coal] > 0)) {
364 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for COAL", ipsc->cCurrentModuleObject));
365 0 : ErrorsFound = true;
366 : }
367 : // Check for Gasoline
368 0 : if (!pm->pollCoeffs[(int)PollFuel::Gasoline].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Gasoline] > 0)) {
369 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for GASOLINE", ipsc->cCurrentModuleObject));
370 0 : ErrorsFound = true;
371 : }
372 : // Check for Propane
373 0 : if (!pm->pollCoeffs[(int)PollFuel::Propane].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Propane] > 0)) {
374 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for PROPANE", ipsc->cCurrentModuleObject));
375 0 : ErrorsFound = true;
376 : }
377 : // Check for Diesel
378 0 : if (!pm->pollCoeffs[(int)PollFuel::Diesel].used && (pm->facilityMeterNums[(int)PollFacilityMeter::Diesel] > 0)) {
379 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for DIESEL", ipsc->cCurrentModuleObject));
380 0 : ErrorsFound = true;
381 : }
382 : // Check for OtherFuel1
383 0 : if (!pm->pollCoeffs[(int)PollFuel::OtherFuel1].used && (pm->facilityMeterNums[(int)PollFacilityMeter::OtherFuel1] > 0)) {
384 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for OTHERFUEL1", ipsc->cCurrentModuleObject));
385 0 : ErrorsFound = true;
386 : }
387 : // Check for OtherFuel2
388 0 : if (!pm->pollCoeffs[(int)PollFuel::OtherFuel2].used && (pm->facilityMeterNums[(int)PollFacilityMeter::OtherFuel2] > 0)) {
389 0 : ShowSevereError(state, format("{} Not Found or Fuel not specified For Pollution Calculation for OTHERFUEL2", ipsc->cCurrentModuleObject));
390 0 : ErrorsFound = true;
391 : }
392 : }
393 :
394 78 : if (ErrorsFound) {
395 0 : ShowFatalError(state, "Errors found in getting Pollution Calculation Reporting Input");
396 : }
397 : }
398 :
399 74 : void SetupPollutionMeterReporting(EnergyPlusData &state)
400 : {
401 :
402 : // SUBROUTINE INFORMATION:
403 : // AUTHOR Richard Liesen
404 : // DATE WRITTEN August 2002
405 : // MODIFIED na
406 : // RE-ENGINEERED December 2003 RJL
407 :
408 : // PURPOSE OF THIS SUBROUTINE:
409 : // This subroutine is the input routines and Get routines
410 :
411 : // METHODOLOGY EMPLOYED:
412 : // Uses the status flags to trigger events.
413 :
414 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
415 74 : auto &pm = state.dataPollution;
416 :
417 74 : if (pm->GetInputFlagPollution) {
418 66 : GetPollutionFactorInput(state);
419 66 : pm->GetInputFlagPollution = false;
420 : }
421 :
422 : // We are using this list rather than the enumeration to preserve the order in which meters are created to avoid ordering diffs.
423 83 : for (PollFuel pollFuel : pm->pollFuelFactorList) {
424 :
425 9 : if (!pm->pollCoeffs[(int)pollFuel].used) {
426 0 : continue;
427 : }
428 :
429 9 : auto &pollComp = pm->pollComps[(int)pollFuel2pollFuelComponent[(int)pollFuel]];
430 :
431 9 : Constant::eFuel fuel = pollFuel2fuel[(int)pollFuel];
432 :
433 9 : constexpr std::array<OutputProcessor::EndUseCat, (int)Constant::eFuel::Num> fuel2sovEndUseCat = {
434 : OutputProcessor::EndUseCat::ElectricityEmissions,
435 : OutputProcessor::EndUseCat::NaturalGasEmissions,
436 : OutputProcessor::EndUseCat::GasolineEmissions,
437 : OutputProcessor::EndUseCat::DieselEmissions,
438 : OutputProcessor::EndUseCat::CoalEmissions,
439 : OutputProcessor::EndUseCat::PropaneEmissions,
440 : OutputProcessor::EndUseCat::FuelOilNo1Emissions,
441 : OutputProcessor::EndUseCat::FuelOilNo2Emissions,
442 : OutputProcessor::EndUseCat::OtherFuel1Emissions,
443 : OutputProcessor::EndUseCat::OtherFuel2Emissions,
444 : OutputProcessor::EndUseCat::Invalid,
445 : OutputProcessor::EndUseCat::Invalid,
446 : OutputProcessor::EndUseCat::Invalid,
447 : OutputProcessor::EndUseCat::Invalid,
448 : OutputProcessor::EndUseCat::Invalid // used for OtherEquipment object
449 : };
450 :
451 : // Need to check whether this fuel is used?
452 45 : SetupOutputVariable(state,
453 18 : format("Environmental Impact {} Source Energy", Constant::eFuelNames[(int)fuel]),
454 : Constant::Units::J,
455 9 : pollComp.sourceVal,
456 : OutputProcessor::TimeStepType::System,
457 : OutputProcessor::StoreType::Sum,
458 : "Site",
459 : Constant::eResource::Source,
460 : OutputProcessor::Group::Invalid,
461 9 : fuel2sovEndUseCat[(int)fuel]);
462 :
463 153 : for (int iPollutant = 0; iPollutant < (int)Pollutant::Num; ++iPollutant) {
464 720 : SetupOutputVariable(state,
465 288 : format("Environmental Impact {} {}", Constant::eFuelNames[(int)fuel], poll2outVarStrs[iPollutant]),
466 144 : pollUnits[iPollutant],
467 144 : pollComp.pollutantVals[iPollutant],
468 : OutputProcessor::TimeStepType::System,
469 : OutputProcessor::StoreType::Sum,
470 : "Site",
471 144 : poll2Resource[iPollutant],
472 : OutputProcessor::Group::Invalid,
473 144 : fuel2sovEndUseCat[(int)fuel]);
474 : }
475 :
476 9 : if (fuel == Constant::eFuel::Electricity) {
477 : // Setup ElectricityPurchased and ElectricitySold variables
478 : // Doing this here as opposed to outside the outer loop to preserve meter order and reduce ordering diffs
479 4 : SetupOutputVariable(state,
480 : "Environmental Impact Purchased Electricity Source Energy",
481 : Constant::Units::J,
482 1 : pm->pollComps[(int)PollFuelComponent::ElectricityPurchased].sourceVal,
483 : OutputProcessor::TimeStepType::System,
484 : OutputProcessor::StoreType::Sum,
485 : "Site",
486 : Constant::eResource::Source,
487 : OutputProcessor::Group::Invalid,
488 : OutputProcessor::EndUseCat::PurchasedElectricityEmissions);
489 4 : SetupOutputVariable(state,
490 : "Environmental Impact Surplus Sold Electricity Source",
491 : Constant::Units::J,
492 1 : pm->pollComps[(int)PollFuelComponent::ElectricitySurplusSold].sourceVal,
493 : OutputProcessor::TimeStepType::System,
494 : OutputProcessor::StoreType::Sum,
495 : "Site",
496 : Constant::eResource::Source,
497 : OutputProcessor::Group::Invalid,
498 : OutputProcessor::EndUseCat::SoldElectricityEmissions);
499 : }
500 :
501 74 : } // End of the NumEnergyTypes Do Loop
502 :
503 : // And Total Carbon Equivalent variables
504 296 : SetupOutputVariable(state,
505 : "Environmental Impact Total N2O Emissions Carbon Equivalent Mass",
506 : Constant::Units::kg,
507 74 : pm->TotCarbonEquivFromN2O,
508 : OutputProcessor::TimeStepType::System,
509 : OutputProcessor::StoreType::Sum,
510 : "Site",
511 : Constant::eResource::CarbonEquivalent,
512 : OutputProcessor::Group::Invalid,
513 : OutputProcessor::EndUseCat::CarbonEquivalentEmissions);
514 296 : SetupOutputVariable(state,
515 : "Environmental Impact Total CH4 Emissions Carbon Equivalent Mass",
516 : Constant::Units::kg,
517 74 : pm->TotCarbonEquivFromCH4,
518 : OutputProcessor::TimeStepType::System,
519 : OutputProcessor::StoreType::Sum,
520 : "Site",
521 : Constant::eResource::CarbonEquivalent,
522 : OutputProcessor::Group::Invalid,
523 : OutputProcessor::EndUseCat::CarbonEquivalentEmissions);
524 296 : SetupOutputVariable(state,
525 : "Environmental Impact Total CO2 Emissions Carbon Equivalent Mass",
526 : Constant::Units::kg,
527 74 : pm->TotCarbonEquivFromCO2,
528 : OutputProcessor::TimeStepType::System,
529 : OutputProcessor::StoreType::Sum,
530 : "Site",
531 : Constant::eResource::CarbonEquivalent,
532 : OutputProcessor::Group::Invalid,
533 : OutputProcessor::EndUseCat::CarbonEquivalentEmissions);
534 :
535 : // Connect pollution meters to energy meters
536 1258 : for (int iMeter = 0; iMeter < (int)PollFacilityMeter::Num; ++iMeter) {
537 1184 : pm->facilityMeterNums[iMeter] = GetMeterIndex(state, Util::makeUPPER(pollFacilityMeterNames[iMeter]));
538 : }
539 74 : }
540 :
541 73 : void CheckPollutionMeterReporting(EnergyPlusData &state)
542 : {
543 :
544 : // SUBROUTINE INFORMATION:
545 : // AUTHOR Linda Lawrie
546 : // DATE WRITTEN October 2008
547 :
548 : // in progress (what is in progress?)
549 :
550 73 : auto const &pm = state.dataPollution;
551 :
552 73 : if (pm->NumFuelFactors == 0 || pm->NumEnvImpactFactors == 0) {
553 219 : if (ReportingThisVariable(state, "Environmental Impact Total N2O Emissions Carbon Equivalent Mass") ||
554 219 : ReportingThisVariable(state, "Environmental Impact Total CH4 Emissions Carbon Equivalent Mass") ||
555 219 : ReportingThisVariable(state, "Environmental Impact Total CO2 Emissions Carbon Equivalent Mass") ||
556 438 : ReportingThisVariable(state, "Carbon Equivalent:Facility") ||
557 219 : ReportingThisVariable(state, "CarbonEquivalentEmissions:Carbon Equivalent")) {
558 0 : ShowWarningError(
559 : state, "GetPollutionFactorInput: Requested reporting for Carbon Equivalent Pollution, but insufficient information is entered.");
560 0 : ShowContinueError(
561 : state, "Both \"FuelFactors\" and \"EnvironmentalImpactFactors\" must be entered or the displayed carbon pollution will all be zero.");
562 : }
563 : }
564 73 : }
565 :
566 : // End of Get Input subroutines for the Pollution Module
567 : //******************************************************************************
568 :
569 19866 : void CalcPollution(EnergyPlusData &state)
570 : {
571 : // SUBROUTINE INFORMATION:
572 : // AUTHOR Richard Liesen
573 : // DATE WRITTEN 1998
574 : // MODIFIED na
575 : // RE-ENGINEERED December 2003 RJL
576 :
577 : // Then the amount of Pollution produced by each fuel type is
578 : // calculated in kgs.
579 : // Input units for the coefficients is not standard and needs to be converted here.
580 : // Most of the units are g/MJ, however water is in L/MJ and low level nuclear water is m3/MJ
581 : // so only the energy has to be converted from J to MJ.
582 :
583 : // For each pollution/fuel type, Schedule values are allowed. Thus, calculations are bundled.
584 19866 : auto &pm = state.dataPollution;
585 :
586 337722 : for (int iPoll = 0; iPoll < (int)Pollutant::Num; ++iPoll) {
587 317856 : pm->pollutantVals[iPoll] = 0.0;
588 :
589 3496416 : for (int iPollFuel = 0; iPollFuel < (int)PollFuel::Num; ++iPollFuel) {
590 3178560 : auto &pollCoeff = pm->pollCoeffs[iPollFuel];
591 3178560 : PollFuelComponent pollFuelComp = pollFuel2pollFuelComponent[iPollFuel];
592 3178560 : auto &pollComp = pm->pollComps[(int)pollFuelComp];
593 :
594 3178560 : if (pollCoeff.used) {
595 0 : pollComp.pollutantVals[iPoll] = 0.0;
596 0 : Real64 pollutantVal = pollCoeff.pollutantCoeffs[iPoll];
597 :
598 : // Why are these two the exceptions?
599 0 : if (iPoll != (int)Pollutant::Water && iPoll != (int)Pollutant::NuclearLow) {
600 0 : pollutantVal *= 0.001;
601 : }
602 :
603 0 : if (pollCoeff.pollutantScheds[iPoll] != nullptr) {
604 0 : pollutantVal *= pollCoeff.pollutantScheds[iPoll]->getCurrentVal();
605 : }
606 0 : pollComp.pollutantVals[iPoll] = pm->facilityMeterFuelComponentVals[(int)pollFuelComp] * 1.0e-6 * pollutantVal;
607 : }
608 :
609 3178560 : pm->pollutantVals[iPoll] += pollComp.pollutantVals[iPoll];
610 : } // for (iPollFactor)
611 : } // for (iPoll)
612 :
613 19866 : pm->TotCarbonEquivFromN2O = pm->pollutantVals[(int)Pollutant::N2O] * pm->CarbonEquivN2O;
614 19866 : pm->TotCarbonEquivFromCH4 = pm->pollutantVals[(int)Pollutant::CH4] * pm->CarbonEquivCH4;
615 19866 : pm->TotCarbonEquivFromCO2 = pm->pollutantVals[(int)Pollutant::CO2] * pm->CarbonEquivCO2;
616 :
617 19866 : auto const &pollCoeffElec = pm->pollCoeffs[(int)PollFuel::Electricity];
618 19866 : auto &pollCompElec = pm->pollComps[(int)PollFuelComponent::Electricity];
619 19866 : auto &pollCompElecPurchased = pm->pollComps[(int)PollFuelComponent::ElectricityPurchased];
620 19866 : auto &pollCompElecSurplusSold = pm->pollComps[(int)PollFuelComponent::ElectricitySurplusSold];
621 :
622 19866 : pollCompElec.sourceVal = pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] * pollCoeffElec.sourceCoeff;
623 19866 : pollCompElecPurchased.sourceVal = pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricityPurchased] * pollCoeffElec.sourceCoeff;
624 19866 : pollCompElecSurplusSold.sourceVal =
625 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricitySurplusSold] * pollCoeffElec.sourceCoeff;
626 :
627 19866 : if (pollCoeffElec.sourceSched != nullptr) {
628 0 : Real64 pollCoeffElecSchedVal = pollCoeffElec.sourceSched->getCurrentVal();
629 0 : pollCompElec.sourceVal *= pollCoeffElecSchedVal;
630 0 : pollCompElecPurchased.sourceVal *= pollCoeffElecSchedVal;
631 0 : pollCompElecSurplusSold.sourceVal *= pollCoeffElecSchedVal;
632 : }
633 :
634 : // does not include district heating or steam
635 19866 : auto const &pollCoeffGas = pm->pollCoeffs[(int)PollFuel::NaturalGas];
636 19866 : auto &pollCompGas = pm->pollComps[(int)PollFuelComponent::NaturalGas];
637 19866 : pollCompGas.sourceVal = pm->facilityMeterVals[(int)PollFacilityMeter::NaturalGas] * pollCoeffGas.sourceCoeff;
638 19866 : if (pollCoeffGas.sourceSched != nullptr) {
639 0 : pollCompGas.sourceVal *= pollCoeffGas.sourceSched->getCurrentVal();
640 : }
641 :
642 178794 : for (PollFuel pollFuel : {PollFuel::FuelOil1,
643 : PollFuel::FuelOil2,
644 : PollFuel::Diesel,
645 : PollFuel::Gasoline,
646 : PollFuel::Propane,
647 : PollFuel::Coal,
648 : PollFuel::OtherFuel1,
649 198660 : PollFuel::OtherFuel2}) {
650 158928 : auto const &pollCoeff = pm->pollCoeffs[(int)pollFuel];
651 158928 : PollFuelComponent pollFuelComponent = pollFuel2pollFuelComponent[(int)pollFuel];
652 158928 : auto &pollComp = pm->pollComps[(int)pollFuelComponent];
653 :
654 158928 : pollComp.sourceVal = pm->facilityMeterFuelComponentVals[(int)pollFuelComponent] * pollCoeff.sourceCoeff;
655 158928 : if (pollCoeff.sourceSched != nullptr) {
656 0 : pollComp.sourceVal *= pollCoeff.sourceSched->getCurrentVal();
657 : }
658 : } // for (pollFuelComponent)
659 19866 : } // CalcPollution()
660 :
661 19866 : void ReadEnergyMeters(EnergyPlusData &state)
662 : {
663 : // SUBROUTINE INFORMATION:
664 : // AUTHOR Richard Liesen
665 : // DATE WRITTEN 1998
666 : // MODIFIED na
667 : // RE-ENGINEERED December 2003 RJL
668 :
669 : // PURPOSE OF THIS SUBROUTINE:
670 : // Read Energy Results from the meters
671 : // This routine reads the meters for the energy used
672 :
673 : // Using/Aliasing
674 19866 : Real64 FracTimeStepZone = state.dataHVACGlobal->FracTimeStepZone;
675 19866 : auto &pm = state.dataPollution;
676 :
677 337722 : for (int iMeter = 0; iMeter < (int)PollFacilityMeter::Num; ++iMeter) {
678 317856 : pm->facilityMeterVals[iMeter] =
679 317856 : GetInstantMeterValue(state, pm->facilityMeterNums[iMeter], OutputProcessor::TimeStepType::Zone) * FracTimeStepZone +
680 317856 : GetInstantMeterValue(state, pm->facilityMeterNums[iMeter], OutputProcessor::TimeStepType::System);
681 : }
682 :
683 : // Now these fuel types have to be sorted and summed into categories that we have pollution factors for.
684 : // The Off-Site Electricity is the total needed by the facility minus the amount generated on-site.
685 : // The on-site pollutants will end up being other fuel types used by the generators.
686 : // If the difference between the 2 electric quantities is <0.0 then it will be zero for that time step.
687 : // We will also add the District Cooling here with a rough conversion from Energy using the User
688 : // defined COP.
689 :
690 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] =
691 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::Electricity] - pm->facilityMeterVals[(int)PollFacilityMeter::ElectricityProduced] +
692 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::CoolPurchased] / pm->PurchCoolCOP;
693 :
694 19866 : if (pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] < 0.0) {
695 0 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Electricity] = 0.0;
696 : }
697 :
698 : // The Natural Gas fuel type will be summed from the meters with the District Heating using an efficiency.
699 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::NaturalGas] =
700 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::NaturalGas] +
701 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::HeatPurchased] / pm->PurchHeatEffic +
702 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::Steam] / pm->SteamConvEffic;
703 :
704 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::FuelOil1] = pm->facilityMeterVals[(int)PollFacilityMeter::FuelOil1];
705 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::FuelOil2] = pm->facilityMeterVals[(int)PollFacilityMeter::FuelOil2];
706 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Gasoline] = pm->facilityMeterVals[(int)PollFacilityMeter::Gasoline];
707 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Propane] = pm->facilityMeterVals[(int)PollFacilityMeter::Propane];
708 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Coal] = pm->facilityMeterVals[(int)PollFacilityMeter::Coal];
709 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::Diesel] = pm->facilityMeterVals[(int)PollFacilityMeter::Diesel];
710 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::OtherFuel1] = pm->facilityMeterVals[(int)PollFacilityMeter::OtherFuel1];
711 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::OtherFuel2] = pm->facilityMeterVals[(int)PollFacilityMeter::OtherFuel2];
712 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricityPurchased] =
713 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::ElectricityPurchased];
714 19866 : pm->facilityMeterFuelComponentVals[(int)PollFuelComponent::ElectricitySurplusSold] =
715 19866 : pm->facilityMeterVals[(int)PollFacilityMeter::ElectricitySurplusSold];
716 19866 : }
717 :
718 : // *****************************************************************************
719 : // Utility Routines to allow access to data inside this module.
720 : // *****************************************************************************
721 :
722 963 : void GetFuelFactorInfo(EnergyPlusData &state,
723 : Constant::eFuel fuel, // input fuel name (standard from Tabular reports)
724 : bool &fuelFactorUsed, // return value true if user has entered this fuel
725 : Real64 &fuelSourceFactor, // if used, the source factor
726 : bool &fuelFactorScheduleUsed, // if true, schedules for this fuel are used
727 : Sched::Schedule **ffSched // if schedules for this fuel are used, return schedule index
728 : )
729 : {
730 :
731 : // SUBROUTINE INFORMATION:
732 : // AUTHOR Linda Lawrie
733 : // DATE WRITTEN July 2008
734 :
735 : // PURPOSE OF THIS SUBROUTINE:
736 : // This routine allows access to data inside this module from other modules (specifically the
737 : // output tabular reports.
738 963 : auto const &pm = state.dataPollution;
739 :
740 963 : if (pm->GetInputFlagPollution) {
741 11 : GetPollutionFactorInput(state);
742 11 : pm->GetInputFlagPollution = false;
743 : }
744 :
745 963 : fuelFactorUsed = false;
746 963 : fuelSourceFactor = 0.0;
747 963 : fuelFactorScheduleUsed = false;
748 963 : *ffSched = nullptr;
749 :
750 963 : PollFuel pollFuel = fuel2pollFuel[(int)fuel];
751 963 : auto const &pollCoeff = pm->pollCoeffs[(int)pollFuel];
752 :
753 963 : if (pollCoeff.used) {
754 0 : fuelFactorUsed = true;
755 0 : fuelSourceFactor = pollCoeff.sourceCoeff;
756 0 : if (pollCoeff.sourceSched == nullptr) {
757 0 : fuelFactorScheduleUsed = false;
758 : } else {
759 0 : fuelFactorScheduleUsed = true;
760 0 : *ffSched = pollCoeff.sourceSched;
761 : }
762 : } else {
763 963 : fuelSourceFactor = pollFuelFactors[(int)pollFuel];
764 : }
765 :
766 963 : if (fuel == Constant::eFuel::DistrictHeatingWater) {
767 74 : fuelSourceFactor /= pm->PurchHeatEffic;
768 889 : } else if (fuel == Constant::eFuel::DistrictCooling) {
769 74 : fuelSourceFactor /= pm->PurchCoolCOP;
770 815 : } else if (fuel == Constant::eFuel::DistrictHeatingSteam) {
771 75 : fuelSourceFactor = 0.3 / pm->SteamConvEffic;
772 : }
773 963 : }
774 :
775 74 : void GetEnvironmentalImpactFactorInfo(EnergyPlusData &state,
776 : Real64 &efficiencyDistrictHeatingWater, // if entered, the efficiency of District Heating Water
777 : Real64 &efficiencyDistrictCooling, // if entered, the efficiency of District Cooling
778 : Real64 &sourceFactorDistrictHeatingSteam // if entered, the source factor for Dictrict Heating Steam
779 : )
780 : {
781 :
782 : // SUBROUTINE INFORMATION:
783 : // AUTHOR Linda Lawrie
784 : // DATE WRITTEN August 2008
785 :
786 : // PURPOSE OF THIS SUBROUTINE:
787 : // This routine allows access to data inside this module from other modules (specifically the
788 : // output tabular reports.
789 :
790 74 : auto const &pm = state.dataPollution;
791 74 : if (pm->GetInputFlagPollution) {
792 0 : GetPollutionFactorInput(state);
793 0 : pm->GetInputFlagPollution = false;
794 : }
795 :
796 74 : if (pm->NumEnvImpactFactors > 0) {
797 0 : efficiencyDistrictHeatingWater = pm->PurchHeatEffic;
798 0 : sourceFactorDistrictHeatingSteam = pm->SteamConvEffic;
799 0 : efficiencyDistrictCooling = pm->PurchCoolCOP;
800 : }
801 74 : }
802 :
803 : } // namespace EnergyPlus::Pollution
|