Create a grid and run a time series power flow
In this example we are going to create a a 5-bus grid from scratch, create fictitious load and generation profiles and then run a power flow and a time series power flow. Finally the results will be stored in an excel file.
import numpy as np
import pandas as pd
import datetime as dt
from GridCal.Engine import *
####################################################################################################################
# Define the circuit
#
# A circuit contains all the grid information regardless of the islands formed or the amount of devices
####################################################################################################################
# create a circuit
grid = MultiCircuit(name='lynn 5 bus')
# let's create a master profile
date0 = dt.datetime(2021, 1, 1)
time_array = pd.DatetimeIndex([date0 + dt.timedelta(hours=i) for i in range(26)])
x = np.linspace(-np.pi, np.pi, len(time_array))
y = np.abs(np.sin(x))
df_0 = pd.DataFrame(data=y, index=time_array) # complex values
# set the grid master time profile
grid.time_profile = df_0.index
####################################################################################################################
# Define the buses
####################################################################################################################
# I will define this bus with all the properties so you see
bus1 = Bus(name='Bus1',
vnom=10, # Nominal voltage in kV
vmin=0.9, # Bus minimum voltage in per unit
vmax=1.1, # Bus maximum voltage in per unit
xpos=0, # Bus x position in pixels
ypos=0, # Bus y position in pixels
height=0, # Bus height in pixels
width=0, # Bus width in pixels
active=True, # Is the bus active?
is_slack=False, # Is this bus a slack bus?
area='Defualt', # Area (for grouping purposes only)
zone='Default', # Zone (for grouping purposes only)
substation='Default' # Substation (for grouping purposes only)
)
# the rest of the buses are defined with the default parameters
bus2 = Bus(name='Bus2')
bus3 = Bus(name='Bus3')
bus4 = Bus(name='Bus4')
bus5 = Bus(name='Bus5')
# add the bus objects to the circuit
grid.add_bus(bus1)
grid.add_bus(bus2)
grid.add_bus(bus3)
grid.add_bus(bus4)
grid.add_bus(bus5)
####################################################################################################################
# Add the loads
####################################################################################################################
# In GridCal, the loads, generators ect are stored within each bus object:
# we'll define the first load completely
l2 = Load(name='Load',
G=0, B=0, # admittance of the ZIP model in MVA at the nominal voltage
Ir=0, Ii=0, # Current of the ZIP model in MVA at the nominal voltage
P=40, Q=20, # Power of the ZIP model in MVA
active=True, # Is active?
mttf=0.0, # Mean time to failure
mttr=0.0 # Mean time to recovery
)
grid.add_load(bus2, l2)
# Define the others with the default parameters
grid.add_load(bus3, Load(P=25, Q=15))
grid.add_load(bus4, Load(P=40, Q=20))
grid.add_load(bus5, Load(P=50, Q=20))
####################################################################################################################
# Add the generators
####################################################################################################################
g1 = Generator(name='gen',
active_power=0.0, # Active power in MW, since this generator is used to set the slack , is 0
voltage_module=1.0, # Voltage set point to control
Qmin=-9999, # minimum reactive power in MVAr
Qmax=9999, # Maximum reactive power in MVAr
Snom=9999, # Nominal power in MVA
power_prof=None, # power profile
vset_prof=None, # voltage set point profile
active=True # Is active?
)
grid.add_generator(bus1, g1)
####################################################################################################################
# Add the lines
####################################################################################################################
br1 = Branch(bus_from=bus1,
bus_to=bus2,
name='Line 1-2',
r=0.05, # resistance of the pi model in per unit
x=0.11, # reactance of the pi model in per unit
g=1e-20, # conductance of the pi model in per unit
b=0.02, # susceptance of the pi model in per unit
rate=50, # Rate in MVA
tap=1.0, # Tap value (value close to 1)
shift_angle=0, # Tap angle in radians
active=True, # is the branch active?
mttf=0, # Mean time to failure
mttr=0, # Mean time to recovery
branch_type=BranchType.Line, # Branch type tag
length=1, # Length in km (to be used with templates)
template=BranchTemplate() # Branch template (The default one is void)
)
grid.add_branch(br1)
grid.add_branch(Branch(bus1, bus3, name='Line 1-3', r=0.05, x=0.11, b=0.02, rate=50))
grid.add_branch(Branch(bus1, bus5, name='Line 1-5', r=0.03, x=0.08, b=0.02, rate=80))
grid.add_branch(Branch(bus2, bus3, name='Line 2-3', r=0.04, x=0.09, b=0.02, rate=3))
grid.add_branch(Branch(bus2, bus5, name='Line 2-5', r=0.04, x=0.09, b=0.02, rate=10))
grid.add_branch(Branch(bus3, bus4, name='Line 3-4', r=0.06, x=0.13, b=0.03, rate=30))
grid.add_branch(Branch(bus4, bus5, name='Line 4-5', r=0.04, x=0.09, b=0.02, rate=30))
####################################################################################################################
# Overwrite the default profiles with the custom ones
####################################################################################################################
for load in grid.get_loads():
load.P_prof = load.P * df_0.values[:, 0]
load.Q_prof = load.Q * df_0.values[:, 0]
for gen in grid.get_static_generators():
gen.P_prof = gen.Q * df_0.values[:, 0]
gen.Q_prof = gen.Q * df_0.values[:, 0]
for gen in grid.get_generators():
gen.P_prof = gen.P * df_0.values[:, 0]
####################################################################################################################
# Run a power flow simulation
####################################################################################################################
# We need to specify power flow options
pf_options = PowerFlowOptions(solver_type=SolverType.NR, # Base method to use
verbose=False, # Verbose option where available
tolerance=1e-6, # power error in p.u.
max_iter=25, # maximum iteration number
control_q=True # if to control the reactive power
)
# Declare and execute the power flow simulation
pf = PowerFlowDriver(grid, pf_options)
pf.run()
writer = pd.ExcelWriter('Results.xlsx')
# now, let's compose a nice DataFrame with the voltage results
headers = ['Vm (p.u.)', 'Va (Deg)', 'Vre', 'Vim']
Vm = np.abs(pf.results.voltage)
Va = np.angle(pf.results.voltage, deg=True)
Vre = pf.results.voltage.real
Vim = pf.results.voltage.imag
data = np.c_[Vm, Va, Vre, Vim]
v_df = pd.DataFrame(data=data, columns=headers, index=grid.bus_names)
# print('\n', v_df)
v_df.to_excel(writer, sheet_name='V')
# Let's do the same for the branch results
headers = ['Loading (%)', 'Power from (MVA)']
loading = np.abs(pf.results.loading) * 100
power = np.abs(pf.results.Sf)
data = np.c_[loading, power]
br_df = pd.DataFrame(data=data, columns=headers, index=grid.branch_names)
br_df.to_excel(writer, sheet_name='Br')
# Finally the execution metrics
print('\nError:', pf.results.error)
print('Elapsed time (s):', pf.results.elapsed, '\n')
####################################################################################################################
# Run a time series power flow simulation
####################################################################################################################
ts = TimeSeries(grid=grid,
options=pf_options,
opf_time_series_results=None,
start_=0,
end_=None)
ts.run()
print()
print('-' * 200)
print('Time series')
print('-' * 200)
print('Voltage time series')
df_voltage = pd.DataFrame(data=np.abs(ts.results.voltage), columns=grid.bus_names, index=grid.time_profile)
df_voltage.to_excel(writer, sheet_name='Vts')
print(df_voltage)
writer.close()
Output
Node voltage results
Buses |
Vm (p.u.) |
Va (Deg) |
Vre |
Vim |
Bus1 |
1 |
0 |
1 |
0 |
Bus2 |
0.955324031 |
-2.404433748 |
0.954482951 |
-0.04007868 |
Bus3 |
0.954837779 |
-2.363419791 |
0.954025558 |
-0.039375371 |
Bus4 |
0.933365539 |
-3.648173178 |
0.931474151 |
-0.059389693 |
Bus5 |
0.953415236 |
-2.688383579 |
0.952365912 |
-0.044718922 |
Branch results
Branches |
Loading (%) |
Power (MVA) |
Line 1-2 |
99.58136275 |
49.79068138 |
Line 1-3 |
99.36456725 |
49.68228363 |
Line 1-5 |
95.04116648 |
76.03293318 |
Line 2-3 |
55.46650524 |
1.663995158 |
Line 2-5 |
50.59527577 |
5.059527578 |
Line 3-4 |
65.51051038 |
19.65315312 |
Line 4-5 |
80.98428606 |
24.29528582 |
Time series voltage results
0 |
1 |
2 |
3 |
4 |
|
---|---|---|---|---|---|
2021-01-01 00:00:00 |
1 |
1.00381101279064 |
1.00424197191543 |
1.00531077229584 |
1.00378836624627 |
2021-01-01 01:00:00 |
1 |
0.992441588490943 |
0.992679964919525 |
0.988491902175231 |
0.992012794689127 |
2021-01-01 02:00:00 |
1 |
0.981398642072788 |
0.981437075141778 |
0.972128786670522 |
0.980554539794808 |
2021-01-01 03:00:00 |
1 |
0.971456488050421 |
0.971303527762746 |
0.957370335293388 |
0.970220247825554 |
2021-01-01 04:00:00 |
1 |
0.963385899999827 |
0.963069287324943 |
0.945369318569842 |
0.961818165699366 |
2021-01-01 05:00:00 |
1 |
0.957870378716229 |
0.957437491303093 |
0.937155965503371 |
0.956069094792859 |
2021-01-01 06:00:00 |
1 |
0.955409893841428 |
0.954923944571358 |
0.933488707758209 |
0.953502548649717 |
2021-01-01 07:00:00 |
1 |
0.956236695969423 |
0.955768660165017 |
0.934721255127363 |
0.954365121060115 |
2021-01-01 08:00:00 |
1 |
0.960272087492213 |
0.959890281812325 |
0.940733654608167 |
0.958573211183415 |
2021-01-01 09:00:00 |
1 |
0.967141368416695 |
0.966901852266799 |
0.950956173297673 |
0.965729383333107 |
2021-01-01 10:00:00 |
1 |
0.976240834713852 |
0.976181355516 |
0.964475719702551 |
0.975195498128214 |
2021-01-01 11:00:00 |
1 |
0.986831642580487 |
0.98697006969342 |
0.98018286030256 |
0.986194479159182 |
2021-01-01 12:00:00 |
1 |
0.998132174820727 |
0.998468584300177 |
0.996913301552821 |
0.997909328746612 |
2021-01-01 13:00:00 |
1 |
0.998132174820727 |
0.998468584300178 |
0.996913301552821 |
0.997909328746612 |
2021-01-01 14:00:00 |
1 |
0.986831642580487 |
0.986970069693419 |
0.98018286030256 |
0.986194479159182 |
2021-01-01 15:00:00 |
1 |
0.976240834713852 |
0.976181355516 |
0.964475719702551 |
0.975195498128214 |
2021-01-01 16:00:00 |
1 |
0.967141368416695 |
0.966901852266799 |
0.950956173297673 |
0.965729383333107 |
2021-01-01 17:00:00 |
1 |
0.960272087492213 |
0.959890281812325 |
0.940733654608167 |
0.958573211183415 |
2021-01-01 18:00:00 |
1 |
0.956236695969423 |
0.955768660165017 |
0.934721255127363 |
0.954365121060115 |
2021-01-01 19:00:00 |
1 |
0.955409893841428 |
0.954923944571358 |
0.933488707758209 |
0.953502548649717 |
2021-01-01 20:00:00 |
1 |
0.957870378716229 |
0.957437491303093 |
0.937155965503371 |
0.956069094792859 |
2021-01-01 21:00:00 |
1 |
0.963385899999827 |
0.963069287324943 |
0.945369318569842 |
0.961818165699366 |
2021-01-01 22:00:00 |
1 |
0.971456488050421 |
0.971303527762746 |
0.957370335293389 |
0.970220247825554 |
2021-01-01 23:00:00 |
1 |
0.981398642072788 |
0.981437075141778 |
0.972128786670522 |
0.980554539794808 |
2021-01-02 00:00:00 |
1 |
0.992441588490943 |
0.992679964919525 |
0.988491902175231 |
0.992012794689127 |
2021-01-02 01:00:00 |
1 |
1.00381101279064 |
1.00424197191543 |
1.00531077229584 |
1.00378836624627 |