Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add agriculture, forestry and fishing #147

Merged
merged 16 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 0 additions & 1 deletion Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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]*"

Expand Down
12 changes: 10 additions & 2 deletions config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
4 changes: 4 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>`_].
Expand Down
4 changes: 2 additions & 2 deletions doc/supply_demand.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
======================
Expand Down
45 changes: 39 additions & 6 deletions scripts/build_energy_totals.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def reverse(dictionary):
"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",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"]

Expand All @@ -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]
Expand All @@ -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]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand Down
81 changes: 77 additions & 4 deletions scripts/prepare_sector_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ def emission_sectors_from_opts(opts):
"domestic navigation",
"international navigation"
]
if "A" in opts:
sectors += [
"agriculture"
]

return sectors

Expand Down Expand Up @@ -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"]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -2297,6 +2367,9 @@ def limit_individual_line_extension(n, maxext):
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)

Expand Down