Skip to content

Commit

Permalink
update docstring and comments get_weights_df function
Browse files Browse the repository at this point in the history
  • Loading branch information
khelfen committed Aug 12, 2024
1 parent ae9f79a commit de013e8
Showing 1 changed file with 72 additions and 51 deletions.
123 changes: 72 additions & 51 deletions edisgo/io/electromobility_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,9 @@ def assure_minimum_potential_charging_parks(
Parameters
----------
edisgo_obj : EDisGo
edisgo_obj : :class:`~.EDisGo`
The eDisGo object containing grid and electromobility data.
potential_charging_parks_gdf : gpd.GeoDataFrame
potential_charging_parks_gdf : :geopandas:`geopandas.GeoDataFrame<GeoDataFrame>`
A GeoDataFrame containing potential charging park locations
and their attributes.
**kwargs : dict, optional
Expand All @@ -395,7 +395,7 @@ def assure_minimum_potential_charging_parks(
Returns
-------
gpd.GeoDataFrame
:geopandas:`geopandas.GeoDataFrame<GeoDataFrame>`
A GeoDataFrame with adjusted potential charging park locations,
ensuring the minimum required number of parks per use case.
Expand Down Expand Up @@ -562,70 +562,83 @@ def distribute_charging_demand(edisgo_obj, **kwargs):

def get_weights_df(edisgo_obj, potential_charging_park_indices, **kwargs):
"""
Get weights per potential charging point for a given set of grid connection indices.
Calculate the weights for potential charging points based on user and grid
preferences.
This function determines the attractiveness of potential charging parks by
calculating weights that reflect either user convenience or grid stability,
or a combination of both. The weights are adjusted based on specified
parameters and are returned as a DataFrame.
Parameters
----------
edisgo_obj : :class:`~.EDisGo`
The eDisGo object containing relevant grid and electromobility data.
potential_charging_park_indices : list
List of potential charging parks indices
A list of indices identifying the potential charging parks to be
evaluated.
Other Parameters
-----------------
mode : str
Only use user friendly weights ("user_friendly") or combine with
grid friendly weights ("grid_friendly"). Default: "user_friendly".
user_friendly_weight : float
Weight of user friendly weight if mode "grid_friendly". Default: 0.5.
distance_weight: float
Grid friendly weight is a combination of the installed capacity of
generators and loads within a LV grid and the distance towards the
nearest substation. This parameter sets the weight for the distance
parameter. Default: 1/3.
----------------
mode : str, optional
Determines the weighting approach. "user_friendly" uses only user-centric
weights, while "grid_friendly" combines user-centric and grid-centric
weights. Default is "user_friendly".
user_friendly_weight : float, optional
The weight assigned to user-centric preferences when using the
"grid_friendly" mode. Default is 0.5.
distance_weight : float, optional
Weighting factor for the distance to the nearest substation when using
the "grid_friendly" mode. This affects the grid-centric weight
calculation. Default is 1/3.
Returns
-------
:pandas:`pandas.DataFrame<DataFrame>`
DataFrame with numeric weights
A DataFrame containing the calculated weights for each potential charging
park, reflecting either user or grid preferences, or a combination thereof.
Notes
-----
- The grid-friendly weighting takes into account the installed capacity of
generators and loads within a low-voltage (LV) grid, as well as the
distance to the nearest substation.
- If the mode is set to "grid_friendly", the function will combine user
preferences with grid stability considerations to determine the final
weights.
Raises
------
ValueError
If an invalid mode is provided, an exception is raised.
"""

def _get_lv_grid_weights():
"""
DataFrame containing technical data of LV grids.
Calculate technical weights for LV grids.
This internal function creates a DataFrame containing technical
information about LV grids, which is used to calculate grid-centric
weights for potential charging parks.
Returns
--------
-------
:pandas:`pandas.DataFrame<DataFrame>`
Columns of the DataFrame are:
peak_generation_capacity : float
Cumulative peak generation capacity of generators in the network in
MW.
p_set : float
Cumulative peak load of loads in the network in MW.
substation_capacity : float
Cumulative capacity of transformers to overlaying network.
generators_weight : float
Weighting used in grid friendly siting of public charging points.
In the case of generators the weight is defined by dividing the
peak_generation_capacity by substation_capacity and norming the
results from 0 .. 1. A higher weight is more attractive.
loads_weight : float
Weighting used in grid friendly siting of public charging points.
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
grid is for new loads from a grid perspective. A higher weight is
more attractive.
A DataFrame with the following columns:
- peak_generation_capacity: Cumulative peak generation capacity of
generators in the LV grid (in MW).
- p_set: Cumulative peak load of loads in the LV grid (in MW).
- substation_capacity: Cumulative capacity of transformers
connecting the LV grid to the overlaying network.
- generators_weight: Weight reflecting the attractiveness of the
LV grid for new generation capacity.
- loads_weight: Weight reflecting the attractiveness of the LV grid
for new loads, from a grid stability perspective.
"""
lv_grids = list(edisgo_obj.topology.mv_grid.lv_grids)

# Create a DataFrame to store LV grid weights
lv_grids_df = pd.DataFrame(
index=[_._id for _ in lv_grids],
columns=[
Expand All @@ -637,14 +650,15 @@ def _get_lv_grid_weights():
],
)

# Populate the DataFrame with relevant data
lv_grids_df.peak_generation_capacity = [
_.peak_generation_capacity for _ in lv_grids
]

lv_grids_df.substation_capacity = [
_.transformers_df.s_nom.sum() for _ in lv_grids
]

# Normalize generator weights
min_max_scaler = preprocessing.MinMaxScaler()
lv_grids_df.generators_weight = lv_grids_df.peak_generation_capacity.divide(
lv_grids_df.substation_capacity
Expand All @@ -655,23 +669,27 @@ def _get_lv_grid_weights():

lv_grids_df.p_set = [_.p_set for _ in lv_grids]

# Normalize load weights and normalize them
lv_grids_df.loads_weight = lv_grids_df.p_set.divide(
lv_grids_df.substation_capacity
)
lv_grids_df.loads_weight = 1 - min_max_scaler.fit_transform(
lv_grids_df.loads_weight.values.reshape(-1, 1)
)

return lv_grids_df

mode = kwargs.get("mode", "user_friendly")

if mode == "user_friendly":
# Retrieve user-centric weights for the selected charging parks
weights = [
_.user_centric_weight
for _ in edisgo_obj.electromobility.potential_charging_parks
if _.id in potential_charging_park_indices
]
elif mode == "grid_friendly":
# Prepare data for grid-centric weight calculation
potential_charging_parks = list(
edisgo_obj.electromobility.potential_charging_parks
)
Expand All @@ -687,28 +705,30 @@ def _get_lv_grid_weights():
generators_weight_factor = kwargs.get("generators_weight_factor", 0.5)
loads_weight_factor = 1 - generators_weight_factor

# Combine generator and load weights to calculate grid-centric weights
combined_weights = (
generators_weight_factor * lv_grids_df["generators_weight"]
+ loads_weight_factor * lv_grids_df["loads_weight"]
)

# Retrieve the IDs of the nearest LV grids for each potential charging park
lv_grid_ids = [
_.nearest_substation["lv_grid_id"] for _ in potential_charging_parks
]

# Map the combined weights to the corresponding charging parks
load_and_generator_capacity_weights = [
combined_weights.at[lv_grid_id] for lv_grid_id in lv_grid_ids
]

# fmt: off
# Extract the distance weights from the eDisGo object
distance_weights = (
edisgo_obj.electromobility._potential_charging_parks_df.distance_weight
.tolist()
edisgo_obj.electromobility._potential_charging_parks_df.distance_weight.tolist() # noqa: E501
)
# fmt: on

distance_weight = kwargs.get("distance_weight", 1 / 3)

# Combine the distance weights with the load and generator capacity weights
grid_friendly_weights = [
(1 - distance_weight) * load_and_generator_capacity_weights[i]
+ distance_weight * distance_weights[i]
Expand All @@ -717,17 +737,18 @@ def _get_lv_grid_weights():

user_friendly_weight = kwargs.get("user_friendly_weight", 0.5)

# Final combined weights considering both user and grid preferences
weights = [
(1 - user_friendly_weight) * grid_friendly_weights[i]
+ user_friendly_weight * user_friendly_weights[i]
for i in range(len(grid_friendly_weights))
]

else:
raise ValueError(
"Provided mode is not valid, needs to be 'user_friendly' or "
"Provided mode is not valid. It must be either 'user_friendly' or "
"'grid_friendly'."
)

return pd.DataFrame(weights)


Expand Down

0 comments on commit de013e8

Please sign in to comment.