Skip to content

Commit

Permalink
Merge pull request #153 from openego/fix/#152
Browse files Browse the repository at this point in the history
Fix/#152
  • Loading branch information
birgits committed Aug 26, 2019
2 parents e6245bd + b0199a6 commit c37bfb8
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 91 deletions.
5 changes: 5 additions & 0 deletions doc/whatsnew/v0-0-10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ Release date: <month> <day>, <year>

Changes
-------
*

Bug fixes
----------
* Corrected calculation of current from pypsa power flow results (PR #153 `#153 <https://github.com/openego/eDisGo/pull/153>`_).
8 changes: 4 additions & 4 deletions edisgo/flex_opt/check_tech_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,8 @@ def mv_voltage_deviation(network, voltage_levels='mv_lv'):
Notes
-----
Over-voltage is determined based on allowed voltage deviations defined in
the config file 'config_grid_expansion' in section
Voltage issues are determined based on allowed voltage deviations defined
in the config file 'config_grid_expansion' in section
'grid_expansion_allowed_voltage_deviations'.
"""
Expand Down Expand Up @@ -445,8 +445,8 @@ def lv_voltage_deviation(network, mode=None, voltage_levels='mv_lv'):
Notes
-----
Over-voltage is determined based on allowed voltage deviations defined in
the config file 'config_grid_expansion' in section
Voltage issues are determined based on allowed voltage deviations defined
in the config file 'config_grid_expansion' in section
'grid_expansion_allowed_voltage_deviations'.
"""
Expand Down
79 changes: 40 additions & 39 deletions edisgo/grid/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def plot_mv_line_loading(self, **kwargs):
timestep=kwargs.get('timestep', None),
line_color='loading',
node_color=kwargs.get('node_color', None),
line_load=self.network.results.s_res(),
line_load=self.network.results.i_res,
filename=kwargs.get('filename', None),
arrows=kwargs.get('arrows', None),
grid_district_geom=kwargs.get('grid_district_geom', True),
Expand Down Expand Up @@ -206,25 +206,34 @@ def histogram_voltage(self, timestep=None, title=True, **kwargs):
"""
Plots histogram of voltages.
For more information see :func:`edisgo.tools.plots.histogram`.
For more information on the histogram plot and possible configurations
see :func:`edisgo.tools.plots.histogram`.
Parameters
----------
timestep : :pandas:`pandas.Timestamp<timestamp>` or None, optional
Specifies time step histogram is plotted for. If timestep is None
timestep : :pandas:`pandas.Timestamp<timestamp>` or list(:pandas:`pandas.Timestamp<timestamp>`) or None, optional
Specifies time steps histogram is plotted for. If timestep is None
all time steps voltages are calculated for are used. Default: None.
title : :obj:`str` or :obj:`bool`, optional
Title for plot. If True title is auto generated. If False plot has
no title. If :obj:`str`, the provided title is used. Default: True.
"""
data = self.network.results.v_res()

if timestep is None:
timestep = data.index
# check if timesteps is array-like, otherwise convert to list
if not hasattr(timestep, "__len__"):
timestep = [timestep]

if title is True:
if timestep is not None:
title = "Voltage histogram for time step {}".format(timestep)
if len(timestep) == 1:
title = "Voltage histogram for time step {}".format(
timestep[0])
else:
title = "Voltage histogram \nfor time steps {} to {}".format(
data.index[0], data.index[-1])
timestep[0], timestep[-1])
elif title is False:
title = None
plots.histogram(data=data, title=title, timeindex=timestep, **kwargs)
Expand All @@ -234,15 +243,17 @@ def histogram_relative_line_load(self, timestep=None, title=True,
"""
Plots histogram of relative line loads.
For more information see :func:`edisgo.tools.plots.histogram`.
For more information on how the relative line load is calculated see
:func:`edisgo.tools.tools.get_line_loading_from_network`.
For more information on the histogram plot and possible configurations
see :func:`edisgo.tools.plots.histogram`.
Parameters
----------
Parameters
----------
timestep : :pandas:`pandas.Timestamp<timestamp>` or None, optional
Specifies time step histogram is plotted for. If timestep is None
all time steps voltages are calculated for are used. Default: None.
timestep : :pandas:`pandas.Timestamp<timestamp>` or list(:pandas:`pandas.Timestamp<timestamp>`) or None, optional
Specifies time step(s) histogram is plotted for. If `timestep` is
None all time steps currents are calculated for are used.
Default: None.
title : :obj:`str` or :obj:`bool`, optional
Title for plot. If True title is auto generated. If False plot has
no title. If :obj:`str`, the provided title is used. Default: True.
Expand All @@ -252,20 +263,6 @@ def histogram_relative_line_load(self, timestep=None, title=True,
fallback option in case of wrong input. Default: 'mv_lv'
"""
residual_load = tools.get_residual_load_from_pypsa_network(
self.network.pypsa)
case = residual_load.apply(
lambda _: 'feedin_case' if _ < 0 else 'load_case')
if timestep is not None:
timeindex = [timestep]
else:
timeindex = self.network.results.s_res().index
load_factor = pd.DataFrame(
data={'s_nom': [float(self.network.config[
'grid_expansion_load_factors'][
'mv_{}_line'.format(case.loc[_])])
for _ in timeindex]},
index=timeindex)
if voltage_level == 'mv':
lines = self.network.pypsa.lines.loc[
self.network.pypsa.lines.v_nom > 1]
Expand All @@ -274,24 +271,28 @@ def histogram_relative_line_load(self, timestep=None, title=True,
self.network.pypsa.lines.v_nom < 1]
else:
lines = self.network.pypsa.lines
s_res = self.network.results.s_res().loc[
timeindex, lines.index]
# get allowed line load
s_allowed = load_factor.dot(
self.network.pypsa.lines.s_nom.to_frame().T * 1e3)
# get line load from pf
data = s_res.divide(s_allowed)

rel_line_loading = tools.calculate_relative_line_load(
self.network.pypsa, self.network.config,
self.network.results.i_res, self.network.pypsa.lines.v_nom,
lines.index, timestep)

if timestep is None:
timestep = rel_line_loading.index
# check if timesteps is array-like, otherwise convert to list
if not hasattr(timestep, "__len__"):
timestep = [timestep]

if title is True:
if timestep is not None:
if len(timestep) == 1:
title = "Relative line load histogram for time step {}".format(
timestep)
timestep[0])
else:
title = "Relative line load histogram \nfor time steps " \
"{} to {}".format(data.index[0], data.index[-1])
"{} to {}".format(timestep[0], timestep[-1])
elif title is False:
title = None
plots.histogram(data=data, title=title, **kwargs)
plots.histogram(data=rel_line_loading, title=title, **kwargs)


