Fix prepare_sector_network to reproduce old sector prenetworks

from pypsa-eur sector branch
This commit is contained in:
Tom Brown 2019-04-17 17:04:33 +02:00
parent ad865c18c6
commit e88b6c850f
4 changed files with 131 additions and 26 deletions

29
README.md Normal file
View 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.

View File

@ -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"

View File

@ -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.

View File

@ -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])