option for losses on bidirectional links via link splitting
This commit is contained in:
parent
706942957b
commit
cc162a9e02
@ -478,6 +478,11 @@ sector:
|
|||||||
electricity_distribution_grid: true
|
electricity_distribution_grid: true
|
||||||
electricity_distribution_grid_cost_factor: 1.0
|
electricity_distribution_grid_cost_factor: 1.0
|
||||||
electricity_grid_connection: true
|
electricity_grid_connection: true
|
||||||
|
transmission_losses:
|
||||||
|
# per 1000 km
|
||||||
|
DC: 0
|
||||||
|
H2 pipeline: 0
|
||||||
|
gas pipeline: 0
|
||||||
H2_network: true
|
H2_network: true
|
||||||
gas_network: false
|
gas_network: false
|
||||||
H2_retrofit: false
|
H2_retrofit: false
|
||||||
|
@ -3280,6 +3280,34 @@ def set_temporal_aggregation(n, opts, solver_name):
|
|||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def lossy_bidirectional_links(n, carrier, losses_per_thousand_km=0.0):
|
||||||
|
"Split bidirectional links into two unidirectional links to include transmission losses."
|
||||||
|
|
||||||
|
carrier_i = n.links.query("carrier == @carrier").index
|
||||||
|
|
||||||
|
if not losses_per_thousand_km or carrier_i.empty:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Specified losses for {carrier} transmission. Splitting bidirectional links."
|
||||||
|
)
|
||||||
|
|
||||||
|
carrier_i = n.links.query("carrier == @carrier").index
|
||||||
|
n.links.loc[carrier_i, "p_min_pu"] = 0
|
||||||
|
n.links["reversed"] = False
|
||||||
|
n.links.loc[carrier_i, "efficiency"] = (
|
||||||
|
1 - n.links.loc[carrier_i, "length"] * losses_per_thousand_km / 1e3
|
||||||
|
)
|
||||||
|
rev_links = (
|
||||||
|
n.links.loc[carrier_i].copy().rename({"bus0": "bus1", "bus1": "bus0"}, axis=1)
|
||||||
|
)
|
||||||
|
rev_links.capital_cost = 0
|
||||||
|
rev_links.reversed = True
|
||||||
|
rev_links.index = rev_links.index.map(lambda x: x + "-reversed")
|
||||||
|
|
||||||
|
n.links = pd.concat([n.links, rev_links], sort=False)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if "snakemake" not in globals():
|
if "snakemake" not in globals():
|
||||||
from _helpers import mock_snakemake
|
from _helpers import mock_snakemake
|
||||||
@ -3446,6 +3474,18 @@ if __name__ == "__main__":
|
|||||||
if options["electricity_grid_connection"]:
|
if options["electricity_grid_connection"]:
|
||||||
add_electricity_grid_connection(n, costs)
|
add_electricity_grid_connection(n, costs)
|
||||||
|
|
||||||
|
for k, v in options["transmission_losses"].items():
|
||||||
|
lossy_bidirectional_links(n, k, v)
|
||||||
|
|
||||||
|
# Workaround: Remove lines with conflicting (and unrealistic) properties
|
||||||
|
# cf. https://github.com/PyPSA/pypsa-eur/issues/444
|
||||||
|
if snakemake.config["solving"]["options"]["transmission_losses"]:
|
||||||
|
idx = n.lines.query("num_parallel == 0").index
|
||||||
|
logger.info(
|
||||||
|
f"Removing {len(idx)} line(s) with properties conflicting with transmission losses functionality."
|
||||||
|
)
|
||||||
|
n.mremove("Line", idx)
|
||||||
|
|
||||||
first_year_myopic = (snakemake.params.foresight == "myopic") and (
|
first_year_myopic = (snakemake.params.foresight == "myopic") and (
|
||||||
snakemake.params.planning_horizons[0] == investment_year
|
snakemake.params.planning_horizons[0] == investment_year
|
||||||
)
|
)
|
||||||
|
@ -494,6 +494,27 @@ def add_battery_constraints(n):
|
|||||||
n.model.add_constraints(lhs == 0, name="Link-charger_ratio")
|
n.model.add_constraints(lhs == 0, name="Link-charger_ratio")
|
||||||
|
|
||||||
|
|
||||||
|
def add_lossy_bidirectional_link_constraints(n):
|
||||||
|
if not n.links.p_nom_extendable.any() or not "reversed" in n.links.columns:
|
||||||
|
return
|
||||||
|
|
||||||
|
carriers = n.links.loc[n.links.reversed, "carrier"].unique()
|
||||||
|
|
||||||
|
backward_i = n.links.query(
|
||||||
|
"carrier in @carriers and reversed and p_nom_extendable"
|
||||||
|
).index
|
||||||
|
forward_i = n.links.query(
|
||||||
|
"carrier in @carriers and ~reversed and p_nom_extendable"
|
||||||
|
).index
|
||||||
|
|
||||||
|
assert len(forward_i) == len(backward_i)
|
||||||
|
|
||||||
|
lhs = n.model["Link-p_nom"].loc[backward_i]
|
||||||
|
rhs = n.model["Link-p_nom"].loc[forward_i]
|
||||||
|
|
||||||
|
n.model.add_constraints(lhs == rhs, name="Link-bidirectional_sync")
|
||||||
|
|
||||||
|
|
||||||
def add_chp_constraints(n):
|
def add_chp_constraints(n):
|
||||||
electric = (
|
electric = (
|
||||||
n.links.index.str.contains("urban central")
|
n.links.index.str.contains("urban central")
|
||||||
@ -593,6 +614,7 @@ def extra_functionality(n, snapshots):
|
|||||||
if "EQ" in o:
|
if "EQ" in o:
|
||||||
add_EQ_constraints(n, o)
|
add_EQ_constraints(n, o)
|
||||||
add_battery_constraints(n)
|
add_battery_constraints(n)
|
||||||
|
add_lossy_bidirectional_link_constraints(n)
|
||||||
add_pipe_retrofit_constraint(n)
|
add_pipe_retrofit_constraint(n)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user