Short-circuit automation in PowerFactory with Python
In Python, a short-circuit calculation is the study case's ComShc command: set the method (iopt_mde = 1 for IEC 60909), the fault type (iopt_shc = "3psc") and the location (iopt_allbus = 2 for every busbar), call Execute(), then read m:Ikss, m:ip and m:Skss on each busbar.
What a short-circuit calculation gives you
A short-circuit calculation gives the current that flows into a fault, and the currents and voltages around it. The maximum currents size equipment: the breaking capacity of breakers, the peak and thermal withstand of busbars and cables, and the fault level a new connection adds to. The minimum currents check that protection still sees a fault.
PowerFactory has several methods in one command, ComShc: IEC 60909 and its German twin VDE 0102 (an equivalent voltage source at the fault with a voltage factor c, no load flow needed), ANSI (IEEE C37), and the complete method, which superimposes the fault on a load flow. The results on a faulted busbar include the initial symmetrical current m:Ikss (Ik"), the peak current m:ip and the initial short-circuit power m:Skss (Sk" = β3 Un Ik").
The method
- Activate the study case whose grids and operating state you want faulted.
- Get the command with
app.GetFromStudyCase("ComShc")and keep its settings to put back. - Choose the method and the fault:
iopt_mde1 for IEC 60909;iopt_shc"3psc"three-phase,"2psc"two-phase,"spgf"single-phase to ground,"2pgf"two-phase to ground;iopt_cur0 for maximum or 1 for minimum currents. - Choose where:
iopt_allbus = 2faults every busbar in turn in one run, oriopt_allbus = 0withshcobjset to one busbar or line. - Run with
Execute()(0 means it worked) and readm:Ikss,m:ipandm:Sksson each faultedElmTerm. - Save the rows to CSV, and put the command's settings back.
The script
short_circuit.py
"""Short circuit in PowerFactory with Python: an IEC 60909 three-phase fault at every busbar, with
the initial, peak and power values of each saved to CSV.
Runs on the "Nine-bus System" example project that comes with PowerFactory 2024.
"""
import csv
import sys
sys.path.append(r"C:\Program Files\DIgSILENT\PowerFactory 2024\Python\3.12")
import powerfactory
PROJECT = "Nine-bus System"
STUDY_CASE = "01- Load Flow"
app = powerfactory.GetApplicationExt() # inside PowerFactory: powerfactory.GetApplication()
app.ActivateProject(PROJECT)
app.GetProjectFolder("study").GetContents(STUDY_CASE + ".IntCase")[0].Activate()
shc = app.GetFromStudyCase("ComShc")
saved = {a: shc.GetAttribute(a) for a in ("iopt_mde", "iopt_shc", "iopt_cur", "iopt_allbus")}
shc.SetAttribute("iopt_mde", 1) # method: 0 VDE 0102, 1 IEC 60909, 2 ANSI, 3 complete
shc.SetAttribute("iopt_shc", "3psc") # fault: 3psc three-phase, 2psc two-phase, spgf single-phase to ground
shc.SetAttribute("iopt_cur", 0) # 0 maximum, 1 minimum short-circuit currents
shc.SetAttribute("iopt_allbus", 2) # 2 a fault at every busbar, one at a time
try:
if shc.Execute() != 0:
raise SystemExit("The short-circuit calculation failed")
rows = [{"Busbar": b.loc_name, "Nominal (kV)": round(b.uknom, 2),
"Ikss (kA)": b.GetAttribute("m:Ikss"), # initial symmetrical short-circuit current
"ip (kA)": b.GetAttribute("m:ip"), # peak short-circuit current
"Skss (MVA)": b.GetAttribute("m:Skss")} # initial short-circuit power
for b in app.GetCalcRelevantObjects("*.ElmTerm")]
finally:
for a, v in saved.items(): # leave the command as it was
shc.SetAttribute(a, v)
for r in sorted(rows, key=lambda r: -r["Ikss (kA)"]):
print(f"{r['Busbar']:6} {r['Nominal (kV)']:6.1f} kV Ikss {r['Ikss (kA)']:6.2f} kA ip {r['ip (kA)']:6.2f} kA")
with open("short_circuit_results.csv", "w", newline="") as f:
out = csv.DictWriter(f, fieldnames=list(rows[0]))
out.writeheader()
out.writerows(rows)
Result on the Nine-bus example
The highest fault level on the 230 kV network is at Bus 7: 3.08 kA, 1227 MVA. The generator terminals (13.8 to 18 kV) see around 50 kA, because the same power at a lower voltage is a larger current. As a check, Bus 5 gives Sk" = β3 Γ 230 kV Γ 2.256 kA = 898.7 MVA, the 898.7 MVA PowerFactory reports.
| Busbar | Nominal kV | Ikss kA | ip kA | Skss MVA |
|---|---|---|---|---|
| Bus 1 | 16.5 | 57.01 | 147.98 | 1629 |
| Bus 2 | 18.0 | 47.75 | 124.86 | 1489 |
| Bus 3 | 13.8 | 48.36 | 125.58 | 1156 |
| Bus 4 | 230.0 | 3.05 | 7.88 | 1216 |
| Bus 5 | 230.0 | 2.26 | 5.61 | 899 |
| Bus 6 | 230.0 | 2.15 | 5.18 | 855 |
| Bus 7 | 230.0 | 3.08 | 8.02 | 1227 |
| Bus 8 | 230.0 | 2.46 | 6.26 | 978 |
| Bus 9 | 230.0 | 2.77 | 7.10 | 1104 |
Open this in the Builder. The Short circuit at every busbar Quick Start loads in the PowerFactory Workflow Builder, ready for your project. Change any block and download the Python file.
Open in the BuilderThe full Python script from the Builder
The Builder's Quick Start does the same with a loop: a fault at one busbar per pass, the results into a table, and a bar chart of the fault currents at the end. It gives the same 2.256 kA at Bus 5 of the Nine-bus example. This is its file, filled in for that example:
short_circuit_at_every_busbar.py as the Builder writes it: the settings and the workflow⬠Download the full file (.py, 2,604 lines)
#!/usr/bin/env python3
# Short circuit at every busbar
"""Made with the PowerFactory Workflow Builder, https://tool.adjiebrotots.com/powerfactory-scripter/builder/
BEFORE YOU RUN
1. Check the SETTINGS below (lines marked REQUIRED).
2. Close PowerFactory, or run this file from inside PowerFactory (see RUN_MODE).
3. Run it with the Python that matches your PowerFactory, e.g. py -3.12 short_circuit_at_every_busbar.py
Results and run.log go to a new folder next to this file (or in OUTPUT_DIR).
"""
# ==== IMPORTS ====
import csv
import difflib
import fnmatch
import glob
import importlib.util
import math
import os
import re
import subprocess
import sys
import time
import traceback
import warnings
# ==== SETTINGS (edit here) ====
# The name of this workflow: used for the run folder and in the log.
WORKFLOW_NAME = "Short circuit at every busbar"
# REQUIRED. The PowerFactory project to open, exactly as the Data Manager shows it (letter case
# counts).
PROJECT = "Nine-bus System"
# OPTIONAL. The study case to activate first ("" = keep the active one).
STUDY_CASE = ""
# OPTIONAL. Where each run writes its folder of results and run.log ("" = the folder of this
# script).
OUTPUT_DIR = ""
# OPTIONAL. True: put every value the workflow changed back when it ends.
UNDO_CHANGES = True
# OPTIONAL. How the results tables are saved in the run folder when the run ends: "csv", "xlsx"
# (Excel, needs pandas and openpyxl) or "both".
TABLE_FORMAT = "csv"
# OPTIONAL. Nominal frequency in Hz for the frequency checks: 50, 60 or "auto" (read from the
# grid).
F_NOMINAL = "auto"
# OPTIONAL. "auto", "engine" (start PowerFactory in the background) or "inside" (run from inside
# PowerFactory).
RUN_MODE = "auto"
# OPTIONAL. True: show the PowerFactory window while running (engine mode only).
SHOW_PF = False
# OPTIONAL. True: no screen updates and fewer output window messages while running.
FAST_MODE = True
# OPTIONAL. Seconds to wait for PowerFactory to start.
CONNECT_TIMEOUT = 120
# OPTIONAL. The folder with the powerfactory Python module ("" = find it).
PF_API_PATH = ""
# OPTIONAL. Log in to PowerFactory as this user ("" = your own).
PF_USER = ""
# How every graph of this run looks: set in the Builder (select nothing, then Graph style).
# Colours are hex codes. A value that cannot be used falls back to the Adjie Brotools style.
GRAPH_STYLE = {
"template": "brotools", # where this style started (a name only)
"colors": ["#5A91E8", "#B45309", "#4A9E87", "#C46A7E", "#9B59B6"], # in order; more lines repeat them, paler
"ramp": "blue", # blue, night, viridis, cividis, magma, coolwarm, greys, classic
"background": "#FFFFFF",
"plot_background": "#FFFFFF",
"text": "#14203A",
"muted": "#4C5B76",
"grid": "#C8D4E8",
"axis": "#C8D4E8",
"font": "DM Sans",
"font_size": 9.5, # points, for the axis numbers (the rest follows)
"title_size": 13, # points
"title_bold": True, # True or False
"title_align": "center", # center, left
"grid_axes": "both", # both, y, x, none
"grid_style": "solid", # solid, dashed, dotted
"frame": "left-bottom", # left-bottom, box, bottom, none
"ticks": "out", # out, in, none
"thousands": True, # True: 1,200 rather than 1200
"line_width": 2, # points
"markers": "auto", # auto, always, never
"line_styles": "solid", # solid, vary
"legend": "bottom", # bottom, top, right, inside, none
"legend_frame": False, # True or False
"width": 10, # inches
"height": 5.6, # inches
"dpi": 150, # the PNG is width x dpi pixels wide
"transparent": False, # True: no background (for slides)
"also_save": "", # "", svg, pdf
}
# REQUIRED. Busbar: Elements. A name (ElmTerm), exactly as in the Data Manager, or a pattern
# with * and ?: every element that matches is used.
BUSBAR_EL = "*"
# True: connect, find every element named in this workflow, say what was found, then stop
# without running anything.
CHECK_ONLY = False
# ==== WORKFLOW ====
# Note: This runs an IEC 60909 maximum three-phase short circuit at each busbar in service in turn (or at the busbars named on For each element) and reads the initial short-circuit current and power there....
def main():
preflight(["matplotlib"])
app = pf_connect()
try:
if CHECK_ONLY:
check_elements(app, [(BUSBAR_EL + ".ElmTerm", "all")])
return
# -- Step 1: Start (Project: Nine-bus System) --
# -- Step 2: Busbar (Each of all * (ElmTerm)) --
busbar_elements = as_items(pf_find(app, BUSBAR_EL + ".ElmTerm", "all", in_service=True))
if not busbar_elements:
log("Busbar: no element to loop over.", "warn")
for busbar_pass_no, busbar in enumerate(busbar_elements, 1):
ctx_push("Busbar", busbar, busbar_pass_no, len(busbar_elements))
try:
# -- Step 3: Run short circuit (IEC 60909, three-phase, max, at the element) --
shc = run_short_circuit(app, "iec", "3ph", "max", busbar, 0, 0)
# Failed: skips to the next pass of Busbar
if not shc.ok:
case_skipped("Short circuit failed")
continue
# -- Step 4: Ikss (m:Ikss of the element (short-circuit results)) --
ikss = pf_get_all(busbar, "m:Ikss", "kA", False, True)
# -- Step 5: Skss (m:Skss of the element (short-circuit results)) --
skss = pf_get_all(busbar, "m:Skss", "MVA", False, True)
# -- Step 6: Add row to results table (Into "Short circuit", saved at the end automatically) --
table("Short circuit").add({
"Ikss (kA)": ikss.value,
"Skss (MVA)": skss.value,
}, status=True)
except StopWorkflow:
raise
except Exception as err:
case_failed(err)
finally:
ctx_pop(["Short circuit"])
# -- Step 7: Fault current at every busbar (Bars) --
fault_currents = table_pick("Short circuit", "Ikss (kA)", "all", None, "num") # Fault currents
busbars = table_pick("Short circuit", "Busbar", "all", None, "text") # Busbars
plot_xy(busbars, [fault_currents], labels=["Ikss (kA)"], kind="bar", title="Fault current at every busbar", xlabel="Busbar", xunit="", ylabel="Initial short-circuit current", yunit="kA", limits=[], stem=None)
except StopWorkflow as stop:
log(f"Stopped: {stop}")
finally:
finish(app)
The file goes on with 2,455 lines of helper functions copied from the Builder's runtime (connecting, logging, tables, graphs, putting values back), only those this workflow needs. The download has them all.
How the script works
- Step 2 finds every busbar in service that matches
BUSBAR_EL(*for all) and loops over them. - Step 3,
run_short_circuit, setsComShcfor this run only (IEC 60909, three-phase, maximum, the fault at this pass's busbar throughshcobj) and puts the settings back. - Steps 4 and 5 read
m:Ikssin kA andm:Skssin MVA at the faulted busbar; step 6 adds the row. - After the loop, two "Table column" blocks pull the busbar names and the currents out of the table, and "Draw 2D graph" saves the bar chart as a PNG in the Adjie Brotools style.
Pitfalls
- Set the method every time. The Nine-bus example's short-circuit command is set to VDE 0102 (
iopt_mde0). A script that does not setiopt_mdegets whatever the study case holds. iopt_shcis text, not a number:"3psc","2psc","spgf","2pgf".- With one fault location, another busbar's
m:Ikssis not its fault level. Only the faulted busbar carries its own fault current. Useiopt_allbus = 2, or loop and fault each busbar, to get a fault level everywhere. - Maximum and minimum currents answer different questions. Maximum (
iopt_cur0) rates equipment; minimum (1) checks protection reaches. Run both when you need both. - ANSI gives other result names than IEC 60909, so a script written for one method may raise
AttributeErrorwith the other. Check the busbar's short-circuit results in the ElmTerm reference. - Put the command's settings back (the script does it in
finally), or the next person to open the study case gets your method and fault type.
Questions
How do I run a short circuit in PowerFactory with Python?
Get the command with app.GetFromStudyCase("ComShc"), set iopt_mde, iopt_shc, iopt_cur and the location (iopt_allbus or shcobj), then call Execute(). It returns 0 when it worked.
How do I get the fault level at every busbar?
Set iopt_allbus to 2 before Execute(), then read m:Ikss (kA) and m:Skss (MVA) on every ElmTerm.
What does iopt_mde mean?
The calculation method of ComShc: 0 VDE 0102, 1 IEC 60909, 2 ANSI, 3 the complete method, 4 IEC 61363.
What are Ikss, ip and Skss?
Ikss (Ik") is the initial symmetrical short-circuit current, ip the peak short-circuit current, and Skss (Sk") the initial short-circuit power, β3 Un Ik".
Related
- Automate PowerFactory studies with Python: every guide
- PowerFactory Python API: getting started
- Load flow and results to CSV or Excel
- N-1 contingency analysis
- Hosting capacity study
- Parameter sweep: PV and QV curves
- RMS and EMT simulation batch runs
Attributes used on this page: ElmTerm, ElmLne, ElmSym, ElmXnet, EvtShc. All attributes.