|
8 | 8 | - Fixed optimal capacities from scenario building |
9 | 9 | - Hurdle costs on DC links |
10 | 10 | - Extended primary fuel source capacities |
| 11 | +- Merge low and high voltage buses |
11 | 12 |
|
12 | 13 | **Inputs** |
13 | 14 |
|
@@ -62,6 +63,78 @@ def extend_primary_fuel_sources( |
62 | 63 | n.generators.loc[gen_i, "p_nom"] = inf |
63 | 64 |
|
64 | 65 |
|
| 66 | +def move_bus_carrier_and_cleanup( |
| 67 | + n: pypsa.Network, |
| 68 | + from_carrier: str = "low voltage", |
| 69 | + busmap: pd.Series | None = None, |
| 70 | +) -> None: |
| 71 | + """ |
| 72 | + Reassign all components attached to buses of `from_carrier` onto their |
| 73 | + corresponding buses (as given by `busmap`), remove the now-empty |
| 74 | + `from_carrier` buses, and drop any lines/links that end up with |
| 75 | + bus0 == bus1 as a result. |
| 76 | +
|
| 77 | + Also drops redundant load-shedding generators sitting on `from_carrier` |
| 78 | + buses, since after reassignment they would duplicate the load-shedding |
| 79 | + generator already present at the target bus. |
| 80 | +
|
| 81 | + Parameters |
| 82 | + ---------- |
| 83 | + n : pypsa.Network |
| 84 | + Network to modify in place. |
| 85 | + from_carrier : str, default "low voltage" |
| 86 | + Bus carrier to remove (e.g. "low voltage"). |
| 87 | + busmap : pandas.Series, optional |
| 88 | + Mapping of `from_carrier` bus name -> target bus name onto which its |
| 89 | + components should be reassigned. Defaults to `None`, in which case |
| 90 | + each bus is mapped onto the bus matching its `location` field, i.e. |
| 91 | + `n.buses.loc[n.buses.carrier == from_carrier, "location"]` is used. |
| 92 | +
|
| 93 | + Returns |
| 94 | + ------- |
| 95 | + None |
| 96 | + Network is modified in place. |
| 97 | + """ |
| 98 | + # create busmap |
| 99 | + if busmap is None: |
| 100 | + busmap = n.buses.loc[n.buses.carrier == from_carrier, "location"] |
| 101 | + # check if buses exist in network |
| 102 | + if not busmap.isin(n.buses.index).all(): |
| 103 | + missing = busmap[~busmap.isin(n.buses.index)] |
| 104 | + raise ValueError(f"Locations not found in n.buses.index: {missing.tolist()}") |
| 105 | + |
| 106 | + # remove load shedding generators on low voltage buses to avoid having two |
| 107 | + # load shedding generators at market node |
| 108 | + load_shedding_gens = n.generators.index[ |
| 109 | + (n.generators.bus.map(n.buses.carrier) == from_carrier) |
| 110 | + & (n.generators.carrier == "load") |
| 111 | + ] |
| 112 | + n.remove("Generator", load_shedding_gens) |
| 113 | + |
| 114 | + # reassign every component attached to the from_carrier buses |
| 115 | + for c in n.components[sorted(n.branch_components | n.one_port_components)]: |
| 116 | + static = c.static |
| 117 | + if static.empty: |
| 118 | + continue |
| 119 | + for port in c.ports: |
| 120 | + col = f"bus{port}" |
| 121 | + mask = static[col].isin(busmap.index) |
| 122 | + if mask.any(): |
| 123 | + static.loc[mask, col] = static.loc[mask, col].map(busmap) |
| 124 | + |
| 125 | + # remove carrier buses |
| 126 | + n.remove("Bus", busmap.index) |
| 127 | + |
| 128 | + # drop branch components (e.g. lines/links) where bus0 == bus1 after remapping |
| 129 | + for c in n.components[sorted(n.branch_components)]: |
| 130 | + static = c.static |
| 131 | + if static.empty: |
| 132 | + continue |
| 133 | + to_remove = static.index[static.bus0 == static.bus1] |
| 134 | + if len(to_remove): |
| 135 | + n.remove(c.name, to_remove) |
| 136 | + |
| 137 | + |
65 | 138 | if __name__ == "__main__": |
66 | 139 | if "snakemake" not in globals(): |
67 | 140 | from scripts._helpers import mock_snakemake |
@@ -96,6 +169,9 @@ def extend_primary_fuel_sources( |
96 | 169 | # TODO: for DE/GA add merging of the two H2 zones |
97 | 170 | # TODO: for DE/GA add EV electricity consumption from SB as fixed demand |
98 | 171 |
|
| 172 | + # merge low voltage to market node |
| 173 | + move_bus_carrier_and_cleanup(n, from_carrier="low voltage") |
| 174 | + |
99 | 175 | # Save base network |
100 | 176 | n.export_to_netcdf(snakemake.output.network) |
101 | 177 | logger.info(f"Saved CBA base network to {snakemake.output.network}") |
0 commit comments