Skip to content

Commit

Permalink
Merge pull request #999 from davide-f/line_types
Browse files Browse the repository at this point in the history
Generalize linetypes for AC and DC lines
  • Loading branch information
davide-f authored Apr 3, 2024
2 parents d3d718b + 429652b commit 39357a2
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 80 deletions.
8 changes: 6 additions & 2 deletions config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,15 @@ electricity:
PV: [solar]

lines:
types:
ac_types:
132.: "243-AL1/39-ST1A 20.0"
220.: "Al/St 240/40 2-bundle 220.0"
300.: "Al/St 240/40 3-bundle 300.0"
380.: "Al/St 240/40 4-bundle 380.0"
dc_type: "HVDC XLPE 1000"
500.: "Al/St 240/40 4-bundle 380.0"
750.: "Al/St 560/50 4-bundle 750.0"
dc_types:
500.: "HVDC XLPE 1000"
s_max_pu: 0.7
s_nom_max: .inf
length_factor: 1.25
Expand Down
8 changes: 6 additions & 2 deletions config.tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,15 @@ electricity:
Onshore: [onwind]
PV: [solar]
lines:
types:
ac_types:
132.: "243-AL1/39-ST1A 20.0"
220.: "Al/St 240/40 2-bundle 220.0"
300.: "Al/St 240/40 3-bundle 300.0"
380.: "Al/St 240/40 4-bundle 380.0"
dc_type: "HVDC XLPE 1000"
500.: "Al/St 240/40 4-bundle 380.0"
750.: "Al/St 560/50 4-bundle 750.0"
dc_types:
500.: "HVDC XLPE 1000"
s_max_pu: 0.7
s_nom_max: .inf
length_factor: 1.25
Expand Down
4 changes: 2 additions & 2 deletions doc/configtables/lines.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
,Unit,Values,Description
types,--,"Values should specify a `line type in PyPSA <https://pypsa.readthedocs.io/en/latest/components.html#line-types>`_ in case AC lines or a custom linetype in case of DC lines modeled as Lines. Keys should specify the corresponding voltage level (e.g. 220., 300. and 380. kV)", "Specifies line types to assume for the different voltage levels of the ENTSO-E grid extraction. Should normally handle voltage levels 220, 300, and 380 kV."
dc_type, --, "Values should specify a `line type in PyPSA <https://pypsa.readthedocs.io/en/latest/components.html#line-types>`_ for DC-lines.", "Specifies DC-line types."
ac_types,--,"Values should specify a `line type in PyPSA <https://pypsa.readthedocs.io/en/latest/components.html#line-types>`_ for AC lines. Keys should specify the corresponding voltage level (e.g. 220., 300. and 380. kV)", "Specifies line types to assume for the different voltage levels of the target region. Should normally handle voltage levels 220, 300, and 380 kV."
dc_types, --, "Values should specify a `line type in PyPSA <https://pypsa.readthedocs.io/en/latest/components.html#line-types>`_ for DC-lines. Keys should specify the corresponding voltage level (e.g. 220., 300. and 380. kV)", "Specifies DC-line types."
s_max_pu,--,"Value in [0.,1.]","Correction factor for line capacities (``s_nom``) to approximate :math:`N-1` security and reserve capacity for reactive power flows"
s_nom_max,MW,"float","Global upper limit for the maximum capacity of each extendable line."
length_factor,--,float,"Correction factor to account for the fact that buses are *not* connected by lines through air-line distance."
Expand Down
2 changes: 2 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ E.g. if a new rule becomes available describe how to use it `snakemake -j1 run_t

* Introduce flexible regional selection of the demand files of GEGIS. `PR #991 <https://github.com/pypsa-meets-earth/pypsa-earth/pull/991>`__

* Generalize line types for AC and DC networks. `PR #999 <https://github.com/pypsa-meets-earth/pypsa-earth/pull/999>`__

**Minor Changes and bug-fixing**

* Minor bug-fixing for GADM_ID format naming. `PR #980 <https://github.com/pypsa-meets-earth/pypsa-earth/pull/980>`__, `PR #986 <https://github.com/pypsa-meets-earth/pypsa-earth/pull/986>`__ and `PR #989 <https://github.com/pypsa-meets-earth/pypsa-earth/pull/989>`__
Expand Down
141 changes: 68 additions & 73 deletions scripts/base_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def _find_closest_links(links, new_links, distance_upper_bound=1.5):
)


