Skip to content

Commit

Permalink
options: biosng_cc, biomass_to_liquid_cc, 98% capture rate Allam gas,… (
Browse files Browse the repository at this point in the history
#1298)

* options: biosng_cc, biomass_to_liquid_cc, 98% capture rate Allam gas, avoid option.get

* fix duplicated bus port
  • Loading branch information
fneum authored Sep 16, 2024
1 parent df71b1a commit d8f8a82
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 37 deletions.
2 changes: 2 additions & 0 deletions config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -693,8 +693,10 @@ sector:
conventional_generation:
OCGT: gas
biomass_to_liquid: false
biomass_to_liquid_cc: false
electrobiofuels: false
biosng: false
biosng_cc: false
bioH2: false
municipal_solid_waste: false
limit_max_growth:
Expand Down
2 changes: 2 additions & 0 deletions doc/configtables/sector.csv
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ biomass_transport,--,"{true, false}",Add option for transporting solid biomass b
biogas_upgrading_cc,--,"{true, false}",Add option to capture CO2 from biomass upgrading
conventional_generation,,,Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.
biomass_to_liquid,--,"{true, false}",Add option for transforming solid biomass into liquid fuel with the same properties as oil
biomass_to_liquid_cc,--,"{true, false}",Add option for transforming solid biomass into liquid fuel with the same properties as oil with carbon capture
biosng,--,"{true, false}",Add option for transforming solid biomass into synthesis gas with the same properties as natural gas
biosng_cc,--,"{true, false}",Add option for transforming solid biomass into synthesis gas with the same properties as natural gas with carbon capture
bioH2,--,"{true, false}",Add option for transforming solid biomass into hydrogen with carbon capture
municipal_solid_waste,--,"{true, false}",Add option for municipal solid waste
limit_max_growth,,,
Expand Down
5 changes: 5 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ Upcoming Release
defaults to 10 km. Previously the distance to the region's centroid was
used, which is not practical when the regions are already aggregated.

* Added options ``biosng_cc`` and ``biomass_to_liquid_cc`` to separate the base
technology from the option to capture carbon from it.

* Added 98% imperfect capture rate of Allam cycle gas turbine.

PyPSA-Eur 0.13.0 (13th September 2024)
======================================

Expand Down
76 changes: 39 additions & 37 deletions scripts/prepare_sector_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ def define_spatial(nodes, options):

# ammonia

if options.get("ammonia"):
if options["ammonia"]:
spatial.ammonia = SimpleNamespace()
if options.get("ammonia") == "regional":
if options["ammonia"] == "regional":
spatial.ammonia.nodes = nodes + " NH3"
spatial.ammonia.locations = nodes
else:
Expand Down Expand Up @@ -519,7 +519,7 @@ def add_carrier_buses(n, carrier, nodes=None):
)

fossils = ["coal", "gas", "oil", "lignite"]
if options.get("fossil_fuels", True) and carrier in fossils:
if options["fossil_fuels"] and carrier in fossils:

suffix = ""

Expand Down Expand Up @@ -750,25 +750,26 @@ def add_co2_network(n, costs):
)


def add_allam(n, costs):
def add_allam_gas(n, costs):
logger.info("Adding Allam cycle gas power plants.")

nodes = pop_layout.index

n.madd(
"Link",
nodes,
suffix=" allam",
suffix=" allam gas",
bus0=spatial.gas.df.loc[nodes, "nodes"].values,
bus1=nodes,
bus2=spatial.co2.df.loc[nodes, "nodes"].values,
carrier="allam",
bus3="co2 atmosphere",
carrier="allam gas",
p_nom_extendable=True,
# TODO: add costs to technology-data
capital_cost=costs.at["allam", "fixed"] * costs.at["allam", "efficiency"],
marginal_cost=costs.at["allam", "VOM"] * costs.at["allam", "efficiency"],
efficiency=costs.at["allam", "efficiency"],
efficiency2=costs.at["gas", "CO2 intensity"],
efficiency2=0.98 * costs.at["gas", "CO2 intensity"],
efficiency3=0.02 * costs.at["gas", "CO2 intensity"],
lifetime=costs.at["allam", "lifetime"],
)

Expand Down Expand Up @@ -823,8 +824,10 @@ def add_biomass_to_methanol_cc(n, costs):
)


def add_methanol_to_power(n, costs, types={}):
# TODO: add costs to technology-data
def add_methanol_to_power(n, costs, types=None):

if types is None:
types = {}

nodes = pop_layout.index

Expand Down Expand Up @@ -1121,8 +1124,7 @@ def add_generation(n, costs):

nodes = pop_layout.index

fallback = {"OCGT": "gas"}
conventionals = options.get("conventional_generation", fallback)
conventionals = options["conventional_generation"]

