How to Simulate an Entire Day using Time Series Simulation in PandaPower
This post will demonstrate how to simulate network power flows using an entire day’s load profile using pandapower. This kind of simulation allows a network operator to see how their network will operate throughout a day as the load varies.
This is called a time series simulation in pandapower and we will use the IEEE 14 bus test system included with panadapower, with some small modifications, for this demonstration.
Load the Relevant Packages in Python
We will be using pandapower and its control, timeseries and datasources libraries in addition to numpy for building the load profile array and pandapower plotting as well as matplotlib for visualization.
import numpy as np import pandas as pd import pandapower as pp import pandapower.control as control import pandapower.networks as nw import pandapower.timeseries as timeseries from pandapower.timeseries.data_sources.frame_data import DFData import pandapower.plotting as plt import matplotlib.pyplot as mplt
Load and Display the IEEE 14 Bus Network
As stated before, we will be using the IEEE 14 bus network for this demonstration. Pandapower has it included as a built in network in its networks library imported above. We load and display the network using the following code:
net = nw.case14() plt.simple_plot(net, plot_loads=True, plot_gens=True)
This results in the image of the network below
Display the Network Info
We can display a summary of the network by just using the network’s name. This will give us a quick overview of the network and its elements.
net This pandapower network includes the following parameter tables: - bus (14 elements) - load (11 elements) - gen (4 elements) - shunt (1 element) - ext_grid (1 element) - line (15 elements) - trafo (5 elements) - poly_cost (5 elements) - controller (2 elements) - bus_geodata (14 elements) - output_writer (1 element) and the following results tables: - res_bus (14 elements) - res_line (15 elements) - res_trafo (5 elements) - res_ext_grid (1 element) - res_load (11 elements) - res_shunt (1 element) - res_gen (4 elements)
Configure the Network
We make some modifications to the network to establish a good starting point for this demonstration. This step is not strictly necessary but it helps to show how you can modify the network to represent what is desired.
We will be using a distributed slack for the powerflow so that each generator will automatically contribute to serving the load throughout the day.
net.gen.slack = True net.ext_grid['in_service'] = False net.gen['vm_pu'] = 1.045
Set the Number of Time Slices for the Simulation
For this demo we will be simulating a one day load profile with a resolution of one (1) hour, therefore we have 24 hours and so we need 24 time slices.
n_ts = 24
Create a Load Scaling Profile to Match the Desired Load Profile
The approach we will take here is to leave all loads with their default values and scale them at each time slice to achieve the desired profile. We will apply th he same scaling factor to all loads. Since we have 11 loads and 24 time slices we need a 24x11 array to store these scaling factors.
lsf = np.zeros([24,11]) l_indx = np.ones([1,11]) lsf_values = np.array([0.863508968609865, 0.833239910313901, 0.802410313901345, 0.74929932735426, 0.735566143497758, 0.743974215246637, 0.781109865470852, 0.830297085201794, 0.88887331838565, 0.964405829596413, 0.975756726457399, 0.996356502242152, 1, 0.982763452914798, 0.943806053811659, 0.916900224215247, 0.925308295964125, 0.943806053811659, 0.928531390134529, 0.944226457399103, 0.932455156950673, 0.876541479820628, 0.871776905829596, 0.821748878923767 ]) for i in range(n_ts): lsf[i,:] = l_indx * lsf_values[i]
Plot the Desired Load Profile
Plot the load profile scaling factors so we can see that the profile is as desired.
This creates the image of the load scaling profile shown below.
Create a Data Frame and Corresponding Data Source Object for Load Scaling Profile
In this step we create the pandas dataframe that will hold the scaling profile above for the loads. After the dataframe is created we use it to create a datasource to pass to pandapower to adjust the values on each timestep.
df = pd.DataFrame(lsf, index=list(range(n_ts)), columns=net.load.index) ds = DFData(df)
Create a Controller to Adjust the Load Scaling According to the Desired Profile
Pandapower uses controllers to adjust the desired system parameters during each timestep. Here we will create a controller to adjust the load scaling during each timestep according to the profile above.
const_load = control.ConstControl(net, element='load', element_index=net.load.index, variable='scaling', data_source=ds, profile_name=net.load.index)
Create a Output Data Writer to Write the Time Series Results to Corresponding Files
In this step we will setup a writer to write the result of the timestep simulation to a file. We set the output patch and the file type. In this case we save results as excel files in a folder called ‘case14bus’.
ow = timeseries.OutputWriter(net, output_path="./case14bus", output_file_type=".xlsx")
Configure which Variables to Save
Now that we have our writer to write the results, we need to tell it what results to write. For this demo we will save the per unit bus voltages, the line loadings in percentage, the output MW of the generators and the sum of the load MW. We do this with the following code:
ow.log_variable('res_bus', 'vm_pu') ow.log_variable('res_line', 'loading_percent') ow.log_variable('res_gen', 'p_mw') ow.log_variable('res_load', 'p_mw', eval_function=lambda x: x.sum(0))
Run the Time Series Simulation
Now we can run the actual time series simulation. This will run the time series simulation and save the results in the format and to the folder specified above by the output writer.
Read the Result Files and Plot the Results
In this step we read the saved files and plot them for a quick visualization of how the saved parameters varied throughout the simulation, in our case a day.
total_load = pd.read_excel('case14bus/res_load/p_mw.xlsx', index_col=0) total_load.plot(legend=False, title='Total Load')
This creates the following plot of the actual load profile for the day:
Now we can also plot the output of the generators for the day as well. We do that with the following code:
gen = pd.read_excel('case14bus/res_gen/p_mw.xlsx', index_col=0) ax = gen.plot(title='Generator Output (MW)') ax.set_xlabel('time') ax.set_ylabel('output (MW)') ax.legend(bbox_to_anchor=(1,1))
Giving the following plots:
The same cn be done for the bus voltages and line loadings. For the bus voltages, we use the following code:
bus_vpu = pd.read_excel('case14bus/res_bus/vm_pu.xlsx', index_col=0) ax = bus_vpu.plot(title='Bus Voltages (pu)') ax.set_xlabel('time') ax.set_ylabel('voltage (pu)') ax.legend(bbox_to_anchor=(1,1))
Which produces the following plots:
For the line loadings we will demonstrate how you could also plot the results without reading in the output files but by using the output writer itself. We do this as shown below:
ax = (ow.output['res_line.loading_percent']*100).plot() ax.set_title('Line Loading (%)') ax.set_xlabel('time') ax.set_ylabel('loading') ax.legend(bbox_to_anchor=(1,1))
That's it guys. Now you can simulate an entire day of power flows using pandapower.
Don't forget to share this article with anyone you think would find it interesting or could benefit from it.
Happy coding :)