def _load_buses_from_osm(fp_buses, base_network_config, voltages_config):
def _load_buses_from_osm(fp_buses):
buses = (
read_csv_nafix(fp_buses, dtype=dict(bus_id="str", voltage="float"))
.set_index("bus_id")
Expand All @@ -128,15 +128,6 @@ def _load_buses_from_osm(fp_buses, base_network_config, voltages_config):
# TODO: Drop NAN maybe somewhere else?
buses = buses.dropna(axis="index", subset=["x", "y", "country"])

# Rebase all voltages to three levels
buses = _rebase_voltage_to_config(base_network_config, voltages_config, buses)

logger.info(
"Removing buses with voltages {}".format(
pd.Index(buses.v_nom.unique()).dropna().difference(voltages_config)
)
)

return buses


Expand Down Expand Up @@ -186,7 +177,7 @@ def _set_dc_underwater_fraction(lines_or_links, fp_offshore_shapes):
)


def _load_lines_from_osm(fp_osm_lines, base_network_config, voltages_config, buses):
def _load_lines_from_osm(fp_osm_lines):
lines = (
read_csv_nafix(
fp_osm_lines,
Expand All @@ -207,9 +198,6 @@ def _load_lines_from_osm(fp_osm_lines, base_network_config, voltages_config, bus
lines["length"] /= 1e3 # m to km conversion
lines["v_nom"] /= 1e3 # V to kV conversion
lines = lines.loc[:, ~lines.columns.str.contains("^Unnamed")] # remove unnamed col
lines = _rebase_voltage_to_config(
base_network_config, voltages_config, lines
) # rebase voltage to config inputs
# lines = _remove_dangling_branches(lines, buses) # TODO: add dangling branch removal?

return lines
Expand Down Expand Up @@ -240,9 +228,6 @@ def _load_links_from_osm(fp_osm_converters, base_network_config, voltages_config
links["length"] /= 1e3 # m to km conversion
links["v_nom"] /= 1e3 # V to kV conversion
links = links.loc[:, ~links.columns.str.contains("^Unnamed")] # remove unnamed col
links = _rebase_voltage_to_config(
base_network_config, voltages_config, links
) # rebase voltage to config inputs
# links = _remove_dangling_branches(links, buses) # TODO: add dangling branch removal?

return links
Expand Down Expand Up @@ -281,30 +266,85 @@ def _load_transformers_from_osm(fp_osm_transformers, buses):
return transformers


def _set_electrical_parameters_lines(lines_config, voltages_config, lines):
def _get_linetypes_config(line_types, voltages):
"""
Return the dictionary of linetypes for selected voltages. The dictionary is
a subset of the dictionary line_types, whose keys match the selected
voltages.
Parameters
----------
line_types : dict
Dictionary of linetypes: keys are nominal voltages and values are linetypes.
voltages : list
List of selected voltages.
Returns
-------
Dictionary of linetypes for selected voltages.
"""
# get voltages value that are not availabile in the line types
vnoms_diff = set(voltages).symmetric_difference(set(line_types.keys()))
if vnoms_diff:
logger.warning(
f"Voltages {vnoms_diff} not in the {line_types} or {voltages} list."
)
return {k: v for k, v in line_types.items() if k in voltages}


def _get_linetype_by_voltage(v_nom, d_linetypes):
"""
Return the linetype of a specific line based on its voltage v_nom.
Parameters
----------
v_nom : float
The voltage of the line.
d_linetypes : dict
Dictionary of linetypes: keys are nominal voltages and values are linetypes.
Returns
-------
The linetype of the line whose nominal voltage is closest to the line voltage.
"""
v_nom_min, line_type_min = min(
d_linetypes.items(),
key=lambda x: abs(x[0] - v_nom),
)
return line_type_min


def _set_electrical_parameters_lines(lines_config, voltages, lines):
if lines.empty:
lines["type"] = []
return lines

v_noms = voltages_config
linetypes = _get_linetypes_config(lines_config["ac_types"], voltages)

lines["carrier"] = "AC"
lines["dc"] = False
linetypes = lines_config["types"]

for v_nom in v_noms:
lines.loc[lines["v_nom"] == v_nom, "type"] = linetypes[v_nom]
lines.loc[:, "type"] = lines.v_nom.apply(
lambda x: _get_linetype_by_voltage(x, linetypes)
)

lines["s_max_pu"] = lines_config["s_max_pu"]

return lines


def _set_electrical_parameters_dc_lines(lines_config, voltages_config, lines):
v_noms = voltages_config
def _set_electrical_parameters_dc_lines(lines_config, voltages, lines):
if lines.empty:
lines["type"] = []
return lines

linetypes = _get_linetypes_config(lines_config["dc_types"], voltages)

lines["carrier"] = "DC"
lines["dc"] = True

lines["type"] = lines_config["dc_type"]
lines.loc[:, "type"] = lines.v_nom.apply(
lambda x: _get_linetype_by_voltage(x, linetypes)
)

lines["s_max_pu"] = lines_config["s_max_pu"]

Expand Down Expand Up @@ -437,41 +477,6 @@ def _set_countries_and_substations(inputs, base_network_config, countries_config
return buses


def _rebase_voltage_to_config(base_network_config, voltages_config, component):
"""
Rebase the voltage of components to the config.yaml input.
Components such as line and buses have voltage levels between
110 kV up to around 850 kV. PyPSA-Africa uses 3 voltages as config input.
This function rebases all inputs to the lower, middle and upper voltage
bound.
Parameters
----------
config : dictionary (by snakemake)
component : dataframe
"""
component["v_nom_original"] = component["v_nom"]

v_min = (
base_network_config["min_voltage_rebase_voltage"] / 1000
) # min. filtered value in dataset
v_low = voltages_config[0]
v_mid = voltages_config[1]
v_up = voltages_config[2]
v_low_mid = (v_mid - v_low) / 2 + v_low # between low and mid voltage
v_mid_up = (v_up - v_mid) / 2 + v_mid # between mid and upper voltage
component.loc[
(v_min <= component["v_nom"]) & (component["v_nom"] < v_low_mid), "v_nom"
] = v_low
component.loc[
(v_low_mid <= component["v_nom"]) & (component["v_nom"] < v_mid_up), "v_nom"
] = v_mid
component.loc[v_mid_up <= component["v_nom"], "v_nom"] = v_up

return component


def base_network(
inputs,
base_network_config,
Expand All @@ -483,29 +488,19 @@ def base_network(
transformers_config,
voltages_config,
):
buses = _load_buses_from_osm(
inputs.osm_buses, base_network_config, voltages_config
).reset_index()
lines = _load_lines_from_osm(
inputs.osm_lines, base_network_config, voltages_config, buses
)
buses = _load_buses_from_osm(inputs.osm_buses).reset_index(drop=True)
lines = _load_lines_from_osm(inputs.osm_lines).reset_index(drop=True)
transformers = _load_transformers_from_osm(inputs.osm_transformers, buses)
converters = _load_converters_from_osm(inputs.osm_converters, buses)

lines_ac = lines[lines.tag_frequency.astype(float) != 0].copy()
lines_dc = lines[lines.tag_frequency.astype(float) == 0].copy()

lines_ac = _set_electrical_parameters_lines(lines_config, voltages_config, lines_ac)
lines_ac.num_parallel = lines_ac.num_parallel * (
lines_ac.v_nom_original / lines_ac.v_nom
)

lines_dc = _set_electrical_parameters_dc_lines(
lines_config, voltages_config, lines_dc
)
lines_dc.num_parallel = lines_dc.num_parallel * (
lines_dc.v_nom_original / lines_dc.v_nom
)

transformers = _set_electrical_parameters_transformers(
transformers_config, transformers
Expand Down
2 changes: 1 addition & 1 deletion scripts/simplify_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ def merge_isolated_nodes(n, threshold, aggregation_strategies=dict()):
n = pypsa.Network(snakemake.input.network)

base_voltage = snakemake.params.electricity["base_voltage"]
linetype = snakemake.params.config_lines["types"][base_voltage]
linetype = snakemake.params.config_lines["ac_types"][base_voltage]
exclude_carriers = snakemake.params.cluster_options["simplify_network"].get(
"exclude_carriers", []
)
Expand Down

0 comments on commit 39357a2

Please sign in to comment.