Time series example with advanced output writer usage

This tutorial shows how specific results are extracted with the time series module in pandapower.

In this example we define the output writer to log the following:

  • The maximum voltage of each medium voltage bus (vn_kv > 1.0 kV and vn_kv < 70.0) in an example grid
  • The sum of all p_kw values for every high voltage bus (vn_kv > 70.0 and vn_kv < 380.0)

This time series calculation requires the minimum following inputs:

  • pandapower net
  • the time series (a Dataframe for example)
  • a pre defined output writer

If you have just read the simple time_series jupyter notebook example. You can directly proceed to the section of create_output_writer()

First we need some imports. Specific for this example are:

  • ConstControl -> "constant" controllers, which change the P and Q values of sgens and loads
  • DFData -> The Dataframe Datasource. This Dataframe holds the time series to be calculated
  • OutputWriter -> The output writer, which is required to write the outputs to the hard disk
  • run_timeseries -> the "main" time series function, which basically calls the controller functions (to update the P, Q of the ConstControllers) and runpp.
In [1]:
import os
import numpy as np
import pandas as pd
import tempfile
import pandapower as pp
from pandapower.control import ConstControl
from pandapower.timeseries import DFData
from pandapower.timeseries import OutputWriter
from pandapower.timeseries.run_time_series import run_timeseries

First we look at the time series example function. It follows these steps:

  1. create a simple test net
  2. create the datasource (which contains the time series P values)
  3. create the controllers to update the P values of the load and the sgen
  4. define the output writer and desired variables to be saved
  5. call the main time series function to calculate the desired results
In [2]:
def timeseries_example(output_dir):
    # 1. create test net
    net = simple_test_net()

    # 2. create (random) data source
    n_timesteps = 10
    profiles, ds = create_data_source(n_timesteps)
    # 3. create controllers (to control P values of the load and the sgen)
    net = create_controllers(net, ds)

    # time steps to be calculated. Could also be a list with non-consecutive time steps
    time_steps = range(0, n_timesteps)

    # 4. the output writer with the desired results to be stored to files.
    ow = create_output_writer(net, time_steps, output_dir)

    # 5. the main time series function
    run_timeseries(net, time_steps, output_writer=ow)

We start by creating a simple example pandapower net consinsting of five buses, a transfomer, three lines, a load and a sgen.

In [3]:
def simple_test_net():
    """
    simple net that looks like:

    ext_grid b0---b1 trafo(110/20) b2----b3 load
                                    |
                                    |
                                    b4 sgen
    """
    net = pp.create_empty_network()
    pp.set_user_pf_options(net, init_vm_pu = "flat", init_va_degree = "dc", calculate_voltage_angles=True)

    b0 = pp.create_bus(net, 110)
    b1 = pp.create_bus(net, 110)
    b2 = pp.create_bus(net, 20)
    b3 = pp.create_bus(net, 20)
    b4 = pp.create_bus(net, 20)

    pp.create_ext_grid(net, b0)
    pp.create_line(net, b0, b1, 10, "149-AL1/24-ST1A 110.0")
    pp.create_transformer(net, b1, b2, "25 MVA 110/20 kV", name='tr1')
    pp.create_line(net, b2, b3, 10, "184-AL1/30-ST1A 20.0")
    pp.create_line(net, b2, b4, 10, "184-AL1/30-ST1A 20.0")

    pp.create_load(net, b2, p_mw=20., q_mvar=10., name='load1')
    pp.create_sgen(net, b4, p_mw=20., q_mvar=0.15, name='sgen1')

    return net

The data source is a simple pandas DataFrame. It contains random values for the load and the sgen P values ("profiles"). Of course your time series values should be loaded from a file later on. Note that the profiles are identified by their column name ("load1_p", "sgen1_p"). You can choose here whatever you prefer. The DFData(profiles) converts the Dataframe to the required format for the controllers. Note that the controller

In [4]:
def create_data_source(n_timesteps=10):
    profiles = pd.DataFrame()
    profiles['load1_p'] = np.random.random(n_timesteps) * 20.
    profiles['sgen1_p'] = np.random.random(n_timesteps) * 20.

    ds = DFData(profiles)

    return profiles, ds

create the controllers by telling the function which element_index belongs to which profile. In this case we map:

  • first load in dataframe (element_index=[0]) to the profile_name "load1_p"
  • first sgen in dataframe (element_index=[0]) to the profile_name "sgen1_p"
In [5]:
def create_controllers(net, ds):
    ConstControl(net, element='load', variable='p_mw', element_index=[0],
                 data_source=ds, profile_name=["load1_p"])
    ConstControl(net, element='sgen', variable='p_mw', element_index=[0],
                 data_source=ds, profile_name=["sgen1_p"])
    return net

create the output writer. Instead of saving the whole net (which takes a lot of time), we extract only pre defined outputs. In this case we:

  • save the results to the folder output_dir
  • write the results to ".xls" Excel files. (Possible are: .json, .p, .csv - You should avoid Excel since it is slow)
  • log sum of real power ("p_mw") for each high voltage (hv) bus
  • log the maximum voltage magnitude ("vm_pu") of each medium voltage (mv) bus
In [6]:
def create_output_writer(net, time_steps, output_dir):
    ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=".xls")
    
    # create a mask to get the indices of all the hv buses in the grid 
    mask_hv_buses = (net.bus.vn_kv > 70.0) & (net.bus.vn_kv < 380.0)
    hv_busses_index = net.bus.loc[mask_hv_buses].index
    # create a mask to get the indices of all the mv buses in the grid
    mask_mv_buses = (net.bus.vn_kv > 1.0) & (net.bus.vn_kv < 70.0)
    mv_busses_index = net.bus.loc[mask_mv_buses].index
    # now define the output writer, so that it gets the indices and specify the evaluation functions
    # since we want the maximum voltage of all mv buses, we provide the indices of the mv buses and the minimum 
    # function np.min. The variable "eval_name" is free to chose and contains the name of the column in
    # which the results are saved. 
    ow.log_variable('res_bus', 'p_mw', index=hv_busses_index, eval_function=np.sum, eval_name="hv_bus_sum_p")
    ow.log_variable('res_bus', 'vm_pu', index=mv_busses_index, eval_function=np.max, eval_name="mv_bus_max")
    return ow

Now lets execude the code.

In [7]:
output_dir = os.path.join(tempfile.gettempdir(), "time_series_example")
print("Results can be found in your local temp folder: {}".format(output_dir))
if not os.path.exists(output_dir):
    os.mkdir(output_dir)
timeseries_example(output_dir)
Results can be found in your local temp folder: /tmp/time_series_example
Progress: |██████████████████████████████████████████████████| 100.0% Complete

If everything works you should have the desired results the temporary folder of your os (see print statement above). In this folder two excel files should have appared containing the desired output for each of the ten time steps

Plot results

Now let us plot the results

In [8]:
import matplotlib.pyplot as plt
%matplotlib inline  

# voltage results
vm_pu_file = os.path.join(output_dir, "res_bus", "vm_pu.xls")
vm_pu = pd.read_excel(vm_pu_file)
vm_pu.plot(label="vm_pu")
plt.xlabel("time step")
plt.ylabel("voltage mag. [p.u.]")
plt.title("Voltage Magnitude")
plt.grid()
plt.show()


# p_mw results
p_mw_file = os.path.join(output_dir, "res_bus", "p_mw.xls")
p_mw = pd.read_excel(p_mw_file)
p_mw.plot(label="p_mw")
plt.xlabel("time step")
plt.ylabel("P [MW]")
plt.title("Real Power at Buses")
plt.grid()
plt.show()