Use Subsystems
For certain applications, such as those that employ dispatch coordination methods or decomposition approaches, it is useful to split components into subsystems based on user-defined criteria. The System provides subsystem containers for this purpose. Each subsystem is defined by a name and can hold references to any number of components. For background on the System container, see System.
Create subsystems and add components
Load a System, then call add_subsystem! to register named subsystems:
using PowerSystems;
using PowerSystemCaseBuilder;
sys = build_system(PSISystems, "c_sys5_pjm")
add_subsystem!(sys, "1")
add_subsystem!(sys, "2")[ Info: Loaded time series from storage file existing=/home/runner/.julia/packages/PowerSystemCaseBuilder/3SVVU/data/serialized_system/614e094ea985a55912fc1321256a49b755f9fc451c0f264f24d6d04af20e84d7/c_sys5_pjm_time_series_storage.h5 new=/tmp/jl_jEvzuI compression=CompressionSettings(false, CompressionTypes.DEFLATE = 1, 3, true)Assign devices to subsystems using add_component_to_subsystem!:
g = get_component(ThermalStandard, sys, "Alta")
add_component_to_subsystem!(sys, "1", g)
g = get_component(ThermalStandard, sys, "Sundance")
add_component_to_subsystem!(sys, "2", g)Retrieve components from a subsystem
Pass the subsystem_name keyword argument to get_components to filter by subsystem:
gens_1 = get_components(ThermalStandard, sys; subsystem_name = "1")
get_name.(gens_1)
gens_2 = get_components(ThermalStandard, sys; subsystem_name = "2")
get_name.(gens_2)1-element Vector{String}:
"Sundance"Export a subsystem as a new System
from_subsystem produces a new, standalone System from the components assigned to a subsystem. This requires careful assignment of all dependencies — not just the devices themselves, but also any topology elements (buses, arcs) they reference.
from_subsystem(sys, "1"; runchecks = false)| System | |
| Property | Value |
|---|---|
| Name | |
| Description | |
| System Units Base | SYSTEM_BASE |
| Base Power | 100.0 |
| Base Frequency | 60.0 |
| Num Components | 1 |
| Static Components | |
| Type | Count |
|---|---|
| ThermalStandard | 1 |
The system above was created with runchecks=false and is technically invalid: the bus connected to the Alta generator is not part of subsystem "1". Without runchecks=false, this call would raise an error. Add the bus first, then re-run from_subsystem:
A valid exported System requires three additional components:
- The generator's bus (
nodeA) — every device must have its connected bus present in the subsystem. - A reference (slack) bus (
nodeD) — at least oneACBuswithbustype = ACBusTypes.REFmust be present for the system to pass validation. - An
ElectricLoad— a subsystem with no load components triggers a validation warning. Adding thePowerLoadconnected to the slack bus satisfies this requirement.
g = get_component(ThermalStandard, sys, "Alta")
b = get_bus(g)
add_component_to_subsystem!(sys, "1", b)
ref_bus = get_component(ACBus, sys, "nodeD")
add_component_to_subsystem!(sys, "1", ref_bus)
load = first(get_components(x -> get_bus(x) === ref_bus, PowerLoad, sys))
add_component_to_subsystem!(sys, "1", load)
from_subsystem(sys, "1")| System | |
| Property | Value |
|---|---|
| Name | |
| Description | |
| System Units Base | SYSTEM_BASE |
| Base Power | 100.0 |
| Base Frequency | 60.0 |
| Num Components | 4 |
| Static Components | |
| Type | Count |
|---|---|
| ACBus | 2 |
| PowerLoad | 1 |
| ThermalStandard | 1 |
| StaticTimeSeries Summary | |||||||
| owner_type | owner_category | name | time_series_type | initial_timestamp | resolution | count | time_step_count |
|---|---|---|---|---|---|---|---|
| String | String | String | String | String | Dates.CompoundPeriod | Int64 | Int64 |
| PowerLoad | Component | max_active_power | SingleTimeSeries | 2024-01-01T00:00:00 | 1 hour | 1 | 168 |
Advanced users can pass runchecks=false to skip topological validation. Only do this if you are confident you can validate the resulting system before using it for modeling.