for generator, carrier in conventionals.items():
carrier_nodes = vars(spatial)[carrier].nodes
Expand Down Expand Up @@ -1564,7 +1566,7 @@ def add_storage_and_grids(n, costs):
complement_edges["length"] = complement_edges.apply(haversine, axis=1)

# apply k_edge_augmentation weighted by length of complement edges
k_edge = options.get("gas_network_connectivity_upgrade", 3)
k_edge = options["gas_network_connectivity_upgrade"]
if augmentation := list(
k_edge_augmentation(G, k_edge, avail=complement_edges.values)
):
Expand Down Expand Up @@ -1612,7 +1614,7 @@ def add_storage_and_grids(n, costs):
lifetime=costs.at["H2 (g) pipeline repurposed", "lifetime"],
)

if options.get("H2_network", True):
if options["H2_network"]:
logger.info("Add options for new hydrogen pipelines.")

h2_pipes = create_network_topology(
Expand Down Expand Up @@ -1682,7 +1684,7 @@ def add_storage_and_grids(n, costs):
bus2=spatial.co2.nodes,
p_nom_extendable=True,
carrier="Sabatier",
p_min_pu=options.get("min_part_load_methanation", 0),
p_min_pu=options["min_part_load_methanation"],
efficiency=costs.at["methanation", "efficiency"],
efficiency2=-costs.at["methanation", "efficiency"]
* costs.at["gas", "CO2 intensity"],
Expand All @@ -1691,7 +1693,7 @@ def add_storage_and_grids(n, costs):
lifetime=costs.at["methanation", "lifetime"],
)

if options.get("coal_cc"):
if options["coal_cc"]:
n.madd(
"Link",
spatial.nodes,
Expand Down Expand Up @@ -1835,7 +1837,7 @@ def add_EVs(
p_set=profile,
)

p_nom = number_cars * options.get("bev_charge_rate", 0.011) * electric_share
p_nom = number_cars * options["bev_charge_rate"] * electric_share

n.madd(
"Link",
Expand All @@ -1847,7 +1849,7 @@ def add_EVs(
carrier="BEV charger",
p_max_pu=avail_profile[spatial.nodes],
lifetime=1,
efficiency=options.get("bev_charge_efficiency", 0.9),
efficiency=options["bev_charge_efficiency"],
)

if options["v2g"]:
Expand All @@ -1861,13 +1863,13 @@ def add_EVs(
carrier="V2G",
p_max_pu=avail_profile[spatial.nodes],
lifetime=1,
efficiency=options.get("bev_charge_efficiency", 0.9),
efficiency=options["bev_charge_efficiency"],
)

if options["bev_dsm"]:
e_nom = (
number_cars
* options.get("bev_energy", 0.05)
* options["bev_energy"]
* options["bev_availability"]
* electric_share
)
Expand Down Expand Up @@ -2116,7 +2118,7 @@ def add_heat(n: pypsa.Network, costs: pd.DataFrame, cop: xr.DataArray):
unit="MWh_th",
)

if heat_system == HeatSystem.URBAN_CENTRAL and options.get("central_heat_vent"):
if heat_system == HeatSystem.URBAN_CENTRAL and options["central_heat_vent"]:
n.madd(
"Generator",
nodes + f" {heat_system} heat vent",
Expand Down Expand Up @@ -2786,7 +2788,7 @@ def add_biomass(n, costs):
p_nom_extendable=True,
)

if options.get("biogas_upgrading_cc"):
if options["biogas_upgrading_cc"]:
# Assuming for costs that the CO2 from upgrading is pure, such as in amine scrubbing. I.e., with and without CC is
# equivalent. Adding biomass CHP capture because biogas is often small-scale and decentral so further
# from e.g. CO2 grid or buyers. This is a proxy for the added cost for e.g. a raw biogas pipeline to a central upgrading facility
Expand Down Expand Up @@ -3034,6 +3036,8 @@ def add_biomass(n, costs):
marginal_cost=costs.at["BtL", "VOM"] * costs.at["BtL", "efficiency"],
)

# Solid biomass to liquid fuel with carbon capture
if options["biomass_to_liquid_cc"]:
# Assuming that acid gas removal (incl. CO2) from syngas i performed with Rectisol
# process (Methanol) and that electricity demand for this is included in the base process
n.madd(
Expand All @@ -3044,7 +3048,7 @@ def add_biomass(n, costs):
bus1=spatial.oil.nodes,
bus2="co2 atmosphere",
bus3=spatial.co2.nodes,
carrier="biomass to liquid",
carrier="biomass to liquid CC",
lifetime=costs.at["BtL", "lifetime"],
efficiency=costs.at["BtL", "efficiency"],
efficiency2=-costs.at["solid biomass", "CO2 intensity"]
Expand Down Expand Up @@ -3112,6 +3116,8 @@ def add_biomass(n, costs):
marginal_cost=costs.at["BioSNG", "VOM"] * costs.at["BioSNG", "efficiency"],
)

