Merge pull request #844 from PyPSA/cm
Distinguish sequestered and stored CO2
This commit is contained in:
commit
43a502f2ec
@ -125,6 +125,7 @@ rule sync:
|
|||||||
shell:
|
shell:
|
||||||
"""
|
"""
|
||||||
rsync -uvarh --ignore-missing-args --files-from=.sync-send . {params.cluster}
|
rsync -uvarh --ignore-missing-args --files-from=.sync-send . {params.cluster}
|
||||||
|
rsync -uvarh --no-g {params.cluster}/resources . || echo "No resources directory, skipping rsync"
|
||||||
rsync -uvarh --no-g {params.cluster}/results . || echo "No results directory, skipping rsync"
|
rsync -uvarh --no-g {params.cluster}/results . || echo "No results directory, skipping rsync"
|
||||||
rsync -uvarh --no-g {params.cluster}/logs . || echo "No logs directory, skipping rsync"
|
rsync -uvarh --no-g {params.cluster}/logs . || echo "No logs directory, skipping rsync"
|
||||||
"""
|
"""
|
||||||
|
@ -478,6 +478,7 @@ sector:
|
|||||||
co2_sequestration_lifetime: 50
|
co2_sequestration_lifetime: 50
|
||||||
co2_spatial: false
|
co2_spatial: false
|
||||||
co2network: false
|
co2network: false
|
||||||
|
co2_network_cost_factor: 1
|
||||||
cc_fraction: 0.9
|
cc_fraction: 0.9
|
||||||
hydrogen_underground_storage: true
|
hydrogen_underground_storage: true
|
||||||
hydrogen_underground_storage_locations:
|
hydrogen_underground_storage_locations:
|
||||||
@ -985,6 +986,7 @@ plotting:
|
|||||||
CO2 sequestration: '#f29dae'
|
CO2 sequestration: '#f29dae'
|
||||||
DAC: '#ff5270'
|
DAC: '#ff5270'
|
||||||
co2 stored: '#f2385a'
|
co2 stored: '#f2385a'
|
||||||
|
co2 sequestered: '#f2682f'
|
||||||
co2: '#f29dae'
|
co2: '#f29dae'
|
||||||
co2 vent: '#ffd4dc'
|
co2 vent: '#ffd4dc'
|
||||||
CO2 pipeline: '#f5627f'
|
CO2 pipeline: '#f5627f'
|
||||||
|
@ -93,6 +93,7 @@ co2_sequestration_cost,currency/tCO2,float,The cost of sequestering a ton of CO2
|
|||||||
co2_spatial,--,"{true, false}","Add option to spatially resolve carrier representing stored carbon dioxide. This allows for more detailed modelling of CCUTS, e.g. regarding the capturing of industrial process emissions, usage as feedstock for electrofuels, transport of carbon dioxide, and geological sequestration sites."
|
co2_spatial,--,"{true, false}","Add option to spatially resolve carrier representing stored carbon dioxide. This allows for more detailed modelling of CCUTS, e.g. regarding the capturing of industrial process emissions, usage as feedstock for electrofuels, transport of carbon dioxide, and geological sequestration sites."
|
||||||
,,,
|
,,,
|
||||||
co2network,--,"{true, false}",Add option for planning a new carbon dioxide transmission network
|
co2network,--,"{true, false}",Add option for planning a new carbon dioxide transmission network
|
||||||
|
co2_network_cost_factor,p.u.,float,The cost factor for the capital cost of the carbon dioxide transmission network
|
||||||
,,,
|
,,,
|
||||||
cc_fraction,--,float,The default fraction of CO2 captured with post-combustion capture
|
cc_fraction,--,float,The default fraction of CO2 captured with post-combustion capture
|
||||||
hydrogen_underground _storage,--,"{true, false}",Add options for storing hydrogen underground. Storage potential depends regionally.
|
hydrogen_underground _storage,--,"{true, false}",Add options for storing hydrogen underground. Storage potential depends regionally.
|
||||||
|
|
@ -10,6 +10,13 @@ Release Notes
|
|||||||
Upcoming Release
|
Upcoming Release
|
||||||
================
|
================
|
||||||
|
|
||||||
|
* Distinguish between stored and sequestered CO2. Stored CO2 is stored
|
||||||
|
overground in tanks and can be used for CCU (e.g. methanolisation).
|
||||||
|
Sequestered CO2 is stored underground and can no longer be used for CCU. This
|
||||||
|
distinction is made because storage in tanks is more expensive than
|
||||||
|
underground storage. The link that connects stored and sequestered CO2 is
|
||||||
|
unidirectional.
|
||||||
|
|
||||||
* Increase deployment density of solar to 5.1 MW/sqkm by default.
|
* Increase deployment density of solar to 5.1 MW/sqkm by default.
|
||||||
|
|
||||||
* Default to full electrification of land transport by 2050.
|
* Default to full electrification of land transport by 2050.
|
||||||
|
@ -102,6 +102,9 @@ def define_spatial(nodes, options):
|
|||||||
spatial.gas.biogas = ["EU biogas"]
|
spatial.gas.biogas = ["EU biogas"]
|
||||||
spatial.gas.industry = ["gas for industry"]
|
spatial.gas.industry = ["gas for industry"]
|
||||||
spatial.gas.biogas_to_gas = ["EU biogas to gas"]
|
spatial.gas.biogas_to_gas = ["EU biogas to gas"]
|
||||||
|
if options.get("biomass_spatial", options["biomass_transport"]):
|
||||||
|
spatial.gas.biogas_to_gas_cc = nodes + " biogas to gas CC"
|
||||||
|
else:
|
||||||
spatial.gas.biogas_to_gas_cc = ["EU biogas to gas CC"]
|
spatial.gas.biogas_to_gas_cc = ["EU biogas to gas CC"]
|
||||||
if options.get("co2_spatial", options["co2network"]):
|
if options.get("co2_spatial", options["co2network"]):
|
||||||
spatial.gas.industry_cc = nodes + " gas for industry CC"
|
spatial.gas.industry_cc = nodes + " gas for industry CC"
|
||||||
@ -549,7 +552,7 @@ def patch_electricity_network(n):
|
|||||||
n.loads_t.p_set.rename(lambda x: x.strip(), axis=1, inplace=True)
|
n.loads_t.p_set.rename(lambda x: x.strip(), axis=1, inplace=True)
|
||||||
|
|
||||||
|
|
||||||
def add_co2_tracking(n, options):
|
def add_co2_tracking(n, costs, options):
|
||||||
# minus sign because opposite to how fossil fuels used:
|
# minus sign because opposite to how fossil fuels used:
|
||||||
# CH4 burning puts CH4 down, atmosphere up
|
# CH4 burning puts CH4 down, atmosphere up
|
||||||
n.add("Carrier", "co2", co2_emissions=-1.0)
|
n.add("Carrier", "co2", co2_emissions=-1.0)
|
||||||
@ -567,7 +570,7 @@ def add_co2_tracking(n, options):
|
|||||||
bus="co2 atmosphere",
|
bus="co2 atmosphere",
|
||||||
)
|
)
|
||||||
|
|
||||||
# this tracks CO2 stored, e.g. underground
|
# add CO2 tanks
|
||||||
n.madd(
|
n.madd(
|
||||||
"Bus",
|
"Bus",
|
||||||
spatial.co2.nodes,
|
spatial.co2.nodes,
|
||||||
@ -576,6 +579,39 @@ def add_co2_tracking(n, options):
|
|||||||
unit="t_co2",
|
unit="t_co2",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
n.madd(
|
||||||
|
"Store",
|
||||||
|
spatial.co2.nodes,
|
||||||
|
e_nom_extendable=True,
|
||||||
|
capital_cost=costs.at["CO2 storage tank", "fixed"],
|
||||||
|
carrier="co2 stored",
|
||||||
|
e_cyclic=True,
|
||||||
|
bus=spatial.co2.nodes,
|
||||||
|
)
|
||||||
|
n.add("Carrier", "co2 stored")
|
||||||
|
|
||||||
|
# this tracks CO2 sequestered, e.g. underground
|
||||||
|
sequestration_buses = pd.Index(spatial.co2.nodes).str.replace(
|
||||||
|
" stored", " sequestered"
|
||||||
|
)
|
||||||
|
n.madd(
|
||||||
|
"Bus",
|
||||||
|
sequestration_buses,
|
||||||
|
location=spatial.co2.locations,
|
||||||
|
carrier="co2 sequestered",
|
||||||
|
unit="t_co2",
|
||||||
|
)
|
||||||
|
|
||||||
|
n.madd(
|
||||||
|
"Link",
|
||||||
|
sequestration_buses,
|
||||||
|
bus0=spatial.co2.nodes,
|
||||||
|
bus1=sequestration_buses,
|
||||||
|
carrier="co2 sequestered",
|
||||||
|
efficiency=1.0,
|
||||||
|
p_nom_extendable=True,
|
||||||
|
)
|
||||||
|
|
||||||
if options["regional_co2_sequestration_potential"]["enable"]:
|
if options["regional_co2_sequestration_potential"]["enable"]:
|
||||||
upper_limit = (
|
upper_limit = (
|
||||||
options["regional_co2_sequestration_potential"]["max_size"] * 1e3
|
options["regional_co2_sequestration_potential"]["max_size"] * 1e3
|
||||||
@ -591,22 +627,22 @@ def add_co2_tracking(n, options):
|
|||||||
.mul(1e6)
|
.mul(1e6)
|
||||||
/ annualiser
|
/ annualiser
|
||||||
) # t
|
) # t
|
||||||
e_nom_max = e_nom_max.rename(index=lambda x: x + " co2 stored")
|
e_nom_max = e_nom_max.rename(index=lambda x: x + " co2 sequestered")
|
||||||
else:
|
else:
|
||||||
e_nom_max = np.inf
|
e_nom_max = np.inf
|
||||||
|
|
||||||
n.madd(
|
n.madd(
|
||||||
"Store",
|
"Store",
|
||||||
spatial.co2.nodes,
|
sequestration_buses,
|
||||||
e_nom_extendable=True,
|
e_nom_extendable=True,
|
||||||
e_nom_max=e_nom_max,
|
e_nom_max=e_nom_max,
|
||||||
capital_cost=options["co2_sequestration_cost"],
|
capital_cost=options["co2_sequestration_cost"],
|
||||||
carrier="co2 stored",
|
bus=sequestration_buses,
|
||||||
bus=spatial.co2.nodes,
|
|
||||||
lifetime=options["co2_sequestration_lifetime"],
|
lifetime=options["co2_sequestration_lifetime"],
|
||||||
|
carrier="co2 sequestered",
|
||||||
)
|
)
|
||||||
|
|
||||||
n.add("Carrier", "co2 stored")
|
n.add("Carrier", "co2 sequestered")
|
||||||
|
|
||||||
if options["co2_vent"]:
|
if options["co2_vent"]:
|
||||||
n.madd(
|
n.madd(
|
||||||
@ -635,6 +671,8 @@ def add_co2_network(n, costs):
|
|||||||
* co2_links.length
|
* co2_links.length
|
||||||
)
|
)
|
||||||
capital_cost = cost_onshore + cost_submarine
|
capital_cost = cost_onshore + cost_submarine
|
||||||
|
cost_factor = snakemake.config["sector"]["co2_network_cost_factor"]
|
||||||
|
capital_cost *= cost_factor
|
||||||
|
|
||||||
n.madd(
|
n.madd(
|
||||||
"Link",
|
"Link",
|
||||||
@ -2224,13 +2262,12 @@ def add_biomass(n, costs):
|
|||||||
# Assuming for costs that the CO2 from upgrading is pure, such as in amine scrubbing. I.e., with and without CC is
|
# 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
|
# 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
|
# 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
|
||||||
|
|
||||||
n.madd(
|
n.madd(
|
||||||
"Link",
|
"Link",
|
||||||
spatial.gas.biogas_to_gas_cc,
|
spatial.gas.biogas_to_gas_cc,
|
||||||
bus0=spatial.gas.biogas,
|
bus0=spatial.gas.biogas,
|
||||||
bus1=spatial.gas.nodes,
|
bus1=spatial.gas.nodes,
|
||||||
bus2="co2 stored",
|
bus2=spatial.co2.nodes,
|
||||||
bus3="co2 atmosphere",
|
bus3="co2 atmosphere",
|
||||||
carrier="biogas to gas CC",
|
carrier="biogas to gas CC",
|
||||||
capital_cost=costs.at["biogas CC", "fixed"]
|
capital_cost=costs.at["biogas CC", "fixed"]
|
||||||
@ -2297,6 +2334,14 @@ def add_biomass(n, costs):
|
|||||||
marginal_cost=costs.at["solid biomass", "fuel"]
|
marginal_cost=costs.at["solid biomass", "fuel"]
|
||||||
+ bus_transport_costs * average_distance,
|
+ bus_transport_costs * average_distance,
|
||||||
)
|
)
|
||||||
|
n.add(
|
||||||
|
"GlobalConstraint",
|
||||||
|
"biomass limit",
|
||||||
|
carrier_attribute="solid biomass",
|
||||||
|
sense="<=",
|
||||||
|
constant=biomass_potentials["solid biomass"].sum(),
|
||||||
|
type="operational_limit",
|
||||||
|
)
|
||||||
|
|
||||||
# AC buses with district heating
|
# AC buses with district heating
|
||||||
urban_central = n.buses.index[n.buses.carrier == "urban central heat"]
|
urban_central = n.buses.index[n.buses.carrier == "urban central heat"]
|
||||||
@ -3628,7 +3673,7 @@ if __name__ == "__main__":
|
|||||||
for carrier in conventional:
|
for carrier in conventional:
|
||||||
add_carrier_buses(n, carrier)
|
add_carrier_buses(n, carrier)
|
||||||
|
|
||||||
add_co2_tracking(n, options)
|
add_co2_tracking(n, costs, options)
|
||||||
|
|
||||||
add_generation(n, costs)
|
add_generation(n, costs)
|
||||||
|
|
||||||
|
@ -182,9 +182,6 @@ def add_co2_sequestration_limit(n, config, limit=200):
|
|||||||
"""
|
"""
|
||||||
Add a global constraint on the amount of Mt CO2 that can be sequestered.
|
Add a global constraint on the amount of Mt CO2 that can be sequestered.
|
||||||
"""
|
"""
|
||||||
n.carriers.loc["co2 stored", "co2_absorptions"] = -1
|
|
||||||
n.carriers.co2_absorptions = n.carriers.co2_absorptions.fillna(0)
|
|
||||||
|
|
||||||
limit = limit * 1e6
|
limit = limit * 1e6
|
||||||
for o in opts:
|
for o in opts:
|
||||||
if "seq" not in o:
|
if "seq" not in o:
|
||||||
@ -202,10 +199,10 @@ def add_co2_sequestration_limit(n, config, limit=200):
|
|||||||
n.madd(
|
n.madd(
|
||||||
"GlobalConstraint",
|
"GlobalConstraint",
|
||||||
names,
|
names,
|
||||||
sense="<=",
|
sense=">=",
|
||||||
constant=limit,
|
constant=-limit,
|
||||||
type="primary_energy",
|
type="operational_limit",
|
||||||
carrier_attribute="co2_absorptions",
|
carrier_attribute="co2 sequestered",
|
||||||
investment_period=periods,
|
investment_period=periods,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -396,7 +393,7 @@ def prepare_network(
|
|||||||
if snakemake.params["sector"]["limit_max_growth"]["enable"]:
|
if snakemake.params["sector"]["limit_max_growth"]["enable"]:
|
||||||
n = add_max_growth(n, config)
|
n = add_max_growth(n, config)
|
||||||
|
|
||||||
if n.stores.carrier.eq("co2 stored").any():
|
if n.stores.carrier.eq("co2 sequestered").any():
|
||||||
limit = co2_sequestration_potential
|
limit = co2_sequestration_potential
|
||||||
add_co2_sequestration_limit(n, config, limit=limit)
|
add_co2_sequestration_limit(n, config, limit=limit)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user