System

The System is the main container of components and the time series data references. PowerSystems.jl uses a hybrid approach to data storage, where the component data and time series references are stored in volatile memory while the actual time series data is stored in an HDF5 file. This design loads into memory the portions of the data that are relevant at time of the query, and so avoids overwhelming the memory resources.

Accessing components stored in the System

PowerSystems.jl implements a wide variety of methods to search for components to aid in data manipulation. Most of these use the Type Structure to retrieve all components of a certain Type.

For example, the most common search function is get_components, which takes a desired device Type (concrete or abstract) and retrieves all components in that category from the System. It also accepts filter functions for a more refined search.

Given the potential size of the return, PowerSystems.jl returns Julia iterators in order to avoid unnecessary memory allocations. The container is optimized for iteration over abstract or concrete component types as described by the Type Structure.

Accessing data stored in a component

Using the "dot" access to get a parameter value from a component is actively discouraged, use "getter" functions instead

Using code autogeneration, PowerSystems.jl implements accessor (or "getter") functions to enable the retrieval of parameters defined in the component struct fields. Julia syntax enables access to this data using the "dot" access (e.g. component.field), however this is actively discouraged for two reasons:

  1. We make no guarantees on the stability of component structure definitions. We will maintain version stability on the accessor methods.
  2. Per-unit conversions are made in the return of data from the accessor functions. (see the per-unit section for more details)

Using subsystems

For certain applications, such as those that employ dispatch coordination methods or decomposition approaches, it is useful to be able to split components into subsystems based upon 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.

The following commands demonstrate how to create subsystems and add components.

