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