diff --git a/.gitignore b/.gitignore index 9371d303..e3f625c3 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ doc/_build *.xls *.geojson + +*.ipynb + +data/costs_* \ No newline at end of file diff --git a/Snakefile b/Snakefile index ef1ed840..b14adacf 100644 --- a/Snakefile +++ b/Snakefile @@ -288,7 +288,7 @@ else: build_biomass_transport_costs_output = {} -if config["sector"].get("sequestration_potential", False): +if config["sector"]["regional_co2_sequestration_potential"]["enable"]: rule build_sequestration_potentials: input: sequestration_potential=HTTP.remote("https://raw.githubusercontent.com/ericzhou571/Co2Storage/main/resources/complete_map_2020_unit_Mt.geojson", keep_local=True), diff --git a/config.default.yaml b/config.default.yaml index d09ff950..c1172bd1 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -250,8 +250,15 @@ sector: coal_cc: false dac: true co2_vent: false + allam_cycle: false SMR: true - sequestration_potential: true # geological co2 storage potential + regional_co2_sequestration_potential: + enable: false # enable regionally resolved geological co2 storage potential + attribute: 'conservative estimate Mt' + include_onshore: false # include onshore sequestration potentials + min_size: 3 # Gt, sites with lower potential will be excluded + max_size: 25 # Gt, max sequestration potential for any one site, TODO research suitable value + years_of_storage: 25 # years until potential exhausted at optimised annual rate co2_sequestration_potential: 200 #MtCO2/a sequestration potential for Europe co2_sequestration_cost: 10 #EUR/tCO2 for sequestration of CO2 co2_spatial: false @@ -281,8 +288,8 @@ sector: gas_network_connectivity_upgrade: 1 # https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation gas_distribution_grid: true gas_distribution_grid_cost_factor: 1.0 #multiplies cost in data/costs.csv - biomass_spatial: false # biomass transport between nodes - biomass_transport: false # biomass transport between nodes + biomass_spatial: false # regionally resolve biomass (e.g. potentials) + biomass_transport: false # allow transport of solid biomass between nodes conventional_generation: # generator : carrier OCGT: gas biomass_to_liquid: false diff --git a/scripts/build_sequestration_potentials.py b/scripts/build_sequestration_potentials.py index 3b06f665..4983640b 100644 --- a/scripts/build_sequestration_potentials.py +++ b/scripts/build_sequestration_potentials.py @@ -27,22 +27,17 @@ if __name__ == "__main__": clusters="181" ) - # TODO move to config.yaml - threshold = 3 - include_onshore = False + cf = snakemake.config["sector"]["regional_co2_sequestration_potential"] gdf = gpd.read_file(snakemake.input.sequestration_potential[0]) regions = gpd.read_file(snakemake.input.regions_offshore) - if include_onshore: + if cf["include_onshore"]: onregions = gpd.read_file(snakemake.input.regions_onshore) regions = pd.concat([regions, onregions]).dissolve(by='name').reset_index() - attr = snakemake.config['sector']["sequestration_potential"] - kwargs = dict(attr=attr, threshold=threshold) if isinstance(attr, str) else {} + s = allocate_sequestration_potential(gdf, regions, attr=cf["attribute"], threshold=cf["min_size"]) - s = allocate_sequestration_potential(gdf, regions, **kwargs) - - s = s.where(s>threshold).dropna() + s = s.where(s>cf["min_size"]).dropna() s.to_csv(snakemake.output.sequestration_potential) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 5348a79e..a47c5710 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -517,10 +517,9 @@ def add_co2_tracking(n, options): unit="t_co2" ) - if options["sequestration_potential"]: - # TODO make configurable options - upper_limit = 25000 # Mt - annualiser = 25 # TODO research suitable value + if options["regional_co2_sequestration_potential"]["enable"]: + upper_limit = options["regional_co2_sequestration_potential"]["max_size"] * 1e3 # Mt + annualiser = options["regional_co2_sequestration_potential"]["years_of_storage"] e_nom_max = pd.read_csv(snakemake.input.sequestration_potential, index_col=0).squeeze() e_nom_max = e_nom_max.reindex(spatial.co2.locations).fillna(0.).clip(upper=upper_limit).mul(1e6) / annualiser # t e_nom_max = e_nom_max.rename(index=lambda x: x + " co2 stored") @@ -570,24 +569,25 @@ def add_co2_network(n, costs): def add_allam(n, costs): - logger.info("Adding Allam cycle generators") + logger.info("Adding Allam cycle gas power plants.") nodes = pop_layout.index n.madd("Link", - nodes + " allam", - bus0=spatial.gas.df.loc[nodes, "nodes"].values, - bus1=nodes, - bus2=spatial.co2.df.loc[nodes, "nodes"].values, - carrier="allam", - p_nom_extendable=True, - # TODO: add costs to technology-data - capital_cost=0.6*1.5e6*0.1, # efficiency * EUR/MW * annuity - marginal_cost=2, - efficiency=0.6, - efficiency2=costs.at['gas', 'CO2 intensity'], - lifetime=30., - ) + nodes, + suffix=" allam", + bus0=spatial.gas.df.loc[nodes, "nodes"].values, + bus1=nodes, + bus2=spatial.co2.df.loc[nodes, "nodes"].values, + carrier="allam", + p_nom_extendable=True, + # TODO: add costs to technology-data + capital_cost=0.6*1.5e6*0.1, # efficiency * EUR/MW * annuity + marginal_cost=2, + efficiency=0.6, + efficiency2=costs.at['gas', 'CO2 intensity'], + lifetime=30., + ) def add_dac(n, costs): @@ -2923,6 +2923,8 @@ if __name__ == "__main__": if options["co2network"]: add_co2_network(n, costs) + + if options["allam_cycle"]: add_allam(n, costs) solver_name = snakemake.config["solving"]["solver"]["name"]