Skip to content

Commit

Permalink
Feature/lv grid property (#279)
Browse files Browse the repository at this point in the history
  • Loading branch information
birgits committed Jul 27, 2022
1 parent b4646b2 commit b74e439
Show file tree
Hide file tree
Showing 27 changed files with 436 additions and 2,792 deletions.
6 changes: 4 additions & 2 deletions doc/usage_details.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,16 @@ A list of all LV grids can be retrieved through:
# (Note that MVGrid.lv_grids returns a generator object that must first be
# converted to a list in order to view the LVGrid objects)
list(edisgo.topology.mv_grid.lv_grids)
# the following yields the same
list(edisgo.topology.lv_grids)
Access to a single LV grid's components can be obtained analog to shown above for
the whole topology and the MV grid:

.. code-block:: python
# Get single LV grid
lv_grid = list(edisgo.topology.mv_grid.lv_grids)[0]
# Get single LV grid by providing its ID (e.g. 1) or name (e.g. "LVGrid_1")
lv_grid = edisgo.topology.get_lv_grid("LVGrid_402945")
# Access all buses in that LV grid
lv_grid.buses_df
Expand Down
15 changes: 9 additions & 6 deletions edisgo/edisgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ def to_pypsa(
* 'lv'
Single LV network topology including the MV/LV transformer is exported.
The LV grid to export is specified through the parameter `lv_grid_name`.
The LV grid to export is specified through the parameter `lv_grid_id`.
The slack is positioned at the secondary side of the MV/LV station.
timesteps : :pandas:`pandas.DatetimeIndex<DatetimeIndex>` or \
Expand All @@ -561,8 +561,9 @@ def to_pypsa(
analyses as initial guess in case of PQ buses. PV buses currently do
not occur and are therefore currently not supported.
Default: False.
lv_grid_name : str
String representative of LV grid to export in case mode is 'lv'.
lv_grid_id : int or str
ID (e.g. 1) or name (string representation, e.g. "LVGrid_1") of LV grid
to export in case mode is 'lv'.
aggregate_loads : str
Mode for load aggregation in LV grids in case mode is 'mv' or 'mvlv'.
Can be 'sectoral' aggregating the loads sector-wise, 'all' aggregating all
Expand Down Expand Up @@ -716,9 +717,11 @@ def analyze(
* 'lv'
Power flow analysis is conducted for one LV grid only. Name of the LV
grid to conduct power flow analysis for needs to be provided through
keyword argument 'lv_grid_name' as string.
Power flow analysis is conducted for one LV grid only. ID or name of
the LV grid to conduct power flow analysis for needs to be provided
through keyword argument 'lv_grid_id' as integer or string.
See parameter `lv_grid_id` in :attr:`~.edisgo.EDisGo.to_pypsa` for more
information.
The slack is positioned at the secondary side of the MV/LV station.
timesteps : :pandas:`pandas.DatetimeIndex<DatetimeIndex>` or \
Expand Down
17 changes: 9 additions & 8 deletions edisgo/flex_opt/check_tech_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,9 @@ def lines_allowed_load(edisgo_obj, voltage_level):
lines_df = edisgo_obj.topology.lines_df[
~edisgo_obj.topology.lines_df.index.isin(mv_grid.lines_df.index)
]
if len(list(mv_grid.lv_grids)) > 0:
nominal_voltage = list(mv_grid.lv_grids)[0].nominal_voltage
lv_grids = list(edisgo_obj.topology.lv_grids)
if len(lv_grids) > 0:
nominal_voltage = lv_grids[0].nominal_voltage
else:
nominal_voltage = np.NaN
elif voltage_level == "mv":
Expand Down Expand Up @@ -332,7 +333,7 @@ def mv_lv_station_load(edisgo_obj):
"""

crit_stations = pd.DataFrame(dtype=float)
for lv_grid in edisgo_obj.topology.mv_grid.lv_grids:
for lv_grid in edisgo_obj.topology.lv_grids:
crit_stations = pd.concat(
[
crit_stations,
Expand Down Expand Up @@ -506,10 +507,10 @@ def lv_voltage_deviation(edisgo_obj, mode=None, voltage_levels="mv_lv"):
Parameters
----------
edisgo_obj : :class:`~.EDisGo`
mode : None or :obj:`str`
mode : None or str
If None voltage at all buses in LV networks is checked. If mode is set
to 'stations' only voltage at bus bar is checked. Default: None.
voltage_levels : :obj:`str`
voltage_levels : str
Specifies which allowed voltage deviations to use. Possible options
are:
Expand All @@ -524,7 +525,7 @@ def lv_voltage_deviation(edisgo_obj, mode=None, voltage_levels="mv_lv"):
Returns
-------
:obj:`dict`
dict
Dictionary with representative of :class:`~.network.grids.LVGrid` as
key and a :pandas:`pandas.DataFrame<DataFrame>` with voltage
deviations from allowed lower or upper voltage limits, sorted
Expand Down Expand Up @@ -553,7 +554,7 @@ def lv_voltage_deviation(edisgo_obj, mode=None, voltage_levels="mv_lv"):
"'lv'.".format(voltage_levels)
)

for lv_grid in edisgo_obj.topology.mv_grid.lv_grids:
for lv_grid in edisgo_obj.topology.lv_grids:

if mode:
if mode == "stations":
Expand All @@ -577,7 +578,7 @@ def lv_voltage_deviation(edisgo_obj, mode=None, voltage_levels="mv_lv"):
)

if not crit_buses_grid.empty:
crit_buses[repr(lv_grid)] = crit_buses_grid
crit_buses[str(lv_grid)] = crit_buses_grid

if crit_buses:
if mode == "stations":
Expand Down
2 changes: 1 addition & 1 deletion edisgo/flex_opt/costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def _get_line_costs(lines_added):
# costs for transformers
if not equipment_changes.empty:
transformers = equipment_changes[
equipment_changes.index.isin(edisgo_obj.topology._grids)
equipment_changes.index.isin(edisgo_obj.topology._grids_repr)
]
added_transformers = transformers[transformers["change"] == "added"]
removed_transformers = transformers[transformers["change"] == "removed"]
Expand Down
2 changes: 1 addition & 1 deletion edisgo/flex_opt/reinforce_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ def _add_transformer_changes_to_equipment_changes(mode: str | None):
# reinforce lines
lines_changes = reinforce_measures.reinforce_lines_voltage_issues(
edisgo_reinforce,
edisgo_reinforce.topology._grids[grid],
edisgo_reinforce.topology.get_lv_grid(grid),
crit_nodes[grid],
)
# write changed lines to results.equipment_changes
Expand Down
14 changes: 10 additions & 4 deletions edisgo/flex_opt/reinforce_measures.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ def _station_overloading(edisgo_obj, critical_stations, voltage_level):

transformers_changes = {"added": {}, "removed": {}}
for grid_name in critical_stations.index:
grid = edisgo_obj.topology._grids[grid_name]
if "MV" in grid_name:
grid = edisgo_obj.topology.mv_grid
else:
grid = edisgo_obj.topology.get_lv_grid(grid_name)
# list of maximum power of each transformer in the station
s_max_per_trafo = grid.transformers_df.s_nom
# missing capacity
Expand Down Expand Up @@ -308,8 +311,11 @@ def reinforce_mv_lv_station_voltage_issues(edisgo_obj, critical_stations):
raise KeyError("Standard MV/LV transformer is not in equipment list.")

transformers_changes = {"added": {}}
for grid_repr in critical_stations.keys():
grid = edisgo_obj.topology._grids[grid_repr]
for grid_name in critical_stations.keys():
if "MV" in grid_name:
grid = edisgo_obj.topology.mv_grid
else:
grid = edisgo_obj.topology.get_lv_grid(grid_name)
# get any transformer to get attributes for new transformer from
duplicated_transformer = grid.transformers_df.iloc[[0]]
# change transformer parameters
Expand All @@ -328,7 +334,7 @@ def reinforce_mv_lv_station_voltage_issues(edisgo_obj, critical_stations):
duplicated_transformer,
]
)
transformers_changes["added"][grid_repr] = duplicated_transformer.index.tolist()
transformers_changes["added"][grid_name] = duplicated_transformer.index.tolist()

if transformers_changes["added"]:
logger.debug(
Expand Down
12 changes: 0 additions & 12 deletions edisgo/io/ding0_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,9 @@ def sort_hvmv_transformer_buses(transformers_df):
"srid": grid.srid,
}

edisgo_obj.topology._grids = {}

# set up medium voltage grid
mv_grid_id = list(set(grid.buses.mv_grid_id))[0]
edisgo_obj.topology.mv_grid = MVGrid(id=mv_grid_id, edisgo_obj=edisgo_obj)
edisgo_obj.topology._grids[
str(edisgo_obj.topology.mv_grid)
] = edisgo_obj.topology.mv_grid

# set up low voltage grids
lv_grid_ids = set(grid.buses.lv_grid_id.dropna())
for lv_grid_id in lv_grid_ids:
lv_grid = LVGrid(id=lv_grid_id, edisgo_obj=edisgo_obj)
edisgo_obj.topology.mv_grid._lv_grids.append(lv_grid)
edisgo_obj.topology._grids[str(lv_grid)] = lv_grid

# Check data integrity
edisgo_obj.topology.check_integrity()
10 changes: 5 additions & 5 deletions edisgo/io/electromobility_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,12 +507,12 @@ def distribute_charging_demand(edisgo_obj, **kwargs):
"grid_friendly" also grid conditions are respected.
Default "user_friendly".
generators_weight_factor : float
Weighting factor of the generators weight within a lv grid in
Weighting factor of the generators weight within an LV grid in
comparison to the loads weight. Default 0.5.
distance_weight : float
Weighting factor for the distance between a potential charging park
and it's nearest substation in comparison to the combination of
the generators and load factors of the lv grids.
and its nearest substation in comparison to the combination of
the generators and load factors of the LV grids.
Default 1 / 3.
user_friendly_weight : float
Weighting factor of the user friendly weight in comparison to the
Expand Down Expand Up @@ -556,7 +556,7 @@ def get_weights_df(edisgo_obj, potential_charging_park_indices, **kwargs):
def _get_lv_grid_weights():

"""
DataFrame containing technical data of lv grids.
DataFrame containing technical data of LV grids.
Returns
--------
Expand All @@ -583,7 +583,7 @@ def _get_lv_grid_weights():
In the case of loads the weight is defined by dividing the
p_set by substation_capacity and norming the results from 0 .. 1.
The result is then substracted from 1 as the higher the p_set is
in relation to the substation_capacity the less attractive this lv
in relation to the substation_capacity the less attractive this LV
grid is for new loads from a grid perspective. A higher weight is
more attractive.
Expand Down
14 changes: 9 additions & 5 deletions edisgo/io/generators_import.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import os
import numpy as np
import random

import pandas as pd
Expand Down Expand Up @@ -233,11 +234,13 @@ def _validate_sample_geno_location():
# get geom of 1 random MV and 1 random LV generator and transform
sample_mv_geno_geom_shp = transform(
projection,
wkt_loads(generators_res_mv["geom"].dropna().sample(n=1).values[0]),
wkt_loads(generators_res_mv["geom"].dropna().sample(
n=1, random_state=42).values[0]),
)
sample_lv_geno_geom_shp = transform(
projection,
wkt_loads(generators_res_lv["geom"].dropna().sample(n=1).values[0]),
wkt_loads(generators_res_lv["geom"].dropna().sample(
n=1, random_state=42).values[0]),
)

# get geom of MV grid district
Expand Down Expand Up @@ -374,7 +377,7 @@ def _update_grids(
Index of the dataframe are the generator IDs.
Columns are the same as in `imported_generators_mv` plus:
* mvlv_subst_id : int
* mvlv_subst_id : int or float
ID of MV-LV substation in grid = grid, the generator will be
connected to.
Expand Down Expand Up @@ -647,9 +650,10 @@ def drop_generators(generator_list, gen_type, total_capacity):

# check if new generators can be allocated to an existing LV grid
if not imported_generators_lv.empty:
grid_ids = [_.id for _ in edisgo_object.topology._grids.values()]
grid_ids = edisgo_object.topology._lv_grid_ids
if not any(
[_ in grid_ids for _ in list(imported_generators_lv["mvlv_subst_id"])]
[int(_) in grid_ids for _ in list(imported_generators_lv["mvlv_subst_id"])
if not np.isnan(_)]
):
logger.warning(
"None of the imported LV generators can be allocated "
Expand Down
9 changes: 5 additions & 4 deletions edisgo/io/pypsa_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,13 @@ def _set_slack(grid):

pypsa_network.mode = "lv"

lv_grid_name = kwargs.get("lv_grid_name", None)
if not lv_grid_name:
lv_grid_id = kwargs.get("lv_grid_id", None)
if not lv_grid_id:
raise ValueError(
"For exporting lv grids, name of lv_grid has to be provided."
"For exporting LV grids, ID or name of LV grid has to be provided"
"using parameter `lv_grid_id`."
)
grid_object = edisgo_object.topology._grids[lv_grid_name]
grid_object = edisgo_object.topology.get_lv_grid(lv_grid_id)
buses_df = grid_object.buses_df.loc[:, ["v_nom"]]
slack_df = _set_slack(grid_object)

Expand Down

0 comments on commit b74e439

Please sign in to comment.