From 7fbdb3655b0904f8c5b98b306b1ef0d1235e99e9 Mon Sep 17 00:00:00 2001 From: lisa Date: Tue, 14 Jul 2026 18:32:19 +0200 Subject: [PATCH 1/8] feat: add function to merge low voltage to market node --- scripts/cba/simplify_sb_network.py | 75 ++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index 390acd80d7..910ca07082 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -8,6 +8,7 @@ - Fixed optimal capacities from scenario building - Hurdle costs on DC links - Extended primary fuel source capacities +- Merge low and high voltage buses **Inputs** @@ -62,6 +63,77 @@ def extend_primary_fuel_sources( n.generators.loc[gen_i, "p_nom"] = inf +def move_bus_carrier_and_cleanup( + n, + from_carrier="low voltage", + busmap=None, +): + """ + Reassign all components attached to buses of `from_carrier` onto their + corresponding buses (as given by `busmap`), remove the now-empty + `from_carrier` buses, and drop any lines/links that end up with + bus0 == bus1 as a result. + + Also drops redundant load-shedding generators sitting on `from_carrier` + buses, since after reassignment they would duplicate the load-shedding + generator already present at the target bus. + + Parameters + ---------- + n : pypsa.Network + Network to modify in place. + from_carrier : str, default "low voltage" + Bus carrier to remove (e.g. "low voltage"). + busmap : pandas.Series, optional + Mapping of `from_carrier` bus name -> target bus name onto which its + components should be reassigned. Defaults to + `n.buses.loc[n.buses.carrier == from_carrier, "location"]`, i.e. + each bus is mapped onto the bus matching its `location` field. + + Returns + ------- + n : pypsa.Network + The modified network (same object, mutated in place). + """ + if busmap is None: + busmap = n.buses.loc[n.buses.carrier == from_carrier, "location"] + + # remove load shedding generators on low voltage buses to avoid having two + # load shedding generators at market node + load_shedding_gens = n.generators.index[ + (n.generators.bus.map(n.buses.carrier) == from_carrier) + & (n.generators.carrier == "load") + ] + n.remove("Generator", load_shedding_gens) + + # reassign every component attached to the from_carrier buses + components = sorted(n.branch_components | n.one_port_components) + for cname in components: + c = n.c[cname] + static = c.static + if static.empty: + continue + for port in c.ports: + col = f"bus{port}" + if col not in static: + continue + mask = static[col].isin(busmap.index) + if mask.any(): + static.loc[mask, col] = static.loc[mask, col].map(busmap) + + # remove carrier buses + n.remove("Bus", busmap.index) + + # drop lines/links where bus0 == bus1 after remapping + for cname in ["Line", "Link"]: + static = n.c[cname].static + if static.empty: + continue + to_remove = static.index[static.bus0 == static.bus1] + if len(to_remove): + n.remove(cname, to_remove) + + if __name__ == "__main__": if "snakemake" not in globals(): from scripts._helpers import mock_snakemake @@ -96,6 +168,9 @@ def extend_primary_fuel_sources( # TODO: for DE/GA add merging of the two H2 zones # TODO: for DE/GA add EV electricity consumption from SB as fixed demand + # merge low voltage to market node + move_bus_carrier_and_cleanup(n, from_carrier="low voltage") + # Save base network n.export_to_netcdf(snakemake.output.network) logger.info(f"Saved CBA base network to {snakemake.output.network}") From e12751be6dbd540405ea7646ac94ff28e3ed6ead Mon Sep 17 00:00:00 2001 From: lisa Date: Wed, 15 Jul 2026 05:36:05 +0200 Subject: [PATCH 2/8] fix: update docstring --- scripts/cba/simplify_sb_network.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index 910ca07082..c2eff8aec2 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -64,10 +64,10 @@ def extend_primary_fuel_sources( def move_bus_carrier_and_cleanup( - n, - from_carrier="low voltage", - busmap=None, -): + n: pypsa.Network, + from_carrier: str = "low voltage", + busmap: pd.Series | None = None, +) -> None: """ Reassign all components attached to buses of `from_carrier` onto their corresponding buses (as given by `busmap`), remove the now-empty @@ -92,8 +92,8 @@ def move_bus_carrier_and_cleanup( Returns ------- - n : pypsa.Network - The modified network (same object, mutated in place). + None + Network is modified in place. """ if busmap is None: busmap = n.buses.loc[n.buses.carrier == from_carrier, "location"] From 9e80f1037c22d83c99ddf1045df9b7c6e6ac5b31 Mon Sep 17 00:00:00 2001 From: lisa Date: Wed, 15 Jul 2026 08:31:32 +0200 Subject: [PATCH 3/8] feat: add check if buses from map are in network --- scripts/cba/simplify_sb_network.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index c2eff8aec2..deed46b41b 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -95,8 +95,13 @@ def move_bus_carrier_and_cleanup( None Network is modified in place. """ + # create busmap if busmap is None: busmap = n.buses.loc[n.buses.carrier == from_carrier, "location"] + # check if buses exist in network + if not busmap.isin(n.buses.index).all(): + missing = busmap[~busmap.isin(n.buses.index)] + raise ValueError(f"Locations not found in n.buses.index: {missing.tolist()}") # remove load shedding generators on low voltage buses to avoid having two # load shedding generators at market node From a8468e4f13c71750c997623a70efe4a14df75000 Mon Sep 17 00:00:00 2001 From: lisazeyen <35347358+lisazeyen@users.noreply.github.com> Date: Tue, 28 Jul 2026 13:05:41 +0200 Subject: [PATCH 4/8] fix: refer to write default in docstring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Rüdt <117752024+daniel-rdt@users.noreply.github.com> --- scripts/cba/simplify_sb_network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index deed46b41b..05c9c656fd 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -86,9 +86,9 @@ def move_bus_carrier_and_cleanup( Bus carrier to remove (e.g. "low voltage"). busmap : pandas.Series, optional Mapping of `from_carrier` bus name -> target bus name onto which its - components should be reassigned. Defaults to - `n.buses.loc[n.buses.carrier == from_carrier, "location"]`, i.e. - each bus is mapped onto the bus matching its `location` field. + components should be reassigned. Defaults to `None`, in which case + each bus is mapped onto the bus matching its `location` field, i.e. + `n.buses.loc[n.buses.carrier == from_carrier, "location"]` is used. Returns ------- From a6edf1b42a399fe2c9de1bf05f6a1ce22c1d8cef Mon Sep 17 00:00:00 2001 From: lisazeyen <35347358+lisazeyen@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:12:09 +0200 Subject: [PATCH 5/8] fix: remove static check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Rüdt <117752024+daniel-rdt@users.noreply.github.com> --- scripts/cba/simplify_sb_network.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index 05c9c656fd..542accadd1 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -112,16 +112,12 @@ def move_bus_carrier_and_cleanup( n.remove("Generator", load_shedding_gens) # reassign every component attached to the from_carrier buses - components = sorted(n.branch_components | n.one_port_components) - for cname in components: - c = n.c[cname] + for c in n.components[sorted(n.branch_components | n.one_port_components)]: static = c.static if static.empty: continue for port in c.ports: col = f"bus{port}" - if col not in static: - continue mask = static[col].isin(busmap.index) if mask.any(): static.loc[mask, col] = static.loc[mask, col].map(busmap) From 59f3e70dac60e9643480f2ac6b6f67e1aaa34221 Mon Sep 17 00:00:00 2001 From: lisa Date: Tue, 28 Jul 2026 14:17:12 +0200 Subject: [PATCH 6/8] refactor: iterate over branch components + use n.components --- scripts/cba/simplify_sb_network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index 542accadd1..9cfe2feeec 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -126,13 +126,13 @@ def move_bus_carrier_and_cleanup( n.remove("Bus", busmap.index) # drop lines/links where bus0 == bus1 after remapping - for cname in ["Line", "Link"]: - static = n.c[cname].static + for c in n.components[sorted(n.branch_components)]: + static = c.static if static.empty: continue to_remove = static.index[static.bus0 == static.bus1] if len(to_remove): - n.remove(cname, to_remove) + n.remove(c.name, to_remove) if __name__ == "__main__": From 7a5b4d96e5c8bdbc36dc40dd2b588c6840336ec1 Mon Sep 17 00:00:00 2001 From: lisa Date: Tue, 28 Jul 2026 14:19:17 +0200 Subject: [PATCH 7/8] docs: add release note --- doc/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release_notes.md b/doc/release_notes.md index 5cf6f67128..6885beeb90 100644 --- a/doc/release_notes.md +++ b/doc/release_notes.md @@ -9,6 +9,8 @@ **Features** +* Merge low voltage and market nodes in the CBA ([#722](https://github.com/open-energy-transition/open-tyndp/pull/722)). + * Add Snakemake rules to launch the `PyPSA-Explorer` with pre-solved SB networks from previous releases ([#724](https://github.com/open-energy-transition/open-tyndp/pull/724)). **Changes** From 34d3bca7dcc25ea06bdc7de00cea596bd8ca74c5 Mon Sep 17 00:00:00 2001 From: lisazeyen <35347358+lisazeyen@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:47:52 +0200 Subject: [PATCH 8/8] docs: update comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Rüdt <117752024+daniel-rdt@users.noreply.github.com> --- scripts/cba/simplify_sb_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index 9cfe2feeec..587b651714 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -125,7 +125,7 @@ def move_bus_carrier_and_cleanup( # remove carrier buses n.remove("Bus", busmap.index) - # drop lines/links where bus0 == bus1 after remapping + # drop branch components (e.g. lines/links) where bus0 == bus1 after remapping for c in n.components[sorted(n.branch_components)]: static = c.static if static.empty: