Fix prepare_sector_network to reproduce old sector prenetworks
from pypsa-eur sector branch
This commit is contained in:
parent
ad865c18c6
commit
e88b6c850f
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# PyPSA-Eur-Sec: A Sector-Coupled Open Optimisation Model of the European Energy System
|
||||
|
||||
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.
|
||||
|
||||
PyPSA-Eur-Sec includes PyPSA-Eur as a
|
||||
[snakemake](https://snakemake.readthedocs.io/en/stable/index.html)
|
||||
[subworkflow](https://snakemake.readthedocs.io/en/stable/snakefiles/modularization.html#snakefiles-sub-workflows). PyPSA-Eur-Sec
|
||||
uses PyPSA-Eur to build the clustered transmission model along with
|
||||
wind, solar PV and hydroelectricity potentials and time series. Then
|
||||
PyPSA-Eur-Sec adds other conventional generators, storage units and
|
||||
the additional sectors.
|
||||
|
||||
Currently the scripts to solve and process the resulting PyPSA models
|
||||
are also included in PyPSA-Eur-Sec, although they could in future be
|
||||
better integrated with the corresponding scripts in PyPSA-Eur.
|
||||
|
||||
# Installation
|
||||
|
||||
First install [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur) and all
|
||||
its dependencies.
|
||||
|
||||
## Data requirements
|
||||
|
||||
JRC-IDEES, JRC biomass potentials, .... EEA emissions.
|
18
Snakefile
18
Snakefile
@ -6,7 +6,8 @@ wildcard_constraints:
|
||||
simpl="[a-zA-Z0-9]*",
|
||||
clusters="[0-9]+m?",
|
||||
sectors="[+a-zA-Z0-9]+",
|
||||
opts="[-+a-zA-Z0-9]*"
|
||||
opts="[-+a-zA-Z0-9]*",
|
||||
sector_opts="[-+a-zA-Z0-9]*"
|
||||
|
||||
|
||||
|
||||
@ -23,7 +24,7 @@ rule test_script:
|
||||
|
||||
rule prepare_sector_networks:
|
||||
input:
|
||||
expand(config['results_dir'] + config['run'] + "/prenetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}.nc",
|
||||
expand(config['results_dir'] + config['run'] + "/prenetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}.nc",
|
||||
**config['scenario'])
|
||||
|
||||
|
||||
@ -141,13 +142,16 @@ rule build_industrial_demand:
|
||||
|
||||
|
||||
|
||||
rule prepare_network:
|
||||
rule prepare_sector_network:
|
||||
input:
|
||||
network=config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}.nc',
|
||||
network=pypsaeur('networks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc'),
|
||||
energy_totals_name='data/energy_totals.csv',
|
||||
co2_totals_name='data/co2_totals.csv',
|
||||
transport_name='data/transport_data.csv',
|
||||
biomass_potentials='data/biomass_potentials.csv',
|
||||
timezone_mappings='data/timezone_mappings.csv',
|
||||
heat_profile="data/heat_load_profile_DK_AdamJensen.csv",
|
||||
costs="data/costs.csv",
|
||||
clustered_pop_layout="resources/pop_layout_{network}_s{simpl}_{clusters}.csv",
|
||||
industrial_demand="resources/industrial_demand_{network}_s{simpl}_{clusters}.csv",
|
||||
heat_demand_urban="resources/heat_demand_urban_{network}_s{simpl}_{clusters}.nc",
|
||||
@ -168,8 +172,8 @@ rule prepare_network:
|
||||
solar_thermal_total="resources/solar_thermal_total_{network}_s{simpl}_{clusters}.nc",
|
||||
solar_thermal_urban="resources/solar_thermal_urban_{network}_s{simpl}_{clusters}.nc",
|
||||
solar_thermal_rural="resources/solar_thermal_rural_{network}_s{simpl}_{clusters}.nc"
|
||||
output: config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc'
|
||||
output: config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}.nc'
|
||||
threads: 1
|
||||
resources: mem=1000
|
||||
benchmark: "benchmarks/prepare_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}"
|
||||
script: "scripts/prepare_network.py"
|
||||
benchmark: "benchmarks/prepare_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}"
|
||||
script: "scripts/prepare_sector_network.py"
|
||||
|
69
config.yaml
69
config.yaml
@ -1,3 +1,4 @@
|
||||
logging_level: INFO
|
||||
|
||||
results_dir: 'results/'
|
||||
summary_dir: results
|
||||
@ -6,9 +7,10 @@ run: '190416-modular-test'
|
||||
scenario:
|
||||
sectors: [E] # ,E+EV,E+BEV,E+BEV+V2G] # [ E+EV, E+BEV, E+BEV+V2G ]
|
||||
simpl: ['']
|
||||
lv: [1.0]#[1.0, 1.125, 1.25, 1.5, 2.0, opt]# or opt
|
||||
lv: [1.0,1.25]#[1.0, 1.125, 1.25, 1.5, 2.0, opt]# or opt
|
||||
clusters: [128] #[90, 128, 181] #[45, 64, 90, 128, 181, 256] #, 362] # (2**np.r_[5.5:9:.5]).astype(int) minimum is 37
|
||||
opts: [Co2L0-3H-T-H-B-I,Co2L0-3H-T-H-B-I-onwind0,Co2L0-3H-T-H-B-I-onwind0p1]#,Co2L0p05-3H-T-H-B-I,Co2L0p10-3H-T-H-B-I,Co2L0p20-3H-T-H-B-I,Co2L0p30-3H-T-H-B-I,Co2L0p50-3H-T-H-B-I]#[Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0-3H-T-H,Co2L0p20-3H-T-H] #Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p20-3H-T-HCo2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p30-3H-T-H,Co2L0p50-3H-T-H] #Co2L-3H,Co2L-3H-T,, LC-FL, LC-T, Ep-T, Co2L-T]
|
||||
opts: [''] #for pypsa-eur
|
||||
sector_opts: [Co2L0-3H-T-H-B-I,Co2L0-3H-T-H-B-I-onwind0,Co2L0-3H-T-H-B-I-onwind0p1,Co2L0p1-3H-T-H-B-I,Co2L0p1-3H-T-H-B-I-onwind0,Co2L0p1-3H-T-H-B-I-onwind0p1]#,Co2L0p05-3H-T-H-B-I,Co2L0p10-3H-T-H-B-I,Co2L0p20-3H-T-H-B-I,Co2L0p30-3H-T-H-B-I,Co2L0p50-3H-T-H-B-I]#[Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0-3H-T-H,Co2L0p20-3H-T-H] #Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p20-3H-T-HCo2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p30-3H-T-H,Co2L0p50-3H-T-H] #Co2L-3H,Co2L-3H-T,, LC-FL, LC-T, Ep-T, Co2L-T]
|
||||
# Co2L will give default (5%); Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions
|
||||
|
||||
|
||||
@ -90,3 +92,66 @@ renewable:
|
||||
biomass:
|
||||
year: 2030
|
||||
scenario: "Med"
|
||||
|
||||
|
||||
sector:
|
||||
'central' : True
|
||||
'central_fraction' : 0.6
|
||||
'dsm_restriction_value' : 0.75 #Set to 0 for no restriction on BEV DSM
|
||||
'dsm_restriction_time' : 7 #Time at which SOC of BEV has to be dsm_restriction_value
|
||||
'transport_heating_deadband_upper' : 20.
|
||||
'transport_heating_deadband_lower' : 15.
|
||||
'ICE_lower_degree_factor' : 0.375 #in per cent increase in fuel consumption per degree above deadband
|
||||
'ICE_upper_degree_factor' : 1.6
|
||||
'EV_lower_degree_factor' : 0.98
|
||||
'EV_upper_degree_factor' : 0.63
|
||||
'district_heating_loss' : 0.1
|
||||
'bev' : True #turns on EV battery
|
||||
'bev_availability' : 0.5 #How many cars do smart charging
|
||||
'v2g' : True #allows feed-in to grid from EV battery
|
||||
'transport_fuel_cell_share' : 0. #0 means all EVs, 1 means all FCs
|
||||
'time_dep_hp_cop' : True
|
||||
'retrofitting' : False
|
||||
'retroI-fraction' : 0.25
|
||||
'retroI-fraction' : 0.55
|
||||
'retrofitting-cost_factor' : 1.0
|
||||
'tes' : True
|
||||
'tes_tau' : 3.
|
||||
'boilers' : True
|
||||
'chp' : True
|
||||
'chp_parameters':
|
||||
'eta_elec' : 0.468 #electrical efficiency with no heat output
|
||||
'c_v' : 0.15 #loss of fuel for each addition of heat
|
||||
'c_m' : 0.75 #backpressure ratio
|
||||
'p_nom_ratio' : 1. #ratio of max heat output to max electrical output
|
||||
'solar_thermal' : True
|
||||
'solar_cf_correction': 0.788457 # = >>> 1/1.2683
|
||||
'marginal_cost_storage' : 0. #1e-4
|
||||
'methanation' : True
|
||||
'helmeth' : True
|
||||
'dac' : True
|
||||
'ccs_fraction' : 0.9
|
||||
|
||||
|
||||
|
||||
costs:
|
||||
year: 2030
|
||||
|
||||
# From a Lion Hirth paper, also reflects average of Noothout et al 2016
|
||||
discountrate: 0.07
|
||||
# [EUR/USD] ECB: https://www.ecb.europa.eu/stats/exchange/eurofxref/html/eurofxref-graph-usd.en.html # noqa: E501
|
||||
USD2013_to_EUR2013: 0.7532
|
||||
|
||||
# Marginal and capital costs can be overwritten
|
||||
# capital_cost:
|
||||
# Wind: Bla
|
||||
marginal_cost: #
|
||||
solar: 0.01
|
||||
onwind: 0.015
|
||||
offwind: 0.015
|
||||
hydro: 0.
|
||||
H2: 0.
|
||||
battery: 0.
|
||||
|
||||
emission_prices: # only used with the option Ep (emission prices)
|
||||
co2: 0.
|
||||
|
@ -10,17 +10,16 @@ import scipy as sp
|
||||
import xarray as xr
|
||||
import re, os
|
||||
|
||||
from six import iteritems
|
||||
import geopandas as gpd
|
||||
from six import iteritems, string_types
|
||||
|
||||
import pypsa
|
||||
|
||||
import yaml
|
||||
|
||||
import pytz
|
||||
|
||||
from vresutils.costdata import annuity
|
||||
|
||||
from add_electricity import load_costs, update_transmission_costs
|
||||
|
||||
|
||||
#First tell PyPSA that links can have multiple outputs by
|
||||
#overriding the component_attrs. This can be done for
|
||||
@ -59,7 +58,8 @@ def add_co2_tracking(n):
|
||||
bus="co2 atmosphere")
|
||||
|
||||
#this tracks CO2 stored, e.g. underground
|
||||
n.add("Bus","co2 stored")
|
||||
n.add("Bus","co2 stored",
|
||||
carrier="co2 stored")
|
||||
|
||||
#NB: can also be negative
|
||||
#cost of 10 euro/tCO2 for whatever stays
|
||||
@ -170,6 +170,16 @@ def average_every_nhours(n, offset):
|
||||
logger.info('Resampling the network to {}'.format(offset))
|
||||
m = n.copy(with_time=False)
|
||||
|
||||
#fix copying of network attributes
|
||||
#copied from pypsa/io.py, should be in pypsa/components.py#Network.copy()
|
||||
allowed_types = (float,int,bool) + string_types + tuple(np.typeDict.values())
|
||||
attrs = dict((attr, getattr(n, attr))
|
||||
for attr in dir(n)
|
||||
if (not attr.startswith("__") and
|
||||
isinstance(getattr(n,attr), allowed_types)))
|
||||
for k,v in iteritems(attrs):
|
||||
setattr(m,k,v)
|
||||
|
||||
snapshot_weightings = n.snapshot_weightings.resample(offset).sum()
|
||||
m.set_snapshots(snapshot_weightings.index)
|
||||
m.snapshot_weightings = snapshot_weightings
|
||||
@ -188,9 +198,6 @@ def average_every_nhours(n, offset):
|
||||
return m
|
||||
|
||||
|
||||
|
||||
timezone_mappings = pd.read_csv("data/timezone_mappings.csv",index_col=0,squeeze=True,header=None)
|
||||
|
||||
def generate_periodic_profiles(dt_index=pd.date_range("2011-01-01 00:00","2011-12-31 23:00",freq="H",tz="UTC"),
|
||||
nodes=[],
|
||||
weekly_profile=range(24*7)):
|
||||
@ -256,7 +263,7 @@ def prepare_data(network):
|
||||
heat_demand_df = xr.open_dataarray(snakemake.input.heat_demand_total).T.to_pandas().reindex(index=network.snapshots, method="ffill")
|
||||
|
||||
|
||||
intraday_profiles = pd.read_csv("data/heating/heat_load_profile_DK_AdamJensen.csv",index_col=0)
|
||||
intraday_profiles = pd.read_csv(snakemake.input.heat_profile,index_col=0)
|
||||
|
||||
intraday_year_profiles = generate_periodic_profiles(heat_demand_df.index.tz_localize("UTC"),
|
||||
nodes=heat_demand_df.columns,
|
||||
@ -388,7 +395,7 @@ def prepare_data(network):
|
||||
def prepare_costs():
|
||||
|
||||
#set all asset costs and other parameters
|
||||
costs = pd.read_csv("data/costs.csv",index_col=list(range(3))).sort_index()
|
||||
costs = pd.read_csv(snakemake.input.costs,index_col=list(range(3))).sort_index()
|
||||
|
||||
#correct units to MW and EUR
|
||||
costs.loc[costs.unit.str.contains("/kW"),"value"]*=1e3
|
||||
@ -1165,15 +1172,20 @@ if __name__ == "__main__":
|
||||
from vresutils.snakemake import MockSnakemake
|
||||
snakemake = MockSnakemake(
|
||||
wildcards=dict(network='elec', simpl='', clusters='37', lv='2', opts='Co2L-3H'),
|
||||
input=['networks/{network}_s{simpl}_{clusters}.nc'],
|
||||
input=dict(network='../pypsa-eur/networks/{network}_s{simpl}_{clusters}.nc', timezone_mappings='data/timezone_mappings.csv'),
|
||||
output=['networks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc']
|
||||
)
|
||||
with open('config.yaml') as f:
|
||||
snakemake.config = yaml.load(f)
|
||||
|
||||
|
||||
logging.basicConfig(level=snakemake.config['logging_level'])
|
||||
|
||||
timezone_mappings = pd.read_csv(snakemake.input.timezone_mappings,index_col=0,squeeze=True,header=None)
|
||||
|
||||
options = snakemake.config["sector"]
|
||||
|
||||
opts = snakemake.wildcards.opts.split('-')
|
||||
opts = snakemake.wildcards.sector_opts.split('-')
|
||||
|
||||
n = pypsa.Network(snakemake.input.network,
|
||||
override_component_attrs=override_component_attrs)
|
||||
@ -1209,8 +1221,6 @@ if __name__ == "__main__":
|
||||
if "I" in opts:
|
||||
add_industry(n)
|
||||
|
||||
set_line_s_max_pu(n)
|
||||
|
||||
for o in opts:
|
||||
m = re.match(r'^\d+h$', o, re.IGNORECASE)
|
||||
if m is not None:
|
||||
@ -1239,7 +1249,4 @@ if __name__ == "__main__":
|
||||
limit = float(limit.replace("p",".").replace("m","-"))
|
||||
restrict_technology_potential(n,"onwind",limit)
|
||||
|
||||
|
||||
set_line_volume_limit(n, snakemake.wildcards.lv)
|
||||
|
||||
n.export_to_netcdf(snakemake.output[0])
|
||||
|
Loading…
Reference in New Issue
Block a user