Usage details

As eDisGo is designed to serve as a toolbox, it provides several methods to analyze distribution grids for grid issues and to evaluate measures responding these. Below, we give a detailed introduction to the data structure and to how different features can be used.

The fundamental data structure

It’s worth to understand how the fundamental data structure of eDisGo is designed in order to make use of its entire features.

The class EDisGo serves as the top-level API for setting up your scenario, invocation of data import, analysis of hosting capacity, grid reinforcement and flexibility measures. It also provides access to all relevant data. Grid data is stored in the Topology class. Time series data can be found in the TimeSeries class. Results data holding results e.g. from the power flow analysis and grid expansion is stored in the Results class. Configuration data from the config files (see Default configuration data) is stored in the Config class. All these can be accessed through the EDisGo object. In the following code examples edisgo constitues an EDisGo object.

# Access Topology grid data container object
edisgo.topology

# Access TimeSeries data container object
edisgo.timeseries

# Access Results data container object
edisgo.results

# Access configuration data container object
edisgo.config

Grid data is stored in pandas.DataFrames in the Topology object. There are extra data frames for all grid elements (buses, lines, switches, transformers), as well as generators, loads and storage units. You can access those dataframes as follows:

# Access all buses in MV grid and underlying LV grids
edisgo.topology.buses_df

# Access all lines in MV grid and underlying LV grids
edisgo.topology.lines_df

# Access all MV/LV transformers
edisgo.topology.transformers_df

# Access all HV/MV transformers
edisgo.topology.transformers_hvmv_df

# Access all switches in MV grid and underlying LV grids
edisgo.topology.switches_df

# Access all generators in MV grid and underlying LV grids
edisgo.topology.generators_df

# Access all loads in MV grid and underlying LV grids
edisgo.topology.loads_df

# Access all storage units in MV grid and underlying LV grids
edisgo.topology.storage_units_df

The grids can also be accessed individually. The MV grid is stored in an MVGrid object and each LV grid in an LVGrid object. The MV grid topology can be accessed through

# Access MV grid
edisgo.topology.mv_grid

Its components can be accessed analog to those of the whole grid topology as shown above.

# Access all buses in MV grid
edisgo.topology.mv_grid.buses_df

# Access all generators in MV grid
edisgo.topology.mv_grid.generators_df

A list of all LV grids can be retrieved through:

# Get list of all underlying LV grids
# (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)

Access to a single LV grid’s components can be obtained analog to shown above for the whole topology and the MV grid:

# Get single LV grid
lv_grid = list(edisgo.topology.mv_grid.lv_grids)[0]

# Access all buses in that LV grid
lv_grid.buses_df

# Access all loads in that LV grid
lv_grid.loads_df

A single grid’s generators, loads, storage units and switches can also be retrieved as Generator, Load, Storage, and Switch objects, respecitvely:

# Get all switch disconnectors in MV grid as Switch objects
# (Note that objects are returned as a python generator object that must
#  first be converted to a list in order to view the Switch objects)
list(edisgo.topology.mv_grid.switch_disconnectors)

# Get all generators in LV grid as Generator objects
list(lv_grid.generators)

For some applications it is helpful to get a graph representation of the grid, e.g. to find the path from the station to a generator. The graph representation of the whole topology or each single grid can be retrieved as follows:

# Get graph representation of whole topology
edisgo.to_graph()

# Get graph representation for MV grid
edisgo.topology.mv_grid.graph

# Get graph representation for LV grid
lv_grid.graph

The returned graph is a networkx.Graph, where lines are represented by edges in the graph, and buses and transformers are represented by nodes.

Identify grid issues

As detailed in A minimum working example, once you set up your scenario by instantiating an EDisGo object, you are ready for a grid analysis and identifying grid issues (line overloading and voltage issues) using analyze():

# Do non-linear power flow analysis for MV and LV grid
edisgo.analyze()

The analyze function conducts a non-linear power flow using PyPSA.

The range of time analyzed by the power flow analysis is by default defined by the timeindex(), that can be given as an input to the EDisGo object through the parameter timeindex or is otherwise set automatically. If you want to change the time steps that are analyzed, you can specify those through the parameter timesteps of the analyze function. Make sure that the specified time steps are a subset of timeindex().

Grid expansion

Grid expansion can be invoked by reinforce():

# Reinforce grid due to overloading and overvoltage issues
edisgo.reinforce()

You can further specify e.g. if to conduct a combined analysis for MV and LV (regarding allowed voltage deviations) or if to only calculate grid expansion needs without changing the topology of the graph. See reinforce_grid() for more information.

