Subsystems

This tutorial covers the grouping class of powfacpy named SubSystem which extends the functionality of the PowerFactory grouping classes (Zone, Area, Boundary). The SubSystem class is designed to represent coherent groups of elements in the power system and provides methods for analyzing and manipulating these groups. The SubSystem class inherits from the Zone class described in the Topology and Groupings tutorial. It became necessary to define the SubSystem class in powfacpy because the functionality goes way beyond the native PowerFactory classes. Furthermore, there is a class for a group of Subsystems named SubSystemContainer which provides methods to analyze and manipulate groups of SubSystems.

Examples for the functionality of SubSystem include (and will be extended in future):

Examples for the functionality of SubSystemContainer include (and will be extended in future):

We first activate the IEEE 39 bus system project and create a study case for this tutorial.

# If you use IPython/Jupyter:
import sys

sys.path.append(
    r"C:\Program Files\DIgSILENT\PowerFactory 2025 SP3\Python\3.13"
)  # you may use a different directory

# Get the PF app
import powerfactory
from powfacpy.base.active_project import ActiveProject

app = powerfactory.GetApplication()
app.Show()
app.ActivateProject(
    r"powfacpy\39_bus_new_england_copy_where_tests_run"
)  # You may change the project path.
act_prj = ActiveProject(app)

study_case = act_prj.copy_single_obj("Study Cases\\2.3 Simulation Fault Bus 31 Stable", "Study Cases", new_name="SubSystems tutorial")
study_case.Activate()
act_prj.clear_folder(act_prj.zones_folder)
act_prj.clear_folder(act_prj.boundaries_folder)
act_prj.clear_folder(act_prj.areas_folder)
act_prj.create_variation("SubSystems tutorial")
<powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Variations.IntPrjfolder\SubSystems tutorial.IntScheme</l1>>

1 Subsystems

1.1 Create

We want to create two subsystems which represent the western and eastern part of the system. This is best done by creating two boundaries and then creating zones from these boundaries. The SubSystem class can then be used to create subsystems from these zones.

from powfacpy.applications.topology import Topology
from powfacpy.pf_classes.elm.boundary import Boundary
from powfacpy.pf_classes.elm.zone import Zone
from powfacpy.applications.subsystems import SubSystem

try:
    app.Hide()
    topo = Topology(app)
    bus_branch = {
        r"Network Model\Network Data\Grid\Bus 14": [
            r"Network Model\Network Data\Grid\Line 04 - 14",
            r"Network Model\Network Data\Grid\Line 13 - 14",
        ],
        r"Network Model\Network Data\Grid\Bus 16": [
            r"Network Model\Network Data\Grid\Line 16 - 17"
        ],
    }
    boundary_west = Boundary(topo.create_boundary_from_bus_branch("West", bus_branch))
    zone_west = boundary_west.create_zone()
    boundary_east = Boundary(topo.create_boundary_from_bus_branch("East", bus_branch, to_branch=False),)
    zone_east = boundary_east.create_zone(color=3)
    Zone.show_zones_in_network_graphic()
    subsystems = [SubSystem(zone_east), SubSystem(zone_west)]
finally:
    app.Show()

1.2 Load Flow State

We can get a summary of the load flow state of the subsystem by calling the get_load_flow_state() method. This returns a pandas DataFrame with information on the active and reactive power balance, etc.

try:
    for subsys in subsystems:
        display(subsys.get_load_flow_state())
finally:
    app.Show()        
total_power_loads total_power_generation total_power_exchange
East 2107.100006+ 395.700001j 2350.000000+ 681.456464j 224.540595+ 2.324056j
total_power_loads total_power_generation total_power_exchange
West 3990.000003+1013.200001j 3790.811072+ 661.117299j -224.540595- 2.324056j

The load flow state can be accessed also as attributes, for example:

subsystems[0].load_flow.total_power_generation
(2350.0000000274126+681.4564641218697j)

1.3 Topology

We can access the internal topology of the subsystem, for example the lines and terminals contained in the subsystem.

try:
    app.Hide()
    display(subsystems[0].topology.lines)
    display(subsystems[0].topology.terminals)
finally:
    app.Show()  
[<powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 14 - 15.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 15 - 16.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 16 - 17.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 16 - 19.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 16 - 21.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 16 - 24.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 21 - 22.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 22 - 23.ElmLne</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Line 23 - 24.ElmLne</l1>>]
[<powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 14.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 15.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 16.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 19.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 20.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 21.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 22.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 23.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 24.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 33.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 34.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 35.ElmTerm</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Bus 36.ElmTerm</l1>>]

You can also check wheter subsystems are connected to each other:

subsystems[0].topology.is_neighbor(subsystems[1])
True

1.4 Dynamic Models

1.4.1 Synchronous Machines

We can also access information on the synchronous machines and their controllers (governors, AVRs, PSS) contained in the subsystem.

try:
    display(subsystems[0].dynamic_models.synchronous_machines.synchronous_machines)
    display(subsystems[0].dynamic_models.synchronous_machines.governors)
finally:
    app.Show() 
[<powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 04.ElmSym</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 05.ElmSym</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 06.ElmSym</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 07.ElmSym</l1>>]
[<powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Power Plant 04.ElmComp\GOV 04.ElmDsl</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Power Plant 05.ElmComp\GOV 05.ElmDsl</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Power Plant 06.ElmComp\GOV 06.ElmDsl</l1>>,
 <powerfactory.DataObject <l1>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\Power Plant 07.ElmComp\GOV 07.ElmDsl</l1>>]

