Samples & Guides
Worked examples showing how to configure the tool for common power-system studies. Every example runs on one of PowerFactory's bundled demonstration projects, so you can reproduce each result without building a network first.
Built-in Models Used
Each sample runs on a PowerFactory example project that ships with the software. Samples 01 to 04 share the Nine-bus System; samples 05 and 06 use two different demonstration projects because a harmonic filter study and an EMT switching study need networks the Nine-bus System does not contain.
Anderson & Fouad Nine-bus System: 9 buses, 3 generators, 6 lines, 230 kV / 60 Hz. Used for the steady-state loss-factor sweep, the minimum-loss optimisation, the RMS rotor-angle stability study, and the N-1 line contingency scan.
PowerFactory's harmonics and resonance demonstration: a 400 kV / 50 Hz transmission system
feeding downstream urban and rural networks, with an HVDC converter station whose tuned filter banks
(11th, 13th and 24th high-pass branches) sit on the 400 kV busbar. Used for the harmonic filter tuning study on the
01.1 Simplified Network Model study case.
PowerFactory's switching-transients demonstration: a 400 / 110 kV, 50 Hz network in
which a power transformer is energised through a circuit breaker. Used for the EMT transformer
energisation study on the 03.1 Transformer Energisation I study case.
On the Nine-bus System, quantify how each generator's MW output affects the active-power flow on
Line 5-7 and Line 7-8, also known as the generation-shift
(transmission loss) factors. Because G1 is the slack machine, its dispatch is set by the load flow, so we
sweep the two dispatchable machines G2 and G3 and record the resulting
G1 output alongside the two line flows. The change in each line's MW per 1 MW of
G2 or G3 is read directly from the slope of the resulting table.
- Slack machine:
G1.ElmSym(Bus 1); its dispatch is the balancing output, not an input - Dispatchable generators:
G2.ElmSym(Bus 2),G3.ElmSym(Bus 3); swept viapgini - Monitored lines:
Line 5-7.ElmLne,Line 7-8.ElmLne, active power at the from-end (m:P:bus1) - System: 230 kV, 60 Hz; base-case dispatch G2 = 163 MW, G3 = 85 MW (G1 ≈ 71.6 MW)
- Loads held constant: Load A 125 MW (Bus 5), Load B 90 MW (Bus 6), Load C 100 MW (Bus 8)
| Name | Object | Attribute | Lower | Upper | Step |
|---|---|---|---|---|---|
.py).
The script writes a CSV to your output directory. Columns are the swept inputs, the convergence flag, then the three monitored outputs. Real values from a Nine-bus run (rounded):
G2_dispatch,G3_dispatch,Converge?,G1_dispatch,Line_5_7_MW,Line_7_8_MW 120,45,True,152.83,-43.20,76.20 120,65,True,132.88,-50.74,68.43 120,85,True,113.18,-58.22,60.70 120,105,True,93.70,-65.63,52.99 120,125,True,74.47,-72.98,45.31 140,45,True,133.08,-55.53,83.48 160,45,True,113.54,-67.76,90.77 180,45,True,94.21,-79.86,98.09 200,45,True,75.10,-91.83,105.42 ... 200,125,True,-0.74,-120.55,74.57
Reading the loss/shift factor: hold one generator fixed and divide the change in a line's MW by the change in that generator's MW. Fitting the run above gives the per-MW generation shift factors:
- Line 5-7: ≈
−0.61 MWper MW of G2, ≈−0.37 MWper MW of G3 - Line 7-8: ≈
+0.37 MWper MW of G2, ≈−0.39 MWper MW of G3 - Slack G1: absorbs ≈
−0.97 MWper MW of G2; it picks up the balance as G2/G3 back off
A negative Line_5_7_MW means power flows from the Bus 7 end toward Bus 5
(the m:P:bus1 from-end convention): as G2 at Bus 7 ramps up it pushes more power back
along Line 5-7 and more forward into Line 7-8 toward Load C.
Find the G2 and G3 active-power dispatch that minimises total
network active-power losses on the Nine-bus System, using a Bayesian optimiser. The slack
machine G1 absorbs the balance at each evaluation, and the total loss is read from the
grid summary (ElmNet c:LossP).
- Slack machine:
G1.ElmSym, balances each candidate dispatch - Optimised variables:
G2.ElmSym/G3.ElmSympgini - Objective: minimise total active losses =
*.ElmNetc:LossP(summed across every matched grid) - Bounds: G2 50–160 MW, G3 20–105 MW (within machine ratings)
- Loads constant; base-case total losses ≈ 4.64 MW (sum of line losses in the reference)
- Algorithm: Bayesian (
gp_minimize), 60 evaluations
| Name | Object | Attribute | Lower | Upper | Step |
|---|---|---|---|---|---|
.py).
The script prints the best objective value and writes every evaluation to the CSV.
Note that *.ElmNet matched two grids (the real
Nine-bus System grid and PowerFactory's auto-generated Summary Grid, which
reports the same c:LossP), so the objective is their sum (≈ 2× the physical
loss). This rescales the printed value but not where the minimum sits. Real values from a
60-evaluation Nine-bus run (rounded):
[objective] 'total_losses' matched 2 columns; using their sum.
... (one line per evaluation)
Optimisation complete. Best value: 5.3461
# Run{timestamp}.csv (selected rows)
G2_dispatch,G3_dispatch,Converge?,total_losses_Nine-bus System,total_losses_Summary Grid,G1_dispatch
138,36,True,3.0313,3.0313,144.03
159,72,True,4.0488,4.0488,88.05
99,28,True,3.0991,3.0991,191.10
97,74,True,2.7062,2.7062,146.71
88,70,True,2.6740,2.6740,159.67
89,71,True,2.6734,2.6734,157.67
88,72,True,2.6731,2.6731,159.67 <- best dispatch
The optimiser converges on G2 ≈ 88 MW,
G3 ≈ 72 MW (slack G1 settling ≈ 159.7 MW), driving
the per-grid total loss down to ≈ 2.67 MW from the base-case
≈ 4.64 MW. The printed best value (5.3461) is that 2.67 MW counted across both
matched grids. To read the single-grid figure directly, target one grid (e.g.
Nine-bus System.ElmNet) instead of the wildcard. The full evaluation history in the CSV lets
you plot loss vs. dispatch and confirm the minimum; the optimiser does not enforce generator or thermal
limits beyond the bounds you set, so verify the result is physical.
Apply a three-phase fault and clear it by opening a circuit breaker after a fixed delay, then sweep
that fault-clearing time from 0.1 s to 1 s and run an RMS simulation at each
value. For every clearing time, track the relative rotor angle (c:firel, referred to the
reference machine) of G2 and G3. When the fault is cleared too late a machine
loses synchronism and its rotor angle swings right through ±180°, so the critical clearing
time is the longest delay the system survives before a generator slips a pole.
- Study case:
02- Five Cycles Classical SG Model(RMS, classical synchronous-machine models) - The fault is cleared by opening a breaker via the
Switch Event.EvtSwitchin the study case's simulation event list - Fault-clearing time = the event's absolute time
e:time, swept 0.1–1.0 s in 0.1 s steps (10 RMS runs) - Monitored machines:
G2.ElmSym(Bus 2),G3.ElmSym(Bus 3); relative rotor anglec:firelin degrees, referred to the reference machineG1(Bus 1) - Stability criterion: a machine is out of step when its rotor angle reaches a maximum > 170° and a minimum < −170° (a full pole slip)
- Simulation window: 0–10 s
| Name | Object | Attribute | Lower | Upper | Step |
|---|---|---|---|---|---|
{output_dir}/graph)
{output_dir}/graph)
The graph is only enabled on the two Maximum
outputs: maximum and minimum read the same c:firel trace, so a single Time-vs-angle
PNG per machine captures both, so checking it on the minimum outputs too would just duplicate the plot.
def stability_verdict(G2_rotor_angle_max, G2_rotor_angle_min, G3_rotor_angle_max, G3_rotor_angle_min):
g2_lost = G2_rotor_angle_max > 170 and G2_rotor_angle_min < -170
g3_lost = G3_rotor_angle_max > 170 and G3_rotor_angle_min < -170
return "Not Stable" if (g2_lost or g3_lost) else "Stable"
Arguments must match the names of the four rotor-angle outputs
above. A machine that slips a pole swings its relative rotor angle past +170° on one side and below
−170° on the other, so the verdict returns Not Stable the moment either generator hits
both extremes.
.py).
Switch Event exists in the active RMS study case. Ten RMS runs (one per clearing time) take a few minutes.The script writes a summary CSV: the swept clearing time, the convergence flag, the
maximum/minimum c:firel of each machine (degrees), and the stability_verdict
string. Time-domain rotor-angle plots for G2 and G3 are saved as
.png in {output_dir}/graph/ for each clearing time. Real values from a
Nine-bus run (rounded):
fault_clearing_time,Converge?,G2_rotor_angle_max,G2_rotor_angle_min,G3_rotor_angle_max,G3_rotor_angle_min,stability_verdict 0.1,True,87.94,2.19,61.12,2.15,Stable 0.2,True,179.99,-179.99,179.99,-179.97,Not Stable 0.3,True,180.00,-180.00,179.98,-180.00,Not Stable 0.4,True,180.00,-179.98,123.11,-41.33,Not Stable 0.5,True,179.99,-179.99,113.39,-40.68,Not Stable 0.6,True,179.99,-179.99,179.99,-179.98,Not Stable 0.7,True,179.99,-180.00,180.00,-179.97,Not Stable 0.8,True,179.97,-179.99,179.99,-179.99,Not Stable 0.9,True,179.99,-179.97,179.95,-179.97,Not Stable 1.0,True,179.99,-180.00,179.98,-180.00,Not Stable
The system survives a 0.1 s clearing time: both machines swing and
settle (G2 peaks at ≈88°, G3 at ≈61°), but by 0.2 s the rotor angles run away to
±180°, so the critical clearing time lies between 0.1 s and 0.2 s. Note the
0.4–0.5 s rows: only G2 slips a pole (max ≈180°, min ≈−180°) while G3
stays in step (within ±170°), yet the verdict still reads Not Stable because the criterion
flags the system the moment any machine loses synchronism.
fault_clearing_time = 0.1 s, Stable: G2's rotor angle swings then damps back toward its pre-fault value.
fault_clearing_time = 1.0 s, Not Stable: G2 slips poles, its rotor angle wrapping continuously through ±180°.These two PNGs are the G2_rotor_angle_max plots the script saves to
{output_dir}/graph/, one per clearing time. The contrast between the damped swing and the
runaway saturation is the visual signature of the critical clearing time being crossed.
Assess the steady-state impact of losing any single transmission line in the Nine-bus System. The Contingency problem type resolves all matching lines at runtime via a wildcard query, with no manual enumeration or spreadsheet required. The script runs a base case followed by one load flow per line, recording bus voltages and line loadings alongside convergence status for every scenario.
- N-1 contingency mode will be used
- All six transmission lines are 230 kV; the filter
e:Unom ≥ 220keeps all of them - Bus voltage and line loading of all buses and lines will be monitored
Select Contingency as the Problem Type. The Input Variables section is hidden because the Contingency Config section replaces it.
| # | Element Query | Filter Attribute | Op | Filter Value |
|---|---|---|---|---|
| 1 |
.py).
The script writes a summary CSV with all nine buses and six lines. The first two columns
identify the contingency and its convergence status; subsequent columns are the output values, each
prefixed with the variable name and the element's loc_name (e.g.
bus_voltage_Bus 5, loading_Line 5-7):
Contingent Element 1,Converge?,bus_voltage_Bus 5,bus_voltage_Bus 6,bus_voltage_Bus 8,loading_Line 4-5,loading_Line 5-7,loading_Line 7-8 Base Case,True,0.996,1.013,1.016,14.15,21.45,18.94 Line 5-7,True,0.938,0.975,1.001,36.03,,40.33 Line 7-8,True,0.974,0.999,0.969,17.20,40.15, Line 8-9,True,0.990,1.009,0.978,18.87,15.74,27.19 Line 6-9,True,0.968,0.964,1.005,14.04,36.40,6.34 Line 4-6,True,0.999,0.942,1.006,20.01,13.88,27.30 Line 4-5,True,0.839,1.020,0.989,,40.29,8.34
Converge? = False on any row would mean the load flow did not converge with that
line tripped. Scan the loading columns for post-contingency overloads (>100%).
This sample runs on the Resonance Studies project, study case
01.1 Simplified Network Model. It is a 400 kV / 50 Hz transmission system with an
HVDC converter station on the 400 kV busbar. The station carries two identical filter banks, each
with an 11th branch, a 13th branch and a 24th high-pass branch, plus
switchable capacitor banks. This is a different network from the Nine-bus System used by samples 01 to 04, because a
harmonic resonance study needs the filter banks and the converter that the Nine-bus System does not have.
Tune the HVDC 11th-harmonic filter (HVDC Filter1 11th.ElmFilter) so that it
minimises the voltage distortion at the HVDC busbar caused by the converter's 11th
harmonic, using a Bayesian optimiser. The filter is described by its tuning order
(nres) and its rated reactive power (qtotn). The optimiser
searches both: a filter tuned close to the 11th and sized correctly absorbs the harmonic, but the same
capacitance can form a parallel resonance with the network that amplifies the harmonic instead,
so the objective surface has a sharp resonance ridge the optimiser has to steer around.
The shipped study case has no harmonic source and two parallel filter banks, so on its own the 11th filter has almost no measurable effect. Two small changes in PowerFactory set up a meaningful single-bank tuning study. This is standard practice for HVDC filter design, where the converter is represented as a harmonic current source and one bank is studied at a time.
- Add a converter harmonic current source at the HVDC 400 kV busbar: an
ElmIacAC current source with aTypHmccurspectrum carrying the 11th harmonic. This represents the characteristic 11th harmonic a 12-pulse converter injects. - Put the redundant second bank and the capacitor banks out of service, leaving
HVDC Filter1as the bank under study: setHVDC Filter2 11th,HVDC Filter2 13th,HVDC Filter2 24th HP,HVDC_C-Bank1andHVDC_C-Bank2to out of service.
- Study case:
01.1 Simplified Network Model(balanced harmonic load flow,ComHldf) - Optimised element:
HVDC Filter1 11th.ElmFilter(single-tuned shunt filter) - Optimised variables: tuning order
nres(9 to 13) and rated reactive powerqtotn(40 to 120 Mvar) - Objective: minimise the total harmonic voltage distortion at the HVDC busbar, read at terminal
400_G3.1.ElmTerm(m:THD, in %) - As-built starting point:
nres = 11,qtotn = 56 Mvargives a busbar THD of ≈ 1.61 % - Algorithm: Bayesian (
gp_minimize), 60 evaluations
| Name | Object | Attribute | Lower | Upper | Step |
|---|---|---|---|---|---|
The HVDC busbar is named 4, which repeats across
many substations, so it is not a unique query. Terminal 400_G3.1 sits on the same electrical
node as the filter and has a unique name, so it is the safe object to read the busbar's m:THD
from.
.py).
ComHldf harmonic load
flow. Requires pip install scikit-optimize.The script prints the best objective value and writes every evaluation to the CSV: the two swept filter parameters, the convergence flag, then the busbar THD. Real values from a 60-evaluation run (rounded, a spread of rows shown):
Optimisation complete. Best value: 0.6197
# Run{timestamp}.csv (selected rows)
filter_tuning_order,filter_rated_power,Converge?,THD_HVDC_bus
9.909,65.52,True,3.175
12.913,76.45,True,2.065
11.689,95.52,True,4.325 <- filter capacitance resonates with the network at the 11th
12.991,101.18,True,4.228 <- resonance ridge again
10.232,61.11,True,2.299
9.351,96.55,True,1.305
9.014,119.67,True,0.629
9.004,119.13,True,0.630
9.000,120.00,True,0.620 <- best
The optimiser drives the busbar THD down to ≈ 0.62 %, from
≈ 1.61 % at the as-built nres = 11,
qtotn = 56 Mvar, a reduction of about 60 %. Along the way it probes points
on the parallel-resonance ridge where the filter capacitance and the network inductance
resonate at the 11th harmonic and the THD jumps above 4 %. The scatter below plots every evaluation
in the two-parameter design space, coloured by the resulting THD:
The best point sits at the edge of the searched range
(nres = 9, qtotn = 120 Mvar), which tells you the true
optimum lies just outside the bounds: widen them, or add a rating or fundamental-voltage constraint, if you
want the interior minimum. The optimiser does not enforce filter or busbar limits beyond the bounds you
set, so confirm the winning design is physically sensible.
This sample runs on the Switching Transients project, study case
03.1 Transformer Energisation I. It is a 400 / 110 kV, 50 Hz network in
which a power transformer (TRF1) is energised from the 110 kV side by closing circuit
breaker CB4. This is a different network from the Nine-bus System used by samples 01 to 04,
because an EMT switching study needs the detailed transformer and breaker models that the Nine-bus System
does not carry.
Energise transformer TRF1 by closing breaker CB4, then sweep the
breaker closing time across one 50 Hz cycle (0 to 0.02 s) and run an EMT simulation at
each value. Transformer inrush depends on where on the voltage wave each pole closes, so the peak inrush
current moves between the three phases as the closing time shifts. For every run the tool records the peak
HV-side current on each phase and saves the current-versus-time waveform, so you can see both the numbers
and the classic offset, slowly decaying inrush shape.
- Study case:
03.1 Transformer Energisation I(EMT,ComIncwithiopt_sim = 'ins'thenComSim), operating scenarioTransformer Energisation - Energised transformer:
TRF1.ElmTr2; the HV-side phase currents are monitored - Breaker:
CB4, closed by theCB4.EvtSwitchswitch event in the study case's simulation event list - Swept input: the switch event's closing time
e:time, 0 to 0.02 s in 0.004 s steps (6 EMT runs). One 50 Hz cycle is 0.02 s, so the sweep covers a full cycle of point-on-wave angles - Monitored outputs: HV phase currents
m:I:bushv:A,m:I:bushv:B,m:I:bushv:C(kA), peak (Maximum) of each, with the time-versus-current graph saved - Units on this study case: the switch-event time is in seconds, but the simulation time base is in milliseconds, so a Stop Time of 200 gives a 0.2 s window; the pre-energisation window starts at −100 ms
| Name | Object | Attribute | Lower | Upper | Step |
|---|---|---|---|---|---|
{output_dir}/graph)
{output_dir}/graph)
{output_dir}/graph)
The Maximum metric records each phase's largest
positive current. Inrush pulses can be positive or negative depending on the phase and the closing
angle, so a phase whose inrush is negative on a given run reads near zero in the table while its real
peak shows up in the saved graph. Reading all three phases together captures the full picture; add a
Minimum output per phase if you want the negative peaks as numbers too.
.py).
{output_dir}/graph/.The script writes a summary CSV: the swept closing time, the convergence flag, then the peak
HV current on each phase (kA). Time-domain inrush waveforms for each phase are saved as .png
in {output_dir}/graph/, one per closing time. Real values from a run (rounded):
breaker_closing_time,Converge?,TRF1_I_HV_A,TRF1_I_HV_B,TRF1_I_HV_C 0.000,True,0.114,0.623,0.002 0.004,True,0.000,0.935,0.003 0.008,True,0.000,0.003,0.937 0.012,True,0.159,0.002,0.591 0.016,True,1.034,0.001,0.001 0.020,True,0.114,0.623,0.001
Each phase's peak inrush appears at a different closing time, because the three pole voltages
are 120 degrees apart. The largest positive peak in this run is ≈ 1.03 kA on phase
A at a 0.016 s closing time, when that pole closes near a voltage zero and drives the core
deep into saturation. Note that the 0.000 s and 0.020 s rows match:
one 50 Hz cycle later is the same point on the wave.
closing_time = 0.012 s: phase A closes near its voltage peak, so its inrush
stays small (≈ 0.16 kA).
closing_time = 0.016 s: phase A closes near a voltage zero, so its inrush
jumps to ≈ 1.03 kA and decays slowly.These two PNGs are the TRF1_I_HV_A graphs the script saves to
{output_dir}/graph/, one per closing time. The offset, unidirectional pulses that decay over
many cycles are the signature of transformer inrush, and the contrast between the two closing times is the
point-on-wave effect. Plotting all three phases from one run shows how the inrush rotates between phases:
closing_time = 0.016 s. Current is zero
until CB4 closes, then each phase draws an offset, slowly decaying inrush.Frequently Asked Questions
The API Path points to the PowerFactory Python API folder. It is located inside a Python subfolder of your PowerFactory installation, with a sub-folder for each available Python version. For example:
C:\Program Files\DIgSILENT\PowerFactory 2024\Python\3.10\
Choose the sub-folder that matches the Python version you are using to run your script.
| Study Type | PF Command | Use to Analyse ... |
|---|---|---|
| Steady State | Load Flow (ldf) |
Steady state voltages, currents, loading, etc |
| Dynamic RMS | RMS simulation (rms) |
Fault response, generator dynamics, inverter dynamics, etc |
| Dynamic EMT | EMT simulation (emt) |
Switching transients, power electronics, detailed waveforms, etc |
| Harmonic | Harmonic load flow (hldf) |
Total harmonic distortion (THD), individual harmonics, etc |
Python File (.py): Best for production use, scheduled runs, or when you want to import the script as a module. Run from the terminal or your IDE.
Notebook (.ipynb): Best for exploratory work, step-by-step debugging, or when you want inline plots and tables alongside the code. Open in Jupyter.