Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ electricity:
by_country: false

transmission_limit: vopt
transmission_limit_myopic:
2025: v1.05
2030: v1.05
2035: v1.05
2040: v1.05
2045: v1.05
2050: v1.05
Comment on lines 156 to +163
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be useful! But I think the setting should be integrated in the existing setting:

transmission_limit: vopt

or

transmission_limit:
    2025: v1.05
    2030: v1.1
    2035: v1.15
    2040: v1.2
    2045: v1.25
    2050: v1.3

This follows how we do this elsewhere.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, I was thinking the same but ultimately the change you're suggesting would create somewhat redundant code. The base_year automatically uses the transmission_limit set in the config; just the default setting of vopt is somewhat not optimal. It would be better to have v1.05 or alike. If TYNDP expansion is active probably v1.0 would be best.

But this is generally true, if not optimizing in 5 year increments but 10 year increments, I'd also use v1.1 rather than v1.05. (Otherwise the model only has 5% increase limit for 10 years)... I guess there's no perfect way to do this?

Anyway if we want to overwrite it also for the base_year, the same fragment is needed in add_baseyear.py.

Pls let me know what you think - not only for this, but also maybe an idea how to handle the default setting depending on how the planning_horizon increments are set?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just checked again - main requirement would be either adding a planning_horizon to prepare_network, or adding another parameter elec: to the keys of transmission_limit.

Increasing the limit is not necessary as the new limit is now always calculated from s_nom_opt + p_nom_opt from the previous horizons, so keeping it at v1.05 is better. We could ofc calculate it always from p_nom, not p_nom_opt, then your suggestion makes sense (better to resolve the issue from my previous comment, worse in case the previous horizon didn't optimise transmission much , in which case there might be a bit of excessive build-out later which I guess is rather unlikely)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but also maybe an idea how to handle the default setting depending on how the planning_horizon increments are set?

I think this should be left to the user. I would not automatically infer this.

just the default setting of vopt is somewhat not optimal. It would be better to have v1.05 or alike.

I would stick with vopt as default because this is the current state. Users can just adjust.

just checked again - main requirement would be either adding a planning_horizon to prepare_network, or adding another parameter elec: to the keys of transmission_limit.

I think the solution will come with @FabianHofmann's #1838. So this PR should be merged afterwards and this is no concern anymore.

Increasing the limit is not necessary as the new limit is now always calculated from s_nom_opt + p_nom_opt from the previous horizons, so keeping it at v1.05 is better.

Ok, agreed. Then the limit is always relative to the volume in the previous horizon, not relative to the capacities in the base year. That is fine. The indexing by planning horizon makes sense nevertheless as build-out could be configured to increase.

transmission_limit:
    2025: v1.05
    2030: v1.05
    2035: v1.05
    2040: v1.1
    2045: v1.1
    2050: v1.1

Does that sound good?


# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#atlite
atlite:
Expand Down
1 change: 1 addition & 0 deletions doc/configtables/electricity.csv
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ autarky,,,
-- enable,bool,true or false,Require each node to be autarkic by removing all lines and links.
-- by_country,bool,true or false,"Require each country to be autarkic by removing all cross-border lines and links. ``electricity: autarky`` must be enabled."
transmission_limit,str,"Values like 'vopt', 'v1.25', 'copt', 'c1.25'","Limit on transmission expansion. The first part can be ``v`` (for setting a limit on line volume) or ``c`` (for setting a limit on line cost). The second part can be ``opt`` or a float bigger than one (e.g. 1.25). If ``opt`` is chosen line expansion is optimised according to its capital cost (where the choice ``v`` only considers overhead costs for HVDC transmission lines, while ``c`` uses more accurate costs distinguishing between overhead and underwater sections and including inverter pairs). The setting ``v1.25`` will limit the total volume of line expansion to 25% of currently installed capacities weighted by individual line lengths. The setting ``c1.25`` will allow to build a transmission network that costs no more than 25 % more than the current system."
transmission_limit_myopic,--,Dictionary with planning horizons as keys.,"Limit on transmission expansion. The first part can be ``v`` (for setting a limit on line volume) or ``c`` (for setting a limit on line cost). The second part can be ``opt`` or a float bigger than one (e.g. 1.25). If ``opt`` is chosen line expansion is optimised according to its capital cost (where the choice ``v`` only considers overhead costs for HVDC transmission lines, while ``c`` uses more accurate costs distinguishing between overhead and underwater sections and including inverter pairs). The setting ``v1.25`` will limit the total volume of line expansion to 25% of currently installed capacities weighted by individual line lengths. The setting ``c1.25`` will allow to build a transmission network that costs no more than 25 % more than the current system."
3 changes: 3 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Release Notes
Upcoming Release
================

* The myopic optimisation workflow is now able to incremantally optimise the transmission limit in
every planning horizon. The added expansion limit can be configured in the configuration file using `transmission_limit_myopic`.

* Add CO2 emission prices configurable per planning horizon for sector-coupled models.
The CO2 price is added as a marginal cost on the `co2 atmosphere` Store.

Expand Down
1 change: 1 addition & 0 deletions rules/solve_myopic.smk
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ rule add_brownfield:
dynamic_ptes_capacity=config_provider(
"sector", "district_heating", "ptes", "dynamic_capacity"
),
transmission_limit=config_provider("electricity", "transmission_limit_myopic"),
input:
unpack(input_profile_tech_brownfield),
simplify_busmap=resources("busmap_base_s.csv"),
Expand Down
11 changes: 10 additions & 1 deletion scripts/add_brownfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
from scripts._helpers import (
configure_logging,
get_snapshots,
load_costs,
sanitize_custom_columns,
set_scenario_config,
update_config_from_wildcards,
)
from scripts.add_electricity import flatten, sanitize_carriers
from scripts.add_existing_baseyear import add_build_year_to_new_assets
from scripts.prepare_network import set_transmission_limit

logger = logging.getLogger(__name__)
idx = pd.IndexSlice
Expand Down Expand Up @@ -370,7 +372,14 @@ def update_dynamic_ptes_capacity(
capacity_threshold=snakemake.params.threshold_capacity,
)

disable_grid_expansion_if_limit_hit(n)
planning_horizon = int(snakemake.wildcards.planning_horizons)

kind = snakemake.params.transmission_limit[planning_horizon][0]
factor = snakemake.params.transmission_limit[planning_horizon][1:]
set_transmission_limit(n, kind, factor, load_costs(snakemake.input.costs))

if float(factor) == 1.0:
disable_grid_expansion_if_limit_hit(n)

n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))