Costs for the grid expansion measures can be obtained as follows:

# Get costs of grid expansion
costs = edisgo.results.grid_expansion_costs

Further information on the grid reinforcement methodology can be found in section Grid expansion.

Battery storage systems

Battery storage systems can be integrated into the grid as an alternative to classical grid expansion. The storage integration heuristic described in section Storage integration is not available at the moment. Instead, you may either integrate a storage unit at a specified bus manually or use the optimal power flow to optimally distribute a given storage capacity in the grid.

Here are two small examples on how to integrate a storage unit manually. In the first one, the EDisGo object is set up for a worst-case analysis, wherefore no time series needs to be provided for the storage unit, as worst-case definition is used. In the second example, a time series analysis is conducted, wherefore a time series for the storage unit needs to be provided.

from edisgo import EDisGo

# Set up EDisGo object
edisgo = EDisGo(ding0_grid=dingo_grid_path,
                worst_case_analysis='worst-case')

# Get random bus to connect storage to
random_bus = edisgo.topology.buses_df.index[3]
# Add storage instance
edisgo.add_component(
    "StorageUnit",
    bus=random_bus,
    p_nom=4)
import pandas as pd
from edisgo import EDisGo

# Set up the EDisGo object using the OpenEnergy DataBase and the oemof
# demandlib to set up time series for loads and fluctuating generators
# (time series for dispatchable generators need to be provided)
timeindex = pd.date_range('1/1/2011', periods=4, freq='H')
timeseries_generation_dispatchable = pd.DataFrame(
    {'biomass': [1] * len(timeindex),
     'coal': [1] * len(timeindex),
     'other': [1] * len(timeindex)
     },
    index=timeindex)
edisgo = EDisGo(
    ding0_grid='ding0_example_grid',
    generator_scenario='ego100',
    timeseries_load='demandlib',
    timeseries_generation_fluctuating='oedb',
    timeseries_generation_dispatchable=timeseries_generation_dispatchable,
    timeindex=timeindex)

# Get random bus to connect storage to
random_bus = edisgo.topology.buses_df.index[3]
# Add storage instance
edisgo.add_component(
    "StorageUnit",
    bus=random_bus,
    p_nom=4,
    ts_active_power=pd.Series(
        [-3.4, 2.5, -3.4, 2.5],
        index=edisgo.timeseries.timeindex))

Following is an example on how to use the OPF to find the optimal storage positions in the grid with regard to grid expansion costs. Storage operation is optimized at the same time. The example uses the same EDisGo instance as above. A total storage capacity of 10 MW is distributed in the grid. storage_buses can be used to specify certain buses storage units may be connected to. This does not need to be provided but will speed up the optimization.

random_bus = edisgo.topology.buses_df.index[3:13]
edisgo.perform_mp_opf(
    timesteps=period,
    scenario="storage",
    storage_units=True,
    storage_buses=busnames,
    total_storage_capacity=10.0,
    results_path=results_path)

Curtailment

The curtailment function is used to spatially distribute the power that is to be curtailed. The two heuristics feedin-proportional and voltage-based, in detail explained in section Curtailment, are currently not available. Instead you may use the optimal power flow to find the optimal generator curtailment with regard to minimizing grid expansion costs for given curtailment requirements. The following example again uses the EDisGo object from above.

edisgo.perform_mp_opf(
    timesteps=period,
    scenario='curtailment',
    results_path=results_path,
    curtailment_requirement=True,
    curtailment_requirement_series=[10, 20, 15, 0])

Plots

EDisGo provides a bunch of predefined plots to e.g. plot the MV grid topology, line loading and node voltages in the MV grid or as a histograms.

# plot MV grid topology on a map
edisgo.plot_mv_grid_topology()

# plot grid expansion costs for lines in the MV grid and stations on a map
edisgo.plot_mv_grid_expansion_costs()

# plot voltage histogram
edisgo.histogram_voltage()

See EDisGo class for more plots and plotting options.

Results

Results such as voltages at nodes and line loading from the power flow analysis as well as grid expansion costs are provided through the Results class and can be accessed the following way:

edisgo.results

Get voltages at nodes from v_res() and line loading from s_res() or i_res. equipment_changes holds details about measures performed during grid expansion. Associated costs can be obtained through grid_expansion_costs. Flexibility measures may not entirely resolve all issues. These unresolved issues are listed in unresolved_issues.

Results can be saved to csv files with:

edisgo.results.save('path/to/results/directory/')

See save() for more information.