class EDisGo(EDisGoReimport):
Expand Down Expand Up @@ -2939,7 +2940,7 @@ def s_res(self, components=None):

def v_res(self, nodes=None, level=None):
"""
Get resulting voltage level at node.
Get voltage results (in p.u.) from power flow analysis.
Parameters
----------
Expand Down
76 changes: 38 additions & 38 deletions edisgo/tools/plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ def histogram(data, **kwargs):
Data to be plotted, e.g. voltage or current (`v_res` or `i_res` from
:class:`edisgo.grid.network.Results`). Index of the dataframe must be
a :pandas:`pandas.DatetimeIndex<datetimeindex>`.
timeindex : :pandas:`pandas.Timestamp<timestamp>` or None, optional
Specifies time step histogram is plotted for. If timeindex is None all
time steps provided in dataframe are used. Default: None.
timeindex : :pandas:`pandas.Timestamp<timestamp>` or list(:pandas:`pandas.Timestamp<timestamp>`) or None, optional
Specifies time steps histogram is plotted for. If timeindex is None all
time steps provided in `data` are used. Default: None.
directory : :obj:`str` or None, optional
Path to directory the plot is saved to. Is created if it does not
exist. Default: None.
Expand Down Expand Up @@ -78,6 +78,12 @@ def histogram(data, **kwargs):
"""
timeindex = kwargs.get('timeindex', None)
if timeindex is None:
timeindex = data.index
# check if timesteps is array-like, otherwise convert to list
if not hasattr(timeindex, "__len__"):
timeindex = [timeindex]

