Merge pull request #147 from PyPSA/agriculture-energy-co2

Add agriculture, forestry and fishing
This commit is contained in:
Fabian Neumann 2021-10-04 11:10:46 +02:00 committed by GitHub
commit cecee2c9c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 18 deletions

View File

@ -21,9 +21,9 @@ it.
PyPSA-Eur-Sec builds on the electricity generation and transmission
model [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur) to add demand
and supply for the following sectors: transport, space and water
heating, biomass, industry and industrial feedstocks. This completes
the energy system and includes all greenhouse gas emitters except
waste management, agriculture, forestry and land use.
heating, biomass, industry and industrial feedstocks, agriculture,
forestry and fishing. This completes the energy system and includes
all greenhouse gas emitters except waste management and land use.
Please see the [documentation](https://pypsa-eur-sec.readthedocs.io/)
for installation instructions and other useful information about the snakemake workflow.

View File

@ -9,7 +9,6 @@ wildcard_constraints:
lv="[a-z0-9\.]+",
simpl="[a-zA-Z0-9]*",
clusters="[0-9]+m?",
sectors="[+a-zA-Z0-9]+",
opts="[-+a-zA-Z0-9]*",
sector_opts="[-+a-zA-Z0-9\.\s]*"

View File

@ -21,13 +21,14 @@ scenario:
opts: # only relevant for PyPSA-Eur
- ''
sector_opts: # this is where the main scenario settings are
- Co2L0-3H-T-H-B-I-solar+p3-dist1
- Co2L0-3H-T-H-B-I-A-solar+p3-dist1
# to really understand the options here, look in scripts/prepare_sector_network.py
# Co2Lx specifies the CO2 target in x% of the 1990 values; default will give default (5%);
# Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions
# xH is the temporal resolution; 3H is 3-hourly, i.e. one snapshot every 3 hours
# single letters are sectors: T for land transport, H for building heating,
# B for biomass supply, I for industry, shipping and aviation
# B for biomass supply, I for industry, shipping and aviation,
# A for agriculture, forestry and fishing
# solar+c0.5 reduces the capital cost of solar to 50\% of reference value
# solar+p3 multiplies the available installable potential by factor 3
# co2 stored+e2 multiplies the potential of CO2 sequestration by a factor 2
@ -181,6 +182,9 @@ sector:
2050: 0.85
transport_fuel_cell_efficiency: 0.5
transport_internal_combustion_efficiency: 0.3
agriculture_machinery_electric_share: 0
agriculture_machinery_fuel_efficiency: 0.7 # fuel oil per use
agriculture_machinery_electric_efficiency: 0.3 # electricity per use
shipping_average_efficiency: 0.4 #For conversion of fuel oil to propulsion in 2011
shipping_hydrogen_liquefaction: false # whether to consider liquefaction costs for shipping H2 demands
shipping_hydrogen_share: # 1 means all hydrogen FC
@ -477,6 +481,10 @@ plotting:
CC: k
co2: '#123456'
co2 vent: '#654321'
agriculture heat: '#D07A7A'
agriculture machinery oil: '#1e1e1e'
agriculture machinery oil emissions: '#111111'
agriculture electricity: '#222222'
solid biomass for industry co2 from atmosphere: '#654321'
solid biomass for industry co2 to stored: '#654321'
gas for industry co2 to atmosphere: '#654321'

View File

@ -86,6 +86,10 @@ Future release
spatially disaggregate biomass potentials to PyPSA-Eur regions based on overlaps with NUTS2 regions from ENSPRESO
(proportional to area) (`#151 <https://github.com/PyPSA/pypsa-eur-sec/pull/151>`_).
* Compatibility with ``xarray`` version 0.19.
* Added option to include emissions and energy demands of agriculture, forestry and fishing sector via the letter ``A`` in the ``{sector_opts}`` wildcard.
Demands are separated into electricity, heat and oil for machinery.
Fuel-switching for machinery from oil to electricity can be set exogenously in the ``config.yaml``
`#147 <https://github.com/PyPSA/PyPSA/pull/147>`_.
* Separate basic chemicals into HVC, chlorine, methanol and ammonia [`#166 <https://github.com/PyPSA/PyPSA-Eur-Sec/pull/166>`_].
* Add option to specify reuse, primary production, and mechanical and chemical recycling fraction of platics [`#166 <https://github.com/PyPSA/PyPSA-Eur-Sec/pull/166>`_].
* Include today's district heating shares in myopic optimisation and add option to specify exogenous path for district heating share increase under ``sector: district_heating:`` [`#149 <https://github.com/PyPSA/PyPSA-Eur-Sec/pull/149>`_].

View File

@ -43,7 +43,7 @@ Heat demand is split into:
* ``urban central``: large-scale district heating networks in urban areas with dense heat demand
* ``residential/services urban decentral``: heating for individual buildings in urban areas
* ``residential/services rural``: heating for individual buildings in rural areas
* ``residential/services rural``: heating for individual buildings in rural areas, agriculture heat uses
Heat supply
@ -189,7 +189,7 @@ Only wastes and residues from the JRC ENSPRESO biomass dataset.
Oil product demand
=====================
Transport fuels and naphtha as a feedstock for the chemicals industry.
Transport fuels, agriculture machinery and naphtha as a feedstock for the chemicals industry.
Oil product supply
======================

View File

@ -117,6 +117,7 @@ to_ipcc = {
"total energy": "1 - Energy",
"industrial processes": "2 - Industrial Processes and Product Use",
"agriculture": "3 - Agriculture",
"agriculture, forestry and fishing": '1.A.4.c - Agriculture/Forestry/Fishing',
"LULUCF": "4 - Land Use, Land-Use Change and Forestry",
"waste management": "5 - Waste management",
"other": "6 - Other Sector",
@ -182,7 +183,7 @@ def idees_per_country(ct, year):
ct_idees = idees_rename.get(ct, ct)
fn_residential = f"{base_dir}/JRC-IDEES-2015_Residential_{ct_idees}.xlsx"
fn_services = f"{base_dir}/JRC-IDEES-2015_Tertiary_{ct_idees}.xlsx"
fn_tertiary = f"{base_dir}/JRC-IDEES-2015_Tertiary_{ct_idees}.xlsx"
fn_transport = f"{base_dir}/JRC-IDEES-2015_Transport_{ct_idees}.xlsx"
# residential
@ -213,14 +214,14 @@ def idees_per_country(ct, year):
ct_totals["electricity residential"] = df[47]
assert df.index[46] == "Derived heat"
ct_totals["Derived heat residential"] = df[46]
ct_totals["derived heat residential"] = df[46]
assert df.index[50] == 'Thermal uses'
ct_totals["thermal uses residential"] = df[50]
# services
df = pd.read_excel(fn_services, "SER_hh_fec", index_col=0)[year]
df = pd.read_excel(fn_tertiary, "SER_hh_fec", index_col=0)[year]
ct_totals["total services space"] = df["Space heating"]
@ -237,7 +238,7 @@ def idees_per_country(ct, year):
assert df.index[31] == "Electricity"
ct_totals["electricity services cooking"] = df[31]
df = pd.read_excel(fn_services, "SER_summary", index_col=0)[year]
df = pd.read_excel(fn_tertiary, "SER_summary", index_col=0)[year]
row = "Energy consumption by fuel - Eurostat structure (ktoe)"
ct_totals["total services"] = df[row]
@ -251,6 +252,35 @@ def idees_per_country(ct, year):
assert df.index[53] == 'Thermal uses'
ct_totals["thermal uses services"] = df[53]
# agriculture, forestry and fishing
start = "Detailed split of energy consumption (ktoe)"
end = "Market shares of energy uses (%)"
df = pd.read_excel(fn_tertiary, "AGR_fec", index_col=0).loc[start:end, year]
rows = [
"Lighting",
"Ventilation",
"Specific electricity uses",
"Pumping devices (electric)"
]
ct_totals["total agriculture electricity"] = df[rows].sum()
rows = ["Specific heat uses", "Low enthalpy heat"]
ct_totals["total agriculture heat"] = df[rows].sum()
rows = [
"Motor drives",
"Farming machine drives (diesel oil incl. biofuels)",
"Pumping devices (diesel oil incl. biofuels)",
]
ct_totals["total agriculture machinery"] = df[rows].sum()
row = "Agriculture, forestry and fishing"
ct_totals["total agriculture"] = df[row]
# transport
df = pd.read_excel(fn_transport, "TrRoad_ene", index_col=0)[year]
@ -568,10 +598,13 @@ def build_eea_co2(year=1990):
"international aviation",
"domestic navigation",
"international navigation",
"agriculture, forestry and fishing"
]
emissions["industrial non-elec"] = emissions["total energy"] - emissions[to_subtract].sum(axis=1)
to_drop = ["total energy", "total wL", "total woL"]
emissions["agriculture"] += emissions["agriculture, forestry and fishing"]
to_drop = ["total energy", "total wL", "total woL", "agriculture, forestry and fishing"]
emissions.drop(columns=to_drop, inplace=True)
# convert from Gg to Mt
@ -616,7 +649,7 @@ def build_co2_totals(countries, eea_co2, eurostat_co2):
# does not include industrial process emissions or fuel processing/refining
"industrial non-elec": (ct, "+", "Industry"),
# does not include non-energy emissions
"agriculture": (ct, "+", "+", "Agriculture / Forestry"),
"agriculture": (eurostat_co2.index.get_level_values(0) == ct) & eurostat_co2.index.isin(["Agriculture / Forestry", "Fishing"], level=3),
}
for i, mi in mappings.items():