You can also get more detailled information on dynmic models such as governors. The get_governor_info() method for example returns a dictionary with the block definitions (BlkDef) of the governors as keys and the values are the respective synchronous machines (parent_elms), the parameter values and the average parameter values (weighted by the rated apparent power by default) of the respective DSL models. The information is sorted by block definition, so you can easily compare the parameter values of different synchronous machines with the same block definition.

from powfacpy.pf_classes.elm.dsl import display_dsl_models_info
display_dsl_models_info(subsystems[0].dynamic_models.synchronous_machines.get_governor_info())
Block Definition 'gov_IEEEG1': <l3>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Library.IntPrjfolder\User Defined Models.IntPrjfolder\gov_IEEEG1.BlkDef</l3>
   Parent elements:
      G 04 of class ElmSym (<l3>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 04.ElmSym</l3>)
      G 05 of class ElmSym (<l3>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 05.ElmSym</l3>)
      G 06 of class ElmSym (<l3>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 06.ElmSym</l3>)
      G 07 of class ElmSym (<l3>\seberlein.IntUser\powfacpy\39_bus_new_england_copy_where_tests_run.IntPrj\Network Model.IntPrjfolder\Network Data.IntPrjfolder\Grid.ElmNet\G 07.ElmSym</l3>)
   Parameter values:
      K: [5.0, 5.0, 5.0, 5.0]
      T1: [0.2, 0.2, 0.2, 0.2]
      T2: [1.0, 1.0, 1.0, 1.0]
      T3: [0.6, 0.6, 0.6, 0.6]
      K1: [0.3, 0.3, 0.3, 0.3]
      K2: [0.0, 0.0, 0.0, 0.0]
      T5: [0.5, 0.5, 0.5, 0.5]
      K3: [0.25, 0.25, 0.25, 0.25]
      K4: [0.0, 0.0, 0.0, 0.0]
      T6: [0.8, 0.8, 0.8, 0.8]
      K5: [0.3, 0.3, 0.3, 0.3]
      K6: [0.0, 0.0, 0.0, 0.0]
      T4: [0.6, 0.6, 0.6, 0.6]
      T7: [1.0, 1.0, 1.0, 1.0]
      K7: [0.15, 0.15, 0.15, 0.15]
      K8: [0.0, 0.0, 0.0, 0.0]
      PNhp: [0.0, 0.0, 0.0, 0.0]
      PNlp: [0.0, 0.0, 0.0, 0.0]
      Uc: [-0.3, -0.3, -0.3, -0.3]
      Pmin: [0.0, 0.0, 0.0, 0.0]
      Uo: [0.3, 0.3, 0.3, 0.3]
      Pmax: [1.0, 1.0, 1.0, 1.0]
   Average parameter values:
      K: 5.0
      T1: 0.2
      T2: 1.0
      T3: 0.6
      K1: 0.3
      K2: 0.0
      T5: 0.5
      K3: 0.25
      K4: 0.0
      T6: 0.8
      K5: 0.3
      K6: 0.0
      T4: 0.6
      T7: 1.0
      K7: 0.15
      K8: 0.0
      PNhp: 0.0
      PNlp: 0.0
      Uc: -0.3
      Pmin: 0.0
      Uo: 0.3
      Pmax: 1.0

2 Subsystem Container

Subsystem containers are designed to group multiple subsystems together and provide methods to analyze these groups.

2.1 Create

For example, you can create a subsystem container for the two subsystems we created in the previous section and one additional zone named ‘North’:

from powfacpy.applications.subsystems import SubSystemContainer

try:
    app.Hide()
    terminals_north = act_prj.get_calc_relevant_obj("Bus 30.ElmTerm, Bus 02.ElmTerm, Bus 25.ElmTerm, Bus 37.ElmTerm")
    zone_north = topo.create_zone("North", terminals_north, color=4)
    ss_container = SubSystemContainer([zone_east, zone_west, zone_north])
    Zone.show_zones_in_network_graphic()
finally:    
    app.Show()    

2.2 Load Flow State

We can get a summary of the load flow states of the subsystems:

try:
    app.Hide()
    display(ss_container.get_load_flow_state())
finally:    
    app.Show()    
total_power_loads total_power_generation total_power_exchange
East 2107.100006+ 395.700001j 2350.000000+ 681.456464j 224.540595+ 2.324056j
West 3766.000003+ 966.000000j 3000.811072+ 514.519195j -782.871862- 98.529796j
North 224.000000+ 47.200001j 790.000000+ 146.598105j 558.331267+ 96.205739j

You can also get a matrix (pandas DataFrame) with the power exchange between the subsystems:

try:
    app.Hide()
    display(ss_container.power_exchange)
finally:    
    app.Show()   
East West North
East NaN+ 0.000000j 224.540595+ 2.324056j NaN+ 0.000000j
West -224.540595- 2.324056j NaN+ 0.000000j -558.331267- 96.205739j
North NaN+ 0.000000j 558.331267+ 96.205739j NaN+ 0.000000j

2.3 Topology

We can also get the neighbors of the subsystems:

try:
    app.Hide()
    for subsys in ss_container.subsystems:
        print(f"{subsys.loc_name} has neighbors: ")
        print([ss.loc_name for ss in ss_container.get_neighboring_subsystems_in_container(subsys)])
finally:    
    app.Show()  
East has neighbors: 
['West']
West has neighbors: 
['East', 'North']
North has neighbors: 
['West']
Back to top