# BioSNG from solid biomass with carbon capture
if options["biosng_cc"]:
# Assuming that acid gas removal (incl. CO2) from syngas i performed with Rectisol
# process (Methanol) and that electricity demand for this is included in the base process
n.madd(
Expand All @@ -3122,7 +3128,7 @@ def add_biomass(n, costs):
bus1=spatial.gas.nodes,
bus2=spatial.co2.nodes,
bus3="co2 atmosphere",
carrier="BioSNG",
carrier="BioSNG CC",
lifetime=costs.at["BioSNG", "lifetime"],
efficiency=costs.at["BioSNG", "efficiency"],
efficiency2=costs.at["BioSNG", "CO2 stored"]
Expand Down Expand Up @@ -3162,10 +3168,6 @@ def add_biomass(n, costs):
* costs.at["solid biomass to hydrogen", "efficiency"]
+ costs.at["biomass CHP capture", "fixed"]
* costs.at["solid biomass", "CO2 intensity"],
overnight_cost=costs.at["solid biomass to hydrogen", "investment"]
* costs.at["solid biomass to hydrogen", "efficiency"]
+ costs.at["biomass CHP capture", "investment"]
* costs.at["solid biomass", "CO2 intensity"],
marginal_cost=0.0,
)

Expand Down Expand Up @@ -3356,7 +3358,7 @@ def add_industry(n, costs):
bus3=spatial.co2.nodes,
carrier="methanolisation",
p_nom_extendable=True,
p_min_pu=options.get("min_part_load_methanolisation", 0),
p_min_pu=options["min_part_load_methanolisation"],
capital_cost=costs.at["methanolisation", "fixed"]
* options["MWh_MeOH_per_MWh_H2"], # EUR/MW_H2/a
marginal_cost=options["MWh_MeOH_per_MWh_H2"]
Expand Down Expand Up @@ -3552,12 +3554,12 @@ def add_industry(n, costs):
efficiency2=-costs.at["oil", "CO2 intensity"]
* costs.at["Fischer-Tropsch", "efficiency"],
p_nom_extendable=True,
p_min_pu=options.get("min_part_load_fischer_tropsch", 0),
p_min_pu=options["min_part_load_fischer_tropsch"],
lifetime=costs.at["Fischer-Tropsch", "lifetime"],
)

# naphtha
demand_factor = options.get("HVC_demand_factor", 1)
demand_factor = options["HVC_demand_factor"]
if demand_factor != 1:
logger.warning(f"Changing HVC demand by {demand_factor*100-100:+.2f}%.")

Expand Down Expand Up @@ -3630,7 +3632,7 @@ def add_industry(n, costs):
efficiency3=process_co2_per_naphtha,
)

if options.get("biomass", True) and options["municipal_solid_waste"]:
if options["biomass"] and options["municipal_solid_waste"]:
n.madd(
"Link",
spatial.msw.locations,
Expand Down Expand Up @@ -3722,7 +3724,7 @@ def add_industry(n, costs):
)

# aviation
demand_factor = options.get("aviation_demand_factor", 1)
demand_factor = options["aviation_demand_factor"]
if demand_factor != 1:
logger.warning(f"Changing aviation demand by {demand_factor*100-100:+.2f}%.")

Expand Down Expand Up @@ -3862,7 +3864,7 @@ def add_industry(n, costs):
lifetime=costs.at["cement capture", "lifetime"],
)

if options.get("ammonia"):
if options["ammonia"]:
if options["ammonia"] == "regional":
p_set = (
industrial_demand.loc[spatial.ammonia.locations, "ammonia"].rename(
Expand Down Expand Up @@ -4639,8 +4641,8 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
if options["co2network"]:
add_co2_network(n, costs)

if options["allam_cycle"]:
add_allam(n, costs)
if options["allam_cycle_gas"]:
add_allam_gas(n, costs)

n = set_temporal_aggregation(
n, snakemake.params.time_resolution, snakemake.input.snapshot_weightings
Expand Down Expand Up @@ -4701,7 +4703,7 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
snakemake.params.planning_horizons[0] == investment_year
)

if options.get("cluster_heat_buses", False) and not first_year_myopic:
if options["cluster_heat_buses"] and not first_year_myopic:
cluster_heat_buses(n)

maybe_adjust_costs_and_potentials(
Expand Down

0 comments on commit d8f8a82

Please sign in to comment.