IEEE39 Bus Tutorial - Part I: Model Creation
This is the first part of a four-part tutorial series for the IEEE 39-bus test system:
- Part I: Model Creation (this tutorial) - Build the network structure with buses, lines, and components
- Part II: Initialization - Perform power flow calculations and dynamic initialization
- Part III: Dynamic Simulation - Run time-domain simulations and analyze system behavior
- Part IV: Advanced Modeling & Parameter Optimization - Create custom components and optimize system parameters
In this first part, we'll construct the complete IEEE 39-bus network model using PowerDynamics.jl, including generators, loads, transmission lines, and control systems.
This script can be downloaded as a normal Julia script here.
System Structure
The system consists of 39 buses (with 10 generators and 19 loads) and 46 branches (12 of which are transformers).
The buses fall into the following categories:
- Junction: pure transient buses without dynamic components
- Load: buses with loads only
- Controlled Machine: buses with controlled machines (generators with AVR and GOV)
- Controlled Machine + Load: buses with controlled machines and loads
- Uncontrolled Machine + Load: buses with uncontrolled machines and loads
For the power flow solution, we have a slack bus, PV buses and PQ buses.
For the dynamic simulation, we will use the following models:
- ZIP Load for loads,
- 6th Order Sauer-Pai Machine and
- AVR Type I and TGOV1 for controlled machines.
Setup and Data Loading
As of now, PowerDynamics.jl does not support any advanced import mechanisms for power grids. Therefore, this tutorial loads the data from some custom CSV files.
First, we'll load the required packages and read the system data from CSV files. The IEEE 39-bus system data is organized into separate files for different components.
using PowerDynamics
using PowerDynamics.Library
using ModelingToolkit
using NetworkDynamics
using DataFrames
using CSV
DATA_DIR = joinpath(pkgdir(PowerDynamics), "docs", "examples", "ieee39data")
The system data is stored in CSV files containing:
bus.csv - Bus Configuration Data
Parameter | Description |
---|---|
bus | Bus number (unique identifier) |
bus_type | Power flow bus type: "PQ" (load), "PV" (generator), "Slack" (reference) |
category | Component category: "junction", "load", "ctrld_machine", "ctrld_machine_load", "unctrld_machine_load" |
P | Active power injection [pu] (positive = generation, negative = load) |
Q | Reactive power injection [pu] (positive = generation, negative = load) |
V | Voltage magnitude [pu] (for PV and Slack buses) |
base_kv | Base voltage level [kV] |
has_load | Boolean flag indicating presence of load component |
has_gen | Boolean flag indicating presence of generator component |
has_avr | Boolean flag indicating presence of automatic voltage regulator |
has_gov | Boolean flag indicating presence of turbine governor |
branch.csv - Transmission Line and Transformer Data
Parameter | Description |
---|---|
src_bus | Source bus number |
dst_bus | Destination bus number |
transformer | Transformer flag (0 = line, 1 = transformer) |
r_src | Source end transformation ratio [pu] |
R | Series resistance [pu] |
X | Series reactance [pu] |
G_src | Source end shunt conductance [pu] |
G_dst | Destination end shunt conductance [pu] |
B_src | Source end shunt susceptance [pu] |
B_dst | Destination end shunt susceptance [pu] |
load.csv - ZIP Load Model Parameters
Parameter | Description |
---|---|
bus | Bus number where load is connected |
Pset | Active power at operation point [pu] |
Qset | Reactive power at operation point [pu] |
KpZ | Active power constant impedance fraction |
KqZ | Reactive power constant impedance fraction |
KpI | Active power constant current fraction |
KqI | Reactive power constant current fraction |
KpC | Active power constant power fraction (1-KpZ-KpI) |
KqC | Reactive power constant power fraction (1-KqZ-KqI) |
Note: ZIP loads combine constant impedance (Z), constant current (I), and constant power (P) components.
machine.csv - Generator (Sauer-Pai Machine) Parameters
Parameter | Description |
---|---|
bus | Bus number where generator is connected |
Sn | Machine power rating [MVA] |
V_b | System voltage basis [kV] |
Vn | Machine voltage rating [kV] |
R_s | Stator resistance [pu] |
X_ls | Stator leakage reactance [pu] |
X_d | d-axis synchronous reactance [pu] |
X_q | q-axis synchronous reactance [pu] |
Xโฒ_d | d-axis transient reactance [pu] |
Xโฒ_q | q-axis transient reactance [pu] |
Xโณ_d | d-axis subtransient reactance [pu] |
Xโณ_q | q-axis subtransient reactance [pu] |
Tโฒ_d0 | d-axis transient time constant [s] |
Tโฒ_q0 | q-axis transient time constant [s] |
Tโณ_d0 | d-axis subtransient time constant [s] |
Tโณ_q0 | q-axis subtransient time constant [s] |
H | Inertia constant [s] |
D | Direct shaft damping coefficient |
avr.csv - Automatic Voltage Regulator (AVR Type I) Parameters
Parameter | Description |
---|---|
bus | Bus number where AVR-controlled generator is located |
Ka | Amplifier gain |
Ke | Field circuit integral deviation |
Kf | Stabilizer gain |
Ta | Amplifier time constant [s] |
Tf | Stabilizer time constant [s] |
Te | Field circuit time constant [s] |
Tr | Measurement time constant [s] |
vr_min | Minimum regulator voltage [pu] |
vr_max | Maximum regulator voltage [pu] |
E1 | First ceiling voltage [pu] |
Se1 | First ceiling saturation factor |
E2 | Second ceiling voltage [pu] |
Se2 | Second ceiling saturation factor |
gov.csv - Turbine Governor (TGOV1) Parameters
Parameter | Description |
---|---|
bus | Bus number where governor-controlled generator is located |
V_min | Minimum valve position [pu] |
V_max | Maximum valve position [pu] |
R | Governor droop [Machine PU] |
T1 | First transient time constant [s] |
T2 | Second transient time constant [s] |
T3 | Third transient time constant [s] |
DT | Turbine damping coefficient |
ฯ_ref | Reference frequency [pu] |
branch_df = CSV.read(joinpath(DATA_DIR, "branch.csv"), DataFrame)
bus_df = CSV.read(joinpath(DATA_DIR, "bus.csv"), DataFrame)
load_df = CSV.read(joinpath(DATA_DIR, "load.csv"), DataFrame)
machine_df = CSV.read(joinpath(DATA_DIR, "machine.csv"), DataFrame)
avr_df = CSV.read(joinpath(DATA_DIR, "avr.csv"), DataFrame)
gov_df = CSV.read(joinpath(DATA_DIR, "gov.csv"), DataFrame)
System base values follow the IEEE 39-bus standard:
BASE_MVA = 100.0
BASE_FREQ = 60.0
Subcomponent Definition
As stated above, our buses fall into 5 different categories. We will define a "template" for each of those categories and then create the individual buses from those templates. By doing so, we can reach substantial performance improvements, as we do not have to repeatedly compile the same models (the symbolic simplification is quite costly). Instead, we copy the templates and adjust parameters.
However, before we can define the bus templates, we need to define the individual subcomponents. Those subcomponents are MTK models and not yet compiled node models. See Modeling Concepts and the custom bus tutorial.
Load Model
We use the ZIP load model to represent loads. This model satisfies the Injector Interface.
(t) โโโโโโโโโโโโ
oโโโค ZIP Load โ
โโโโโโโโโโโโ
load = ZIPLoad(;name=:ZIPLoad)
Generator Models
For generators, we use the Sauer-Pai machine model, which is a 6th-order synchronous machine model. We create two variants:
Uncontrolled Machine: No external control inputs for mechanical torque or field voltage. This model satisfies the Injector Interface directly.
(t) โโโโโโโโโโโ
oโโโค Machine โ
โโโโโโโโโโโ
uncontrolled_machine = SauerPaiMachine(;
ฯ_m_input=false, ## No external mechanical torque input
vf_input=false, ## No external field voltage input
name=:machine,
)
Controlled Machine: Includes automatic voltage regulator (AVR) and turbine governor controls.
The controlled machine is modeled as a composite injector. It consists of 3 subcomponents: the machine, the AVR and the governor. The AVR receives the voltage magnitude measurement from the terminal of the machine and sets the field voltage. The governor receives the frequency measurement and sets the mechanical torque. Together, they satisfy the Injector Interface.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CtrldMachine u_mag_meas โ
โ โญโโโโโโโโโโโฎ โ
โ โโโโโโโโโโโดโโ โโโดโโโโ โ
(t) โ โ โโโโโโโโค AVR โ โ
oโโโผโโโโโค Sauer-Pai โ vf โโโโโโโ โ
โ โ Machine โ ฯ_m โโโโโโโ โ
โ โ โโโโโโโโค Gov โ โ
โ โโโโโโโโโโโฌโโ โโโฌโโโโ โ
โ โฐโโโโโโโโโโโฏ โ
โ ฯ_meas โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
_machine = SauerPaiMachine(;
name=:machine,
)
_avr = AVRTypeI(;
name=:avr,
ceiling_function=:quadratic,
)
_gov = TGOV1(; name=:gov,)
controlled_machine = CompositeInjector(
[_machine, _avr, _gov],
name=:ctrld_gen
)
Bus Template Creation
Now we have all the components (i.e., the MTK models) so we can combine them into full bus models and compile the methods.
Junction Bus
Pure transmission buses with no generation or load
โโโโโโโโโโโโโโโโโโโโโโโโ
โ Junction (compiled) โ
Network โ โโโโโโโโโโโโโโโโโโโ โ
interface โ โMTKBus โ โ
current โโโโโโโโโโโโโโ โ โ
โ โโBusBarโ(nothing)โ โ
voltage โโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโ
@named junction_bus_template = Bus(MTKBus())
strip_defaults!(junction_bus_template) ## Clear default parameters for manual setting
VertexModel :junction_bus_template PureStateMap()
โโ 2 inputs: [busbarโi_r, busbarโi_i]
โโ 2 states: [busbarโu_i, busbarโu_r]
| with diagonal mass matrix [0, 0]
โโ 2 outputs: [busbarโu_r, busbarโu_i]
Load Bus
Buses with only load components
โโโโโโโโโโโโโโโโโโโโโโโ
โ Load (compiled) โ
Network โ โโโโโโโโโโโโโโโโโโ โ
interface โ โMTKBus โ โ
current โโโโโโโโโโโโโโ โโโโโโ โ โ
โ โโBusBarโoโคLoadโ โ โ
voltage โโโโโโโโโโโโโโ โโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโ
@named load_bus_template = Bus(MTKBus(load))
strip_defaults!(load_bus_template)
VertexModel :load_bus_template PureStateMap()
โโ 2 inputs: [busbarโi_r, busbarโi_i]
โโ 2 states: [busbarโu_r, busbarโu_i]
| with diagonal mass matrix [0, 0]
โโ 2 outputs: [busbarโu_r, busbarโu_i]
โโ 9 params: [ZIPLoadโPset, ZIPLoadโQset, ZIPLoadโVsetโ1, ZIPLoadโKpZ, ZIPLoadโKqZ, ZIPLoadโKpI, ZIPLoadโKqI, ZIPLoadโKpC, ZIPLoadโKqC]
Generator Bus (Controlled)
Buses with controlled generators (machine + AVR + governor)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Ctrld Machine Bus (compiled) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โMTKBus โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
Network โ โ โCtrldMachine โญโโโโโโโโโโโฎ โ โ โ
interface โ โ โ โโโโโโโโโโโดโโ โโโดโโโโ โ โ โ
current โโโโโโโโโโโโโโ โ โ โโโโโโโโค AVR โ โ โ โ
โ โโBusBarโoโผโโโโโค Sauer-Pai โ โโโโโโโ โ โ โ
voltage โโโโโโโโโโโโโโ โ โ Machine โ โโโโโโโ โ โ โ
โ โ โ โ โโโโโโโโค Gov โ โ โ โ
โ โ โ โโโโโโโโโโโฌโโ โโโฌโโโโ โ โ โ
โ โ โ โฐโโโโโโโโโโโฏ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@named ctrld_machine_bus_template = Bus(
MTKBus(controlled_machine);
)
strip_defaults!(ctrld_machine_bus_template)
# Set system-wide base values for all generators
set_default!(ctrld_machine_bus_template, r"S_b$", BASE_MVA)
set_default!(ctrld_machine_bus_template, r"ฯ_b$", 2ฯ*BASE_FREQ)
VertexModel :ctrld_machine_bus_template PureStateMap()
โโ 2 inputs: [busbarโi_r, busbarโi_i]
โโ 15 states: [ctrld_genโgovโฯ_mโuโ0, ctrld_genโgovโxg1โ1, ctrld_genโgovโxg2โ0, ctrld_genโavrโvfoutโ1, ctrld_genโavrโv_fbโ0, ctrld_genโavrโvrโ0, ctrld_genโavrโvmโ1, ctrld_genโmachineโฯโณ_qโ0, ctrld_genโmachineโฯโณ_dโ1, ctrld_genโmachineโEโฒ_dโ1, ctrld_genโmachineโEโฒ_qโ0, ctrld_genโmachineโฯโ1, ctrld_genโmachineโฮดโ0, busbarโu_r, busbarโu_i]
| with diagonal mass matrix [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
โโ 2 outputs: [busbarโu_r, busbarโu_i]
โโ 42 params: [ctrld_genโmachineโR_s, ctrld_genโmachineโX_d, ctrld_genโmachineโX_q, ctrld_genโmachineโXโฒ_d, ctrld_genโmachineโXโฒ_q, ctrld_genโmachineโXโณ_d, ctrld_genโmachineโXโณ_q, ctrld_genโmachineโX_ls, ctrld_genโmachineโTโฒ_d0, ctrld_genโmachineโTโณ_d0, ctrld_genโmachineโTโฒ_q0, ctrld_genโmachineโTโณ_q0, ctrld_genโmachineโH, ctrld_genโmachineโD, ctrld_genโmachineโS_b=100, ctrld_genโmachineโV_b, ctrld_genโmachineโฯ_b=376.99, ctrld_genโmachineโSn, ctrld_genโmachineโVn, ctrld_genโavrโTr, ctrld_genโavrโvrefโ1, ctrld_genโavrโKa, ctrld_genโavrโKe, ctrld_genโavrโKf, ctrld_genโavrโTa, ctrld_genโavrโTf, ctrld_genโavrโTe, ctrld_genโavrโvr_min, ctrld_genโavrโvr_max, ctrld_genโavrโE1, ctrld_genโavrโE2, ctrld_genโavrโSe1, ctrld_genโavrโSe2, ctrld_genโgovโฯ_ref, ctrld_genโgovโp_refโ1, ctrld_genโgovโV_min, ctrld_genโgovโV_max, ctrld_genโgovโR, ctrld_genโgovโT1, ctrld_genโgovโT2, ctrld_genโgovโT3, ctrld_genโgovโDT]
Generator + Load Bus (Controlled)
Buses with both controlled generators and loads
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Ctrld Machine Load Bus (compiled) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โMTKBus โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โCtrldMachine โญโโโโโโโโโโโฎ โ โ โ
โ โ โ โโโโโโโโโโโดโโ โโโดโโโโ โ โ โ
โ โ โ โ โโโโโโโโค AVR โ โ โ โ
Network โ โ โโโผโโโโโค Sauer-Pai โ โโโโโโโ โ โ โ
interface โ โ โ โ โ Machine โ โโโโโโโ โ โ โ
current โโโโโโโโโโโโโโโ โ โ โโโโโโโโค Gov โ โ โ โ
โ โโBusBarโo โ โโโโโโโโโโโฌโโ โโโฌโโโโ โ โ โ
voltage โโโโโโโโโโโโโโโ โ โฐโโโโโโโโโโโฏ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โโโโโโโโ โ โ
โ โ โโโค Load โ โ โ
โ โ โโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@named ctrld_machine_load_bus_template = Bus(
MTKBus(controlled_machine, load);
)
strip_defaults!(ctrld_machine_load_bus_template)
set_default!(ctrld_machine_load_bus_template, r"S_b$", BASE_MVA)
set_default!(ctrld_machine_load_bus_template, r"ฯ_b$", 2ฯ*BASE_FREQ)
VertexModel :ctrld_machine_load_bus_template NoFeedForward()
โโ 2 inputs: [busbarโi_r, busbarโi_i]
โโ 15 states: [ctrld_genโgovโฯ_mโuโ0, ctrld_genโmachineโterminalโi_rโ0, ctrld_genโmachineโterminalโi_iโ0, ctrld_genโgovโxg1โ1, ctrld_genโgovโxg2โ0, ctrld_genโavrโvfoutโ1, ctrld_genโavrโv_fbโ0, ctrld_genโavrโvrโ0, ctrld_genโavrโvmโ1, ctrld_genโmachineโฯโณ_qโ0, ctrld_genโmachineโฯโณ_dโ1, ctrld_genโmachineโEโฒ_dโ1, ctrld_genโmachineโEโฒ_qโ0, ctrld_genโmachineโฯโ1, ctrld_genโmachineโฮดโ0]
| with diagonal mass matrix [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
โโ 2 outputs: [busbarโu_r, busbarโu_i]
โโ 51 params: [ctrld_genโmachineโR_s, ctrld_genโmachineโX_d, ctrld_genโmachineโX_q, ctrld_genโmachineโXโฒ_d, ctrld_genโmachineโXโฒ_q, ctrld_genโmachineโXโณ_d, ctrld_genโmachineโXโณ_q, ctrld_genโmachineโX_ls, ctrld_genโmachineโTโฒ_d0, ctrld_genโmachineโTโณ_d0, ctrld_genโmachineโTโฒ_q0, ctrld_genโmachineโTโณ_q0, ctrld_genโmachineโH, ctrld_genโmachineโD, ctrld_genโmachineโS_b=100, ctrld_genโmachineโV_b, ctrld_genโmachineโฯ_b=376.99, ctrld_genโmachineโSn, ctrld_genโmachineโVn, ctrld_genโavrโTr, ctrld_genโavrโvrefโ1, ctrld_genโavrโKa, ctrld_genโavrโKe, ctrld_genโavrโKf, ctrld_genโavrโTa, ctrld_genโavrโTf, ctrld_genโavrโTe, ctrld_genโavrโvr_min, ctrld_genโavrโvr_max, ctrld_genโavrโE1, ctrld_genโavrโE2, ctrld_genโavrโSe1, ctrld_genโavrโSe2, ctrld_genโgovโฯ_ref, ctrld_genโgovโp_refโ1, ctrld_genโgovโV_min, ctrld_genโgovโV_max, ctrld_genโgovโR, ctrld_genโgovโT1, ctrld_genโgovโT2, ctrld_genโgovโT3, ctrld_genโgovโDT, ZIPLoadโPset, ZIPLoadโQset, ZIPLoadโVsetโ1, ZIPLoadโKpZ, ZIPLoadโKqZ, ZIPLoadโKpI, ZIPLoadโKqI, ZIPLoadโKpC, ZIPLoadโKqC]
Generator + Load Bus (Uncontrolled)
Buses with uncontrolled generators and loads
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Unctr. Ma. Load Bus (compiled) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโ โ
Network โ โMTKBus โโโโโโโโโโโ โ โ
interface โ โ โโโค Machine โ โ โ
current โโโโโโ โโโโโโโโ โ โโโโโโโโโโโ โ โ
โ โ โBusBarโโo โ โ
voltage โโโโโโ โโโโโโโโ โ โโโโโโโโ โ โ
โ โ โโโค Load โ โ โ
โ โ โโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@named unctrld_machine_load_bus_template = Bus(
MTKBus(uncontrolled_machine, load);
)
strip_defaults!(unctrld_machine_load_bus_template)
set_default!(unctrld_machine_load_bus_template, r"S_b$", BASE_MVA)
set_default!(unctrld_machine_load_bus_template, r"ฯ_b$", 2ฯ*BASE_FREQ)
VertexModel :unctrld_machine_load_bus_template NoFeedForward()
โโ 2 inputs: [busbarโi_r, busbarโi_i]
โโ 8 states: [machineโterminalโi_iโ0, machineโterminalโi_rโ0, machineโฯโณ_qโ0, machineโฯโณ_dโ1, machineโEโฒ_dโ1, machineโEโฒ_qโ0, machineโฯโ1, machineโฮดโ0]
| with diagonal mass matrix [0, 0, 1, 1, 1, 1, 1, 1]
โโ 2 outputs: [busbarโu_r, busbarโu_i]
โโ 30 params: [machineโvf_setโ1, machineโฯ_m_setโ1, machineโR_s, machineโX_d, machineโX_q, machineโXโฒ_d, machineโXโฒ_q, machineโXโณ_d, machineโXโณ_q, machineโX_ls, machineโTโฒ_d0, machineโTโณ_d0, machineโTโฒ_q0, machineโTโณ_q0, machineโH, machineโD, machineโS_b=100, machineโV_b, machineโฯ_b=376.99, machineโSn, machineโVn, ZIPLoadโPset, ZIPLoadโQset, ZIPLoadโVsetโ1, ZIPLoadโKpZ, ZIPLoadโKqZ, ZIPLoadโKpI, ZIPLoadโKqI, ZIPLoadโKpC, ZIPLoadโKqC]
Bus Instantiation and Parameter Setting
Now we create the actual bus instances by copying templates and applying specific parameters from the CSV data files.
# Helper function to apply CSV parameters to bus components
function apply_csv_params!(bus, table, bus_index)
row_idx = findfirst(table.bus .== bus_index)
# Apply all parameters except "bus" column
row = table[row_idx, :]
for col_name in names(table)
if col_name != "bus"
set_default!(bus, Regex(col_name*"\$"), row[col_name])
end
end
end
For each bus in the system, we:
- Select the appropriate template based on its category
- Create a bus instance with the correct vertex index and name
- Apply component-specific parameters from CSV files
- Set the power flow model (PQ, PV, or Slack)
busses = []
for row in eachrow(bus_df)
i = row.bus
# Select template based on bus category
bus = if row.category == "junction"
Bus(junction_bus_template; vidx=i, name=Symbol("bus$i"))
elseif row.category == "load"
Bus(load_bus_template; vidx=i, name=Symbol("bus$i"))
elseif row.category == "ctrld_machine"
Bus(ctrld_machine_bus_template; vidx=i, name=Symbol("bus$i"))
elseif row.category == "ctrld_machine_load"
Bus(ctrld_machine_load_bus_template; vidx=i, name=Symbol("bus$i"))
elseif row.category == "unctrld_machine_load"
Bus(unctrld_machine_load_bus_template; vidx=i, name=Symbol("bus$i"))
end
# Apply component parameters from CSV files
row.has_load && apply_csv_params!(bus, load_df, i)
row.has_gen && apply_csv_params!(bus, machine_df, i)
row.has_avr && apply_csv_params!(bus, avr_df, i)
row.has_gov && apply_csv_params!(bus, gov_df, i)
# Set power flow model based on bus type
pf_model = if row.bus_type == "PQ"
pfPQ(P=row.P, Q=row.Q) ## Load bus: fixed P and Q
elseif row.bus_type == "PV"
pfPV(P=row.P, V=row.V) ## Generator bus: fixed P and V
elseif row.bus_type == "Slack"
pfSlack(V=row.V, ฮด=0) ## Slack bus: fixed V and angle
end
set_pfmodel!(bus, pf_model)
push!(busses, bus)
end
Transmission Line Creation
The IEEE 39-bus system includes both transmission lines and transformers, all modeled using the ฯ-line equivalent circuit model.
The model consists of several layers:
- The
PiModel
, which satisfies the Branch Interface as it has two terminals - The
MTKLine
constructor, which creates a MTK model fulfilling the MTKLine Interface - The compiled
EdgeModel
created by calling theLine
constructor
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ EdgeModel (compiled) โ
src โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ dst
vertex โ โMTKLine โ โ vertex
u โโโโโโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโโโโโ u
โ โโLineEndโoโค PiLine โoโคLineEndโโ โ
i โโโโโโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโโโโโ i
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
(We used the PiLine_fault
model since we plan on simulating short circuits later.)
@named piline_template = Line(MTKLine(PiLine_fault(;name=:piline)))
EdgeModel :piline_template PureFeedForward()
โโ 2/2 inputs: src=[srcโu_r, srcโu_i] dst=[dstโu_r, dstโu_i]
โโ 0 states: []
โโ 2/2 outputs: src=[srcโi_r, srcโi_i] dst=[dstโi_r, dstโi_i]
โโ 16 params: [pilineโR, pilineโX, pilineโG_src, pilineโB_src, pilineโG_dst, pilineโB_dst, pilineโr_src=1, pilineโr_dst=1, pilineโR_fault=0, pilineโX_fault=0, pilineโG_fault=0, pilineโB_fault=0, pilineโpos=0.5, pilineโactive=1, pilineโshortcircuit=0, pilineโfaultimp=0]
Each transmission element is created by:
- Instantiating a line from the template with source and destination buses
- Setting electrical parameters (resistance, reactance, susceptance) from CSV data
branches = []
for row in eachrow(branch_df)
# Create line instance with topology
line = Line(piline_template; src=row.src_bus, dst=row.dst_bus)
# Apply electrical parameters from CSV data
for col_name in names(branch_df)
if col_name โ ["src_bus", "dst_bus", "transformer"]
set_default!(line, Regex(col_name*"\$"), row[col_name])
end
end
push!(branches, line)
end
Network Assembly
Finally, we combine all buses and transmission lines into a complete network model. This creates the IEEE 39-bus test system ready for initialization and simulation.
nw = Network(busses, branches)
Network with 201 states and 1306 parameters
โโ 39 vertices (5 unique types)
โโ 46 edges (1 unique type)
Edge-Aggregation using SequentialAggregator(+)
The network nw
now contains the complete IEEE 39-bus model structure. In Part 2 of this tutorial series, we'll initialize this network by solving the power flow and setting up the dynamic initial conditions.
This page was generated using Literate.jl.