directory = kwargs.get('directory', None)
filename = kwargs.get('filename', None)
title = kwargs.get('title', "")
Expand All @@ -102,10 +108,7 @@ def histogram(data, **kwargs):
except:
fig_size = standard_sizes['a5landscape']

if timeindex is not None:
plot_data = data.loc[timeindex, :]
else:
plot_data = data.T.stack()
plot_data = data.loc[timeindex, :].T.stack()

if binwidth is not None:
if x_limits is not None:
Expand Down Expand Up @@ -217,7 +220,7 @@ def mv_grid_topology(pypsa_network, configs, timestep=None,
Time step to plot analysis results for. If `timestep` is None maximum
line load and if given, maximum voltage deviation, is used. In that
case arrows cannot be drawn. Default: None.
line_color : :obj:`str`
line_color : :obj:`str` or None
Defines whereby to choose line colors (and implicitly size). Possible
options are:
Expand All @@ -234,7 +237,7 @@ def mv_grid_topology(pypsa_network, configs, timestep=None,
Lines are plotted in black. Is also the fallback option in case of
wrong input.
node_color : :obj:`str`
node_color : :obj:`str` or None
Defines whereby to choose node colors (and implicitly size). Possible
options are:
Expand All @@ -251,13 +254,16 @@ def mv_grid_topology(pypsa_network, configs, timestep=None,
Nodes are not plotted. Is also the fallback option in case of wrong
input.
line_load : :pandas:`pandas.DataFrame<dataframe>`
line_load : :pandas:`pandas.DataFrame<dataframe>` or None
Dataframe with current results from power flow analysis in A. Index of
the dataframe is a :pandas:`pandas.DatetimeIndex<datetimeindex>`,
columns are the line representatives.
grid_expansion_costs : :pandas:`pandas.DataFrame<dataframe>`
columns are the line representatives. Only needs to be provided when
parameter `line_color` is set to 'loading'. Default: None.
grid_expansion_costs : :pandas:`pandas.DataFrame<dataframe>` or None
Dataframe with grid expansion costs in kEUR. See `grid_expansion_costs`
in :class:`~.grid.network.Results` for more information.
in :class:`~.grid.network.Results` for more information. Only needs to
be provided when parameter `line_color` is set to 'expansion_costs'.
Default: None.
filename : :obj:`str`
Filename to save plot under. If not provided, figure is shown directly.
Default: None.
Expand All @@ -266,12 +272,25 @@ def mv_grid_topology(pypsa_network, configs, timestep=None,
only work when `line_color` option 'loading' is used and a time step
is given.
Default: False.
grid_district_geom : :obj:`Boolean`
If True grid district polygon is plotted in the background. This also
requires the geopandas package to be installed. Default: True.
background_map : :obj:`Boolean`
If True map is drawn in the background. This also requires the
contextily package to be installed. Default: True.
voltage : :pandas:`pandas.DataFrame<dataframe>`
Dataframe with voltage results from power flow analysis in p.u.. Index
of the dataframe is a :pandas:`pandas.DatetimeIndex<datetimeindex>`,
columns are the bus representatives. Only needs to be provided when
parameter `node_color` is set to 'voltage'. Default: None.
limits_cb_lines : :obj:`tuple`
Tuple with limits for colorbar of line color. First entry is the
minimum and second entry the maximum value. Default: None.
minimum and second entry the maximum value. Only needs to be provided
when parameter `line_color` is not None. Default: None.
limits_cb_nodes : :obj:`tuple`
Tuple with limits for colorbar of nodes. First entry is the
minimum and second entry the maximum value. Default: None.
minimum and second entry the maximum value. Only needs to be provided
when parameter `node_color` is not None. Default: None.
xlim : :obj:`tuple`
Limits of x-axis. Default: None.
ylim : :obj:`tuple`
Expand Down Expand Up @@ -412,7 +431,7 @@ def nodes_by_costs(buses, grid_expansion_costs):

# create pypsa network only containing MV buses and lines
pypsa_plot = PyPSANetwork()
pypsa_plot.buses = pypsa_network.buses.loc[pypsa_network.buses.v_nom >= 10]
pypsa_plot.buses = pypsa_network.buses.loc[pypsa_network.buses.v_nom > 1]
# filter buses of aggregated loads and generators
pypsa_plot.buses = pypsa_plot.buses[
~pypsa_plot.buses.index.str.contains("agg")]
Expand All @@ -422,28 +441,9 @@ def nodes_by_costs(buses, grid_expansion_costs):

# line colors
if line_color == 'loading':
# calculate relative line loading
# get load factor
residual_load = tools.get_residual_load_from_pypsa_network(
pypsa_network)
case = residual_load.apply(
lambda _: 'feedin_case' if _ < 0 else 'load_case')
if timestep is not None:
timeindex = [timestep]
else:
timeindex = line_load.index
load_factor = pd.DataFrame(
data={'s_nom': [float(configs[
'grid_expansion_load_factors'][
'mv_{}_line'.format(case.loc[_])])
for _ in timeindex]},
index=timeindex)
# get allowed line load
s_allowed = load_factor.dot(
pypsa_plot.lines.s_nom.to_frame().T * 1e3)
# get line load from pf
line_colors = line_load.loc[:, pypsa_plot.lines.index].divide(
s_allowed).max()
line_colors = tools.calculate_relative_line_load(
pypsa_network, configs, line_load, pypsa_network.lines.v_nom,
pypsa_plot.lines.index, timestep).max()
elif line_color == 'expansion_costs':
node_color = 'expansion_costs'
line_costs = pypsa_plot.lines.join(
Expand Down
21 changes: 11 additions & 10 deletions edisgo/tools/pypsa_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,13 @@ def lv_to_pypsa(network):

line['type'].append("")
line['x'].append(
l['line'].type['L'] * omega / 1e3 * l['line'].length)
line['r'].append(l['line'].type['R'] * l['line'].length)
l['line'].type['L'] / l['line'].quantity * omega / 1e3 *
l['line'].length)
line['r'].append(l['line'].type['R'] / l['line'].quantity *
l['line'].length)
line['s_nom'].append(
sqrt(3) * l['line'].type['I_max_th'] * l['line'].type['U_n'] / 1e3)
sqrt(3) * l['line'].type['I_max_th'] * l['line'].type['U_n'] *
l['line'].quantity / 1e3)
line['length'].append(l['line'].length)

# create dataframe representing storages
Expand Down Expand Up @@ -1281,14 +1284,12 @@ def process_pfa_results(network, pypsa, timesteps):
list(lines_bus1.values()), :].copy()
bus1_v_mag_pu.index = list(lines_bus1.keys())

line_voltage_avg = 0.5 * (bus0_v_mag_pu.loc[:, timesteps] +
bus1_v_mag_pu.loc[:, timesteps])
# Get line current
network.results._i_res = np.hypot(
pypsa.lines_t['p0'], pypsa.lines_t['q0']).truediv(
pypsa.lines['v_nom'] * bus0_v_mag_pu.T,
axis='columns') / sqrt(3) * 1e3

# Get voltage levels at line (avg. of buses at both sides)
network.results._i_res = \
network.results.s_res()[pypsa.lines_t['q0'].columns].truediv(
pypsa.lines['v_nom'] * line_voltage_avg.T,
axis='columns') * sqrt(3)
# process results at nodes
generators_names = [repr(g) for g in network.mv_grid.generators]
generators_mapping = {v: k for k, v in
Expand Down

0 comments on commit c37bfb8

Please sign in to comment.