View File

@ -92,6 +92,10 @@ def emission_sectors_from_opts(opts):
"domestic navigation",
"international navigation"
]
if "A" in opts:
sectors += [
"agriculture"
]
return sectors
@ -882,8 +886,9 @@ def insert_electricity_distribution_grid(n, costs):
capital_cost=costs.at['electricity distribution grid', 'fixed'] * cost_factor
)
# this catches regular electricity load and "industry electricity"
loads = n.loads.index[n.loads.carrier.str.contains("electricity")]
# this catches regular electricity load and "industry electricity" and
# "agriculture machinery electric" and "agriculture electricity"
loads = n.loads.index[n.loads.carrier.str.contains("electric")]
n.loads.loc[loads, "bus"] += " low voltage"
bevs = n.links.index[n.links.carrier == "BEV charger"]
@ -1321,8 +1326,8 @@ def add_land_transport(n, costs):
co2 = ice_share / ice_efficiency * transport[nodes].sum().sum() / 8760 * costs.at["oil", 'CO2 intensity']
n.madd("Load",
["land transport oil emissions"],
n.add("Load",
"land transport oil emissions",
bus="co2 atmosphere",
carrier="land transport oil emissions",
p_set=-co2
@ -2161,6 +2166,71 @@ def add_waste_heat(n):
n.links.loc[urban_central + " H2 Fuel Cell", "efficiency2"] = 0.95 - n.links.loc[urban_central + " H2 Fuel Cell", "efficiency"]
def add_agriculture(n, costs):
logger.info('Add agriculture, forestry and fishing sector.')
nodes = pop_layout.index
# electricity
n.madd("Load",
nodes,
suffix=" agriculture electricity",
bus=nodes,
carrier='agriculture electricity',
p_set=nodal_energy_totals.loc[nodes, "total agriculture electricity"] * 1e6 / 8760
)
# heat
n.madd("Load",
nodes,
suffix=" agriculture heat",
bus=nodes + " services rural heat",
carrier="agriculture heat",
p_set=nodal_energy_totals.loc[nodes, "total agriculture heat"] * 1e6 / 8760
)
# machinery
electric_share = get(options["agriculture_machinery_electric_share"], investment_year)
assert electric_share <= 1.
ice_share = 1 - electric_share
machinery_nodal_energy = nodal_energy_totals.loc[nodes, "total agriculture machinery"]
if electric_share > 0:
efficiency_gain = options["agriculture_machinery_fuel_efficiency"] / options["agriculture_machinery_electric_efficiency"]
n.madd("Load",
nodes,
suffix=" agriculture machinery electric",
bus=nodes,
carrier="agriculture machinery electric",
p_set=electric_share / efficiency_gain * machinery_nodal_energy * 1e6 / 8760,
)
if ice_share > 0:
n.add("Load",
"agriculture machinery oil",
bus="EU oil",
carrier="agriculture machinery oil",
p_set=ice_share * machinery_nodal_energy.sum() * 1e6 / 8760
)
co2 = ice_share * machinery_nodal_energy.sum() * 1e6 / 8760 * costs.at["oil", 'CO2 intensity']
n.add("Load",
"agriculture machinery oil emissions",
bus="co2 atmosphere",
carrier="agriculture machinery oil emissions",
p_set=-co2
)
def decentral(n):
"""Removes the electricity transmission system."""
n.lines.drop(n.lines.index, inplace=True)
@ -2297,6 +2367,9 @@ if __name__ == "__main__":
if "I" in opts and "H" in opts:
add_waste_heat(n)
if "A" in opts: # requires H and I
add_agriculture(n, costs)
if options['dac']:
add_dac(n, costs)