Add emissions to generators

This how-to shows how to attach EmissionsData supplemental attributes to generators. For background on emissions metadata, see Emissions metadata.

Prerequisites

using PowerSystems
using PowerSystemCaseBuilder

sys = build_system(PSITestSystems, "c_sys5_uc")
thermals = collect(get_components(ThermalStandard, sys))
5-element Vector{ThermalStandard}:
 ThermalStandard(Solitude, true, true, ACBus(3, nodeC, true, ACBusTypes.PV = 2, 0.0, 1.0, (min = 0.9, max = 1.05), 230.0, nothing, nothing, Dict{String, Any}()), 2.7, 0.0, 5.2, (min = 1.0, max = 5.2), (min = -3.9, max = 3.9), (up = 0.00624, down = 0.00624), ThermalGenerationCost(CostCurve{QuadraticCurve}(QuadraticCurve(0.0, 30.0, 0.0), UnitSystem.NATURAL_UNITS = 2, LinearCurve(0.0, 0.0)), 0.0, 3.0, 1.5), 100.0, (up = 5.0, down = 3.0), false, PrimeMovers.ST = 20, ThermalFuels.COAL = 1, Service[], 10000.0, nothing, Dict{String, Any}())
 ThermalStandard(Park City, true, false, ACBus(1, nodeA, true, ACBusTypes.PV = 2, 0.0, 1.0, (min = 0.9, max = 1.05), 230.0, nothing, nothing, Dict{String, Any}()), 0.0, 0.0, 2.2125, (min = 0.65, max = 1.7), (min = -1.275, max = 1.275), (up = 0.04425, down = 0.04425), ThermalGenerationCost(CostCurve{QuadraticCurve}(QuadraticCurve(0.0, 15.0, 0.0), UnitSystem.NATURAL_UNITS = 2, LinearCurve(0.0, 0.0)), 0.0, 1.5, 0.75), 100.0, (up = 0.0, down = 0.0), false, PrimeMovers.ST = 20, ThermalFuels.COAL = 1, Service[], 10000.0, nothing, Dict{String, Any}())
 ThermalStandard(Alta, true, false, ACBus(1, nodeA, true, ACBusTypes.PV = 2, 0.0, 1.0, (min = 0.9, max = 1.05), 230.0, nothing, nothing, Dict{String, Any}()), 0.0, 0.0, 0.5, (min = 0.2, max = 0.4), (min = -0.3, max = 0.3), (up = 0.4, down = 0.4), ThermalGenerationCost(CostCurve{LinearCurve}(LinearCurve(14.0, 0.0), UnitSystem.NATURAL_UNITS = 2, LinearCurve(0.0, 0.0)), 0.0, 4.0, 2.0), 100.0, (up = 0.0, down = 0.0), false, PrimeMovers.ST = 20, ThermalFuels.COAL = 1, Service[], 10000.0, nothing, Dict{String, Any}())
 ThermalStandard(Brighton, true, true, ACBus(5, nodeE, true, ACBusTypes.PV = 2, 0.0, 1.0, (min = 0.9, max = 1.05), 230.0, nothing, nothing, Dict{String, Any}()), 6.0, 0.0, 7.5, (min = 3.0, max = 6.0), (min = -4.5, max = 4.5), (up = 0.01125, down = 0.01125), ThermalGenerationCost(CostCurve{QuadraticCurve}(QuadraticCurve(0.0, 10.0, 0.0), UnitSystem.NATURAL_UNITS = 2, LinearCurve(0.0, 0.0)), 0.0, 1.5, 0.75), 100.0, (up = 5.0, down = 3.0), false, PrimeMovers.ST = 20, ThermalFuels.COAL = 1, Service[], 10000.0, nothing, Dict{String, Any}())
 ThermalStandard(Sundance, true, false, ACBus(4, nodeD, true, ACBusTypes.REF = 3, 0.0, 1.0, (min = 0.9, max = 1.05), 230.0, nothing, nothing, Dict{String, Any}()), 0.0, 0.0, 2.5, (min = 1.0, max = 2.0), (min = -1.5, max = 1.5), (up = 0.0375, down = 0.0375), ThermalGenerationCost(CostCurve{QuadraticCurve}(QuadraticCurve(0.0, 40.0, 0.0), UnitSystem.NATURAL_UNITS = 2, LinearCurve(0.0, 0.0)), 0.0, 4.0, 2.0), 100.0, (up = 2.0, down = 1.0), false, PrimeMovers.ST = 20, ThermalFuels.COAL = 1, Service[], 10000.0, nothing, Dict{String, Any}())

Create a constant-rate emissions attribute

A scalar emission_rate is automatically wrapped in a constant-rate IncrementalCurve:

co2 = EmissionsData(;
    name = "co2_ccgt",
    pollutant = PollutantType.CO2,
    emission_rate = 117.6,
    basis = EmissionBasis.FUEL_INPUT,
    energy_unit = EnergyUnit.MMBTU,
)
EmissionsData: 280e2f49-d82e-478d-a405-e83382772de9:
   name: co2_ccgt
   pollutant: PollutantType.CO2 = 1
   emission_rate: IncrementalCurve{LinearFunctionData}(LinearFunctionData(0.0, 117.6), nothing, nothing)
   basis: EmissionBasis.FUEL_INPUT = 1
   start_up_adder: 0.0
   mass_unit: MassUnit.KG = 1
   energy_unit: EnergyUnit.MMBTU = 1
   gwp: 1.0
   available: true
   ext: Dict{String, Any}()
   has_time_series: false

Create a varying-rate emissions attribute

Pass a ValueCurve directly for nonlinear or piecewise relationships:

nox = EmissionsData(;
    name = "nox_ccgt",
    pollutant = PollutantType.NOX,
    emission_rate = IncrementalCurve(LinearFunctionData(0.001, 0.01), nothing, nothing),
    basis = EmissionBasis.FUEL_INPUT,
    energy_unit = EnergyUnit.MMBTU,
    start_up_adder = 5.0,
)
EmissionsData: 62a1747d-9130-4463-a07d-88275dae9b7d:
   name: nox_ccgt
   pollutant: PollutantType.NOX = 10
   emission_rate: IncrementalCurve{LinearFunctionData}(LinearFunctionData(0.001, 0.01), nothing, nothing)
   basis: EmissionBasis.FUEL_INPUT = 1
   start_up_adder: 5.0
   mass_unit: MassUnit.KG = 1
   energy_unit: EnergyUnit.MMBTU = 1
   gwp: 1.0
   available: true
   ext: Dict{String, Any}()
   has_time_series: false

Attach to generators

Attach attributes with add_supplemental_attribute!. The same EmissionsData instance can be shared across multiple units:

add_supplemental_attribute!(sys, thermals[1], co2)
add_supplemental_attribute!(sys, thermals[2], co2)
add_supplemental_attribute!(sys, thermals[1], nox)

Verify attachments with get_component_supplemental_attribute_pairs:

pairs = collect(
    get_component_supplemental_attribute_pairs(ThermalStandard, EmissionsData, sys),
)
length(pairs)
3

See also