Expand Down
33 changes: 28 additions & 5 deletions scripts/prepare_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,34 +150,57 @@ def set_line_s_max_pu(n, s_max_pu=0.7):
logger.info(f"N-1 security margin of lines set to {s_max_pu}")


def set_transmission_limit(n, kind, factor, costs, Nyears=1):
def set_transmission_limit(n, kind, factor, costs):
links_dc_b = n.links.carrier == "DC" if not n.links.empty else pd.Series()
if "reversed" in n.links.columns:
links_dc_b = (
(n.links.carrier == "DC") & ~n.links.reversed
if not n.links.empty
else pd.Series()
)

_lines_s_nom = (
np.sqrt(3)
* n.lines.type.map(n.line_types.i_nom)
* n.lines.num_parallel
* n.lines.bus0.map(n.buses.v_nom)
)
lines_s_nom = n.lines.s_nom.where(n.lines.type == "", _lines_s_nom)
if (n.links.loc[links_dc_b].p_nom_min > n.links.loc[links_dc_b].p_nom).any():
attr_link = "p_nom_min"
else:
attr_link = "p_nom"

if (n.lines.type == "").any():
lines_s_nom = (
n.lines[["s_nom", "s_nom_min", "s_nom_opt"]]
.max(axis=1)
.where(n.lines.type == "", _lines_s_nom)
)
else:
lines_s_nom = n.lines[["s_nom", "s_nom_min", "s_nom_opt"]].max(axis=1)

col = "capital_cost" if kind == "c" else "length"
ref = (
lines_s_nom @ n.lines[col]
+ n.links.loc[links_dc_b, "p_nom"] @ n.links.loc[links_dc_b, col]
+ n.links.loc[links_dc_b, attr_link] @ n.links.loc[links_dc_b, col]
)

set_transmission_costs(n, costs)

if factor == "opt" or float(factor) > 1.0:
n.lines["s_nom_min"] = lines_s_nom
n.lines["s_nom"] = lines_s_nom
n.lines["s_nom_extendable"] = True

n.links.loc[links_dc_b, "p_nom_min"] = n.links.loc[links_dc_b, "p_nom"]
n.links.loc[links_dc_b, ["p_nom_min", "p_nom"]] = n.links.loc[
links_dc_b, attr_link
]
n.links.loc[links_dc_b, "p_nom_extendable"] = True

if factor != "opt":
con_type = "expansion_cost" if kind == "c" else "volume_expansion"
if f"l{kind}_limit" in n.global_constraints.index:
n.remove("GlobalConstraint", f"l{kind}_limit")
rhs = float(factor) * ref
n.add(
"GlobalConstraint",
Expand Down Expand Up @@ -350,7 +373,7 @@ def set_line_nom_max(

kind = snakemake.params.transmission_limit[0]
factor = snakemake.params.transmission_limit[1:]
set_transmission_limit(n, kind, factor, costs, Nyears)
set_transmission_limit(n, kind, factor, costs)

set_line_nom_max(
n,
Expand Down