julia> using PowerSystems;
julia> using PowerSystemCaseBuilder;
julia> sys = build_system(PSISystems, "c_sys5_pjm")┌ Info: Building new system c_sys5_pjm from raw data sys_descriptor.raw_data = "/home/runner/.julia/artifacts/45fbc4fe058ae508e0d03c697ca276c3484a9c5e/PowerSystemsTestData-3.2/psy_data/data_5bus_pu.jl" [ Info: rating 200.0 MW for 1 is outside the expected range (min = 327.0, max = 797.0) MW for Line at a 230.0 kV Voltage level. [ Info: rating 200.0 MW for 2 is outside the expected range (min = 327.0, max = 797.0) MW for Line at a 230.0 kV Voltage level. ┌ Warning: rating 1881.2 MW for 3 is 2x larger than the max expected rating 797.0 MW for Line at a 230.0 kV Voltage level. @ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/branchdata_checks.jl:102 [ Info: rating 1114.8 MW for 4 is outside the expected range (min = 327.0, max = 797.0) MW for Line at a 230.0 kV Voltage level. ┌ Warning: rating 4053.0 MW for 5 is 2x larger than the max expected rating 797.0 MW for Line at a 230.0 kV Voltage level. @ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/branchdata_checks.jl:102 [ Info: rating 200.0 MW for 6 is outside the expected range (min = 327.0, max = 797.0) MW for Line at a 230.0 kV Voltage level. [ Info: Serialized time series data to /home/runner/.julia/packages/PowerSystemCaseBuilder/dJGb8/data/serialized_system/614e094ea985a55912fc1321256a49b755f9fc451c0f264f24d6d04af20e84d7/c_sys5_pjm_time_series_storage.h5. [ Info: Serialized System to /home/runner/.julia/packages/PowerSystemCaseBuilder/dJGb8/data/serialized_system/614e094ea985a55912fc1321256a49b755f9fc451c0f264f24d6d04af20e84d7/c_sys5_pjm.json [ Info: Serialized System metadata to /home/runner/.julia/packages/PowerSystemCaseBuilder/dJGb8/data/serialized_system/614e094ea985a55912fc1321256a49b755f9fc451c0f264f24d6d04af20e84d7/c_sys5_pjm_metadata.json System ┌───────────────────┬─────────────┐ │ Property │ Value │ ├───────────────────┼─────────────┤ │ Name │ │ │ Description │ │ │ System Units Base │ SYSTEM_BASE │ │ Base Power │ 100.0 │ │ Base Frequency │ 60.0 │ │ Num Components │ 27 │ └───────────────────┴─────────────┘ Static Components ┌───────────────────┬───────┐ │ Type │ Count │ ├───────────────────┼───────┤ │ ACBus │ 5 │ │ Arc │ 6 │ │ Line │ 6 │ │ PowerLoad │ 3 │ │ RenewableDispatch │ 2 │ │ ThermalStandard │ 5 │ └───────────────────┴───────┘ StaticTimeSeries Summary ┌───────────────────┬────────────────┬──────────────────┬─────────────────────── │ owner_type │ owner_category │ time_series_type │ initial_timestamp ⋯ │ String │ String │ String │ String ⋯ ├───────────────────┼────────────────┼──────────────────┼─────────────────────── │ PowerLoad │ Component │ SingleTimeSeries │ 2024-01-01T00:00:00 ⋯ │ RenewableDispatch │ Component │ SingleTimeSeries │ 2024-01-01T00:00:00 ⋯ └───────────────────┴────────────────┴──────────────────┴─────────────────────── 3 columns omitted
julia> add_subsystem!(sys, "1")
julia> add_subsystem!(sys, "2")

Devices in the system can be assigned to the subsystems in the following way using the function add_component_to_subsystem!

julia> g = get_component(ThermalStandard, sys, "Alta")ThermalStandard: Alta:
   name: Alta
   available: true
   status: true
   bus: ACBus: nodeA
   active_power: 0.4
   reactive_power: 0.01
   rating: 0.5
   active_power_limits: (min = 0.0, max = 0.4)
   reactive_power_limits: (min = -0.3, max = 0.3)
   ramp_limits: nothing
   operation_cost: ThermalGenerationCost composed of variable: CostCurve{LinearCurve}
   base_power: 100.0
   time_limits: nothing
   must_run: false
   prime_mover_type: PrimeMovers.ST = 20
   fuel: ThermalFuels.COAL = 1
   services: 0-element Vector{Service}
   time_at_status: 10000.0
   dynamic_injector: nothing
   ext: Dict{String, Any}()
   InfrastructureSystems.SystemUnitsSettings:
      base_value: 100.0
      unit_system: UnitSystem.SYSTEM_BASE = 0
   has_supplemental_attributes: false
   has_time_series: false
julia> add_component_to_subsystem!(sys, "1", g)
julia> g = get_component(ThermalStandard, sys, "Sundance")ThermalStandard: Sundance: name: Sundance available: true status: true bus: ACBus: nodeD active_power: 2.0 reactive_power: 0.4 rating: 2.5 active_power_limits: (min = 0.0, max = 2.0) reactive_power_limits: (min = -1.5, max = 1.5) ramp_limits: (up = 0.0375, down = 0.0375) operation_cost: ThermalGenerationCost composed of variable: CostCurve{LinearCurve} base_power: 100.0 time_limits: (up = 2.0, down = 1.0) must_run: false prime_mover_type: PrimeMovers.ST = 20 fuel: ThermalFuels.COAL = 1 services: 0-element Vector{Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}() InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false
julia> add_component_to_subsystem!(sys, "2", g)

To retrieve components assigned to a specific subsystem, add the subsystem_name keyword argument to get_components.

julia> gens_1 = get_components(ThermalStandard, sys; subsystem_name = "1")ThermalStandard Counts:
ThermalStandard: 1
julia> get_name.(gens_1)1-element Vector{String}: "Alta"
julia> gens_2 = get_components(ThermalStandard, sys; subsystem_name = "2")ThermalStandard Counts: ThermalStandard: 1
julia> get_name.(gens_2)1-element Vector{String}: "Sundance"

One useful feature that requires care when used is generating a new System from a subsystem assignment.

The function from_subsystem will allow the user to produce a new System that can be used or exported. This functionality requires careful subsystem assignemnt of the devices and its dependencies. In the current example this code will as follows

julia> from_subsystem(sys, "1")┌ Error: Model doesn't contain a slack bus
@ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/system_checks.jl:27
┌ Warning: There are no ACBus Components in the System
@ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/system_checks.jl:59
┌ Warning: There are no ElectricLoad Components in the System
@ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/system_checks.jl:59
ERROR: InfrastructureSystems.InvalidValue("A component must be assigned to at least one subsystem as its topological component(s).ThermalStandard: Alta: [\"1\"] ACBus: nodeA: String[] diff: [\"1\"]")

The system is invalid because the bus connected to the Alta generator is not part of the subsystem. We can add it, and then run from_subsystem again

julia> g = get_component(ThermalStandard, sys, "Alta")ThermalStandard: Alta:
   name: Alta
   available: true
   status: true
   bus: ACBus: nodeA
   active_power: 0.4
   reactive_power: 0.01
   rating: 0.5
   active_power_limits: (min = 0.0, max = 0.4)
   reactive_power_limits: (min = -0.3, max = 0.3)
   ramp_limits: nothing
   operation_cost: ThermalGenerationCost composed of variable: CostCurve{LinearCurve}
   base_power: 100.0
   time_limits: nothing
   must_run: false
   prime_mover_type: PrimeMovers.ST = 20
   fuel: ThermalFuels.COAL = 1
   services: 0-element Vector{Service}
   time_at_status: 10000.0
   dynamic_injector: nothing
   ext: Dict{String, Any}()
   InfrastructureSystems.SystemUnitsSettings:
      base_value: 100.0
      unit_system: UnitSystem.SYSTEM_BASE = 0
   has_supplemental_attributes: false
   has_time_series: false
julia> b = get_bus(g)ACBus: nodeA: number: 1 name: nodeA bustype: ACBusTypes.PV = 2 angle: 0.0 magnitude: 1.0 voltage_limits: (min = 0.9, max = 1.05) base_voltage: 230.0 area: nothing load_zone: nothing ext: Dict{String, Any}() InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false
julia> add_component_to_subsystem!(sys, "1", b)
julia> from_subsystem(sys, "1")┌ Error: Model doesn't contain a slack bus @ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/system_checks.jl:27 ┌ Warning: There are no ElectricLoad Components in the System @ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/system_checks.jl:59 System ┌───────────────────┬─────────────┐ │ Property │ Value │ ├───────────────────┼─────────────┤ │ Name │ │ │ Description │ │ │ System Units Base │ SYSTEM_BASE │ │ Base Power │ 100.0 │ │ Base Frequency │ 60.0 │ │ Num Components │ 2 │ └───────────────────┴─────────────┘ Static Components ┌─────────────────┬───────┐ │ Type │ Count │ ├─────────────────┼───────┤ │ ACBus │ 1 │ │ ThermalStandard │ 1 │ └─────────────────┴───────┘

Advanced users can use the keyword runchecks=false and avoid any topological check in the process. It is highly recommended that users only do this if they clearly understand how to validate the resulting system before using it for modeling.