Split offshore wind turbines into AC and DC connections
This commit is contained in:
parent
bddea86b1e
commit
db073d8daa
16
config.yaml
16
config.yaml
@ -72,7 +72,7 @@ renewable:
|
||||
distance_grid_codes: [1, 2, 3, 4, 5, 6]
|
||||
natura: true
|
||||
potential: conservative # or heuristic
|
||||
offwind:
|
||||
offwind-ac:
|
||||
cutout: europe-2013-era5
|
||||
resource:
|
||||
method: wind
|
||||
@ -83,6 +83,20 @@ renewable:
|
||||
corine: [44, 255]
|
||||
natura: true
|
||||
max_depth: 50
|
||||
max_shore_distance: 80000
|
||||
potential: conservative # or heuristic
|
||||
offwind-dc:
|
||||
cutout: europe-2013-era5
|
||||
resource:
|
||||
method: wind
|
||||
turbine: NREL_ReferenceTurbine_5MW_offshore
|
||||
# ScholzPhd Tab 4.3.1: 10MW/km^2
|
||||
capacity_per_sqkm: 3
|
||||
# correction_factor: 0.93
|
||||
corine: [44, 255]
|
||||
natura: true
|
||||
max_depth: 50
|
||||
min_shore_distance: 80000
|
||||
potential: conservative # or heuristic
|
||||
solar:
|
||||
cutout: europe-2013-sarah
|
||||
|
@ -16,10 +16,15 @@ lignite,2030,lifetime,40,years,IEA2010
|
||||
geothermal,2030,lifetime,40,years,IEA2010
|
||||
biomass,2030,lifetime,30,years,ECF2010 in DIW DataDoc http://hdl.handle.net/10419/80348
|
||||
oil,2030,lifetime,30,years,ECF2010 in DIW DataDoc http://hdl.handle.net/10419/80348
|
||||
onwind,2030,investment,910,EUR/kWel,DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
onwind,2030,investment,1110,EUR/kWel,DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
onwind-landcosts,2030,investment,200,EUR/kWel,Land costs and compensation payments conservatively estimated based on DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
offwind,2030,investment,1640,EUR/kWel,DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
offwind-grid,2030,investment,255,EUR/kWel,Haertel 2017; assuming one onshore and one offshore node
|
||||
offwind-grid-perlength,2030,investment,0.97,EUR/kWel/km,Haertel 2017
|
||||
offwind-ac-station,2030,investment,250,EUR/kWel,DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
offwind-ac-connection-submarine,2030,investment,2685,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
offwind-ac-connection-underground,2030,investment,1342,EUR/MW/km,DEA https://ens.dk/en/our-services/projections-and-models/technology-data
|
||||
offwind-dc-station,2030,investment,400,EUR/kWel,Haertel 2017; assuming one onshore and one offshore node + 13% learning reduction
|
||||
offwind-dc-connection-submarine,2030,investment,2000,EUR/MW/km,DTU report based on Fig 34 of https://ec.europa.eu/energy/sites/ener/files/documents/2014_nsog_report.pdf
|
||||
offwind-dc-connection-underground,2030,investment,1000,EUR/MW/km,Haertel 2017; average + 13% learning reduction
|
||||
solar,2030,investment,600,EUR/kWel,DIW DataDoc http://hdl.handle.net/10419/80348
|
||||
biomass,2030,investment,2209,EUR/kWel,DIW DataDoc http://hdl.handle.net/10419/80348
|
||||
geothermal,2030,investment,3392,EUR/kWel,DIW DataDoc http://hdl.handle.net/10419/80348
|
||||
@ -177,7 +182,7 @@ HVAC overhead,2030,FOM,2,%/year,Hagspiel
|
||||
HVDC overhead,2030,investment,400,EUR/MW/km,Hagspiel
|
||||
HVDC overhead,2030,lifetime,40,years,Hagspiel
|
||||
HVDC overhead,2030,FOM,2,%/year,Hagspiel
|
||||
HVDC submarine,2030,investment,2000,EUR/MW/km,Own analysis of European submarine HVDC projects since 2000
|
||||
HVDC submarine,2030,investment,2000,EUR/MW/km,DTU report based on Fig 34 of https://ec.europa.eu/energy/sites/ener/files/documents/2014_nsog_report.pdf
|
||||
HVDC submarine,2030,lifetime,40,years,Hagspiel
|
||||
HVDC submarine,2030,FOM,2,%/year,Hagspiel
|
||||
HVDC inverter pair,2030,investment,150000,EUR/MW,Hagspiel
|
||||
|
|
@ -161,15 +161,18 @@ def attach_wind_and_solar(n, costs):
|
||||
|
||||
n.add("Carrier", name=tech)
|
||||
with xr.open_dataset(getattr(snakemake.input, 'profile_' + tech)) as ds:
|
||||
capital_cost = costs.at[tech, 'capital_cost']
|
||||
if tech + "-grid" in costs.index:
|
||||
if tech + "-grid-perlength" in costs.index:
|
||||
grid_cost = costs.at[tech + "-grid", "capital_cost"] + costs.at[tech + "-grid-perlength", 'capital_cost'] * ds['average_distance'].to_pandas()
|
||||
logger.info("Added connection cost of {:0.0f}-{:0.0f} Eur/MW/a to {}".format(grid_cost.min(), grid_cost.max(), tech))
|
||||
else:
|
||||
grid_cost = costs.at[tech + "-grid", "capital_cost"]
|
||||
logger.info("Added connection cost of {:0.0f} Eur/MW/a to {}".format(grid_cost, tech))
|
||||
capital_cost = capital_cost + grid_cost
|
||||
suptech = tech.split('-', 2)[0]
|
||||
if suptech == 'offwind':
|
||||
underwater_fraction = ds['underwater_fraction'].to_pandas()
|
||||
connection_cost = (snakemake.config['lines']['length_factor'] * ds['average_distance'].to_pandas() *
|
||||
(underwater_fraction * costs.at[tech + '-connection-submarine', 'capital_cost'] +
|
||||
(1. - underwater_fraction) * costs.at[tech + '-connection-underground', 'capital_cost']))
|
||||
capital_cost = costs.at['offwind', 'capital_cost'] + costs.at[tech + '-station', 'capital_cost'] + connection_cost
|
||||
logger.info("Added connection cost of {:0.0f}-{:0.0f} Eur/MW/a to {}".format(connection_cost.min(), connection_cost.max(), tech))
|
||||
elif suptech == 'onwind':
|
||||
capital_cost = costs.at['onwind', 'capital_cost'] + costs.at['onwind-landcosts', 'capital_cost']
|
||||
else:
|
||||
capital_cost = costs.at[tech, 'capital_cost']
|
||||
|
||||
n.madd("Generator", ds.indexes['bus'], ' ' + tech,
|
||||
bus=ds.indexes['bus'],
|
||||
@ -177,9 +180,9 @@ def attach_wind_and_solar(n, costs):
|
||||
p_nom_extendable=True,
|
||||
p_nom_max=ds['p_nom_max'].to_pandas(),
|
||||
weight=ds['weight'].to_pandas(),
|
||||
marginal_cost=costs.at[tech, 'marginal_cost'],
|
||||
marginal_cost=costs.at[suptech, 'marginal_cost'],
|
||||
capital_cost=capital_cost,
|
||||
efficiency=costs.at[tech, 'efficiency'],
|
||||
efficiency=costs.at[suptech, 'efficiency'],
|
||||
p_max_pu=ds['profile'].transpose('time', 'bus').to_pandas())
|
||||
|
||||
|
||||
|
@ -63,22 +63,51 @@ def simplify_network_to_380(n):
|
||||
|
||||
return n, trafo_map
|
||||
|
||||
def _adjust_costs_using_distance(n, distance):
|
||||
def _prepare_connection_costs_per_link(n):
|
||||
costs = load_costs(n.snapshot_weightings.sum() / 8760, snakemake.input.tech_costs,
|
||||
snakemake.config['costs'], snakemake.config['electricity'])
|
||||
|
||||
connection_costs_per_link = {}
|
||||
|
||||
for tech in snakemake.config['renewable']:
|
||||
if tech + "-grid-perlength" in costs.index:
|
||||
cost_perlength = costs.at[tech + "-grid-perlength", "capital_cost"]
|
||||
tech_b = n.generators.carrier == tech
|
||||
generator_distance = n.generators.loc[tech_b, "bus"].map(distance).loc[lambda s: s>0]
|
||||
if not generator_distance.empty:
|
||||
n.generators.loc[generator_distance.index, "capital_cost"] += cost_perlength * generator_distance
|
||||
logger.info("Displacing generator(s) {}; capital_cost is adjusted accordingly"
|
||||
.format(", ".join("`{}` by {:.0f}km".format(b, d) for b, d in generator_distance.iteritems())))
|
||||
if tech.startswith('offwind'):
|
||||
connection_costs_per_link[tech] = (
|
||||
n.links.length * snakemake.config['lines']['length_factor'] *
|
||||
(n.links.underwater_fraction * costs.at[tech + '-connection-submarine', 'capital_cost'] +
|
||||
(1. - n.links.underwater_fraction) * costs.at[tech + '-connection-underground', 'capital_cost'])
|
||||
)
|
||||
|
||||
return connection_costs_per_link
|
||||
|
||||
def _aggregate_and_move_components(n, busmap, distance, aggregate_one_ports={"Load", "StorageUnit"}):
|
||||
def _compute_connection_costs_to_bus(n, busmap, connection_costs_per_link=None, buses=None):
|
||||
if connection_costs_per_link is None:
|
||||
connection_costs_per_link = _prepare_connection_costs_per_link(n)
|
||||
|
||||
if buses is None:
|
||||
buses = busmap.index[busmap.index != busmap.values]
|
||||
|
||||
connection_costs_to_bus = pd.DataFrame(index=buses)
|
||||
|
||||
for tech in connection_costs_per_link:
|
||||
adj = n.adjacency_matrix(weights=pd.concat(dict(Link=connection_costs_per_link[tech].reindex(n.links.index),
|
||||
Line=pd.Series(0., n.lines.index))))
|
||||
|
||||
costs_between_buses = dijkstra(adj, directed=False, indices=n.buses.index.get_indexer(buses))
|
||||
connection_costs_to_bus[tech] = costs_between_buses[np.arange(len(buses)),
|
||||
n.buses.index.get_indexer(busmap.loc[buses])]
|
||||
|
||||
return connection_costs_to_bus
|
||||
|
||||
def _adjust_capital_costs_using_connection_costs(n, connection_costs_to_bus):
|
||||
for tech in connection_costs_to_bus:
|
||||
tech_b = n.generators.carrier == tech
|
||||
costs = n.generators.loc[tech_b, "bus"].map(connection_costs_to_bus[tech]).loc[lambda s: s>0]
|
||||
if not costs.empty:
|
||||
n.generators.loc[costs.index, "capital_cost"] += costs
|
||||
logger.info("Displacing {} generator(s) and adding connection costs {} to capital_costs"
|
||||
.format(tech, ", ".join("of {:.0f} Eur/MW to `{}`".format(d, b) for b, d in costs.iteritems())))
|
||||
|
||||
def _aggregate_and_move_components(n, busmap, connection_costs_to_bus, aggregate_one_ports={"Load", "StorageUnit"}):
|
||||
def replace_components(n, c, df, pnl):
|
||||
n.mremove(c, n.df(c).index)
|
||||
|
||||
@ -87,7 +116,7 @@ def _aggregate_and_move_components(n, busmap, distance, aggregate_one_ports={"Lo
|
||||
if not df.empty:
|
||||
import_series_from_dataframe(n, df, c, attr)
|
||||
|
||||
_adjust_costs_using_distance(n, distance)
|
||||
_adjust_capital_costs_using_connection_costs(n, connection_costs_to_bus)
|
||||
|
||||
generators, generators_pnl = aggregategenerators(n, busmap)
|
||||
replace_components(n, "Generator", generators, generators_pnl)
|
||||
@ -102,16 +131,6 @@ def _aggregate_and_move_components(n, busmap, distance, aggregate_one_ports={"Lo
|
||||
df = n.df(c)
|
||||
n.mremove(c, df.index[df.bus0.isin(buses_to_del) | df.bus1.isin(buses_to_del)])
|
||||
|
||||
def _compute_distance(n, busmap, buses=None, adjacency_matrix=None):
|
||||
if buses is None:
|
||||
buses = busmap.index[busmap.index != busmap.values]
|
||||
|
||||
if adjacency_matrix is None:
|
||||
adjacency_matrix = n.adjacency_matrix(weights=pd.concat(dict(Link=n.links.length, Line=pd.Series(0., n.lines.index))))
|
||||
|
||||
dist = dijkstra(adjacency_matrix, directed=False, indices=n.buses.index.get_indexer(buses))
|
||||
return pd.Series(dist[np.arange(len(buses)), n.buses.index.get_indexer(busmap.loc[buses])], buses)
|
||||
|
||||
def simplify_links(n):
|
||||
## Complex multi-node links are folded into end-points
|
||||
logger.info("Simplifying connected link components")
|
||||
@ -155,8 +174,9 @@ def simplify_links(n):
|
||||
seen.add(u)
|
||||
|
||||
busmap = n.buses.index.to_series()
|
||||
distance = pd.Series(0., n.buses.index)
|
||||
adjacency_matrix = n.adjacency_matrix(weights=pd.concat(dict(Link=n.links.length, Line=pd.Series(0., n.lines.index))))
|
||||
|
||||
connection_costs_per_link = _prepare_connection_costs_per_link(n)
|
||||
connection_costs_to_bus = pd.DataFrame(0., index=n.buses.index, columns=list(connection_costs_per_link))
|
||||
|
||||
for lbl in labels.value_counts().loc[lambda s: s > 2].index:
|
||||
|
||||
@ -169,7 +189,7 @@ def simplify_links(n):
|
||||
m = sp.spatial.distance_matrix(n.buses.loc[b, ['x', 'y']],
|
||||
n.buses.loc[buses[1:-1], ['x', 'y']])
|
||||
busmap.loc[buses] = b[np.r_[0, m.argmin(axis=0), 1]]
|
||||
distance.loc[buses] += _compute_distance(n, busmap, buses)
|
||||
connection_costs_to_bus.loc[buses] += _compute_connection_costs_to_bus(n, busmap, connection_costs_per_link, buses)
|
||||
|
||||
all_links = [i for _, i in sum(links, [])]
|
||||
|
||||
@ -200,7 +220,7 @@ def simplify_links(n):
|
||||
|
||||
logger.debug("Collecting all components using the busmap")
|
||||
|
||||
_aggregate_and_move_components(n, busmap, distance)
|
||||
_aggregate_and_move_components(n, busmap, connection_costs_to_bus)
|
||||
return n, busmap
|
||||
|
||||
def remove_stubs(n):
|
||||
@ -208,9 +228,9 @@ def remove_stubs(n):
|
||||
|
||||
busmap = busmap_by_stubs(n) # ['country'])
|
||||
|
||||
distance = _compute_distance(n, busmap)
|
||||
connection_costs_to_bus = _compute_connection_costs_to_bus(n, busmap)
|
||||
|
||||
_aggregate_and_move_components(n, busmap, distance)
|
||||
_aggregate_and_move_components(n, busmap, connection_costs_to_bus)
|
||||
|
||||
return n, busmap
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user