Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for subsystems #1047

Merged
merged 2 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/PowerSystems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,20 @@ export get_renewable_unit
export get_interconnection_rating
export get_interconnection_impedance

# Subsystems
export add_subsystem!
export get_subsystems
export get_num_subsystems
daniel-thom marked this conversation as resolved.
Show resolved Hide resolved
export remove_subsystem!
export add_component_to_subsystem!
export get_subsystem_components
export remove_component_from_subsystem!
export remove_component_from_subsystems!
export has_component
export get_assigned_subsystems
export has_subsystems
export is_assigned_to_subsystem

export set_runchecks!
export check
export check_component
Expand Down Expand Up @@ -578,6 +592,7 @@ include("outages.jl")

# Definitions of PowerSystem
include("base.jl")
include("subsystems.jl")
include("data_format_conversions.jl")

#Data Checks
Expand Down
33 changes: 27 additions & 6 deletions src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ function System(file_path::AbstractString; assign_new_uuids = false, kwargs...)
)
runchecks && check(sys)
if assign_new_uuids
IS.assign_new_uuid_internal!(sys)
IS.assign_new_uuid!(sys)
for component in get_components(Component, sys)
assign_new_uuid!(sys, component)
end
Expand Down Expand Up @@ -334,6 +334,8 @@ function Base.deepcopy(sys::System)
return sys2
end

IS.assign_new_uuid!(sys::System) = IS.assign_new_uuid_internal!(sys)

"""
Return the internal of the system
"""
Expand Down Expand Up @@ -902,16 +904,21 @@ generators = collect(PowerSystems.get_components(Generator, sys))

See also: [`iterate_components`](@ref)
"""
function get_components(::Type{T}, sys::System) where {T <: Component}
return IS.get_components(T, sys.data, nothing)
function get_components(
::Type{T},
sys::System;
subsystem_name = nothing,
) where {T <: Component}
return IS.get_components(T, sys.data; subsystem_name = subsystem_name)
end

function get_components(
filter_func::Function,
::Type{T},
sys::System,
sys::System;
subsystem_name = nothing,
) where {T <: Component}
return IS.get_components(T, sys.data, filter_func)
return IS.get_components(filter_func, T, sys.data; subsystem_name = subsystem_name)
end

"""
Expand Down Expand Up @@ -1408,15 +1415,29 @@ function check(sys::System)
buscheck(buses)
critical_components_check(sys)
adequacy_check(sys)
check_components(sys)
return
end

"""
Check the values of all components. See [`check_component`](@ref) for exceptions thrown.
"""
function check_components(sys::System; check_masked_components = true)
for component in iterate_components(sys)
must_be_assigned_to_subsystem = false
for (i, component) in enumerate(iterate_components(sys))
is_assigned = is_assigned_to_subsystem(sys, component)
if i == 1
must_be_assigned_to_subsystem = is_assigned
elseif is_assigned != must_be_assigned_to_subsystem
throw(
IS.InvalidValue(
"If any component is assigned to a subsystem then all " *
"components must be assigned to a subsystem.",
),
)
end
check_component(sys, component)
check_subsystems(sys, component)
end

if check_masked_components
Expand Down
283 changes: 283 additions & 0 deletions src/subsystems.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
"""
Add a new subsystem to the system.
"""
function add_subsystem!(sys::System, subsystem_name::AbstractString)
_check_num_subsystems(sys)
IS.add_subsystem!(sys.data, subsystem_name)
end

"""
Return the number of subsystems stored in the system.
"""
get_num_subsystems(sys::System) = IS.get_num_subsystems(sys.data)

"""
Return an iterator of all subsystem names in the system.
"""
get_subsystems(sys::System) = IS.get_subsystems(sys.data)

"""
Remove a subsystem from the system.

Throws ArgumentError if the subsystem name is not stored.
"""
remove_subsystem!(sys::System, subsystem_name::AbstractString) =
IS.remove_subsystem!(sys.data, subsystem_name)

"""
Return true if the system has one or more subsystems.
"""
function has_subsystems(sys::System)
for _ in get_subsystems(sys)
return true
end

return false
end

"""
Add a component to a subsystem.
"""
function add_component_to_subsystem!(
sys::System,
subsystem_name::AbstractString,
component::Component,
)
IS.add_component_to_subsystem!(sys.data, subsystem_name, component)
handle_component_addition_to_subsystem!(sys, subsystem_name, component)
return
end

"""
Peforms component-type-specific postprocessing when a component is added to a subsystem.
"""
handle_component_addition_to_subsystem!(
::System,
subsystem_name::AbstractString,
::Component,
) = nothing

"""
Peforms component-type-specific postprocessing when a component is removed from a subsystem.
"""
handle_component_removal_from_subsystem!(
::System,
subsystem_name::AbstractString,
::Component,
) = nothing

function handle_component_addition_to_subsystem!(
sys::System,
subsystem_name::AbstractString,
component::StaticInjectionSubsystem,
)
for subcomponent in get_subcomponents(component)
if !is_assigned_to_subsystem(sys, subcomponent, subsystem_name)
add_component_to_subsystem!(sys, subsystem_name, subcomponent)
end
end
end

function handle_component_removal_from_subsystem!(
sys::System,
subsystem_name::AbstractString,
component::StaticInjectionSubsystem,
)
for subcomponent in get_subcomponents(component)
if is_assigned_to_subsystem(sys, subcomponent, subsystem_name)
remove_component_from_subsystem!(sys, subsystem_name, subcomponent)
end
end
end

function handle_component_addition_to_subsystem!(
sys::System,
subsystem_name::AbstractString,
component::RegulationDevice,
)
if !is_assigned_to_subsystem(sys, component.device, subsystem_name)
add_component_to_subsystem!(sys, subsystem_name, component.device)
end
end

function handle_component_removal_from_subsystem!(
sys::System,
subsystem_name::AbstractString,
component::RegulationDevice,
)
if is_assigned_to_subsystem(sys, component.device, subsystem_name)
remove_component_from_subsystem!(sys, subsystem_name, component.device)
end
end

"""
Return a Generator of all components in the subsystem.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we missing some code here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Management of subsystems occurs in the InfrastructureSystems (IS) layer. We implement storage of components and related objects in this sub-package so that other packages can benefit from the logic. The real function is here: https://github.com/daniel-thom/InfrastructureSystems.jl/blob/subsystems/src/subsystems.jl#L74

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Throws ArgumentError if the subsystem name is not stored.
"""
get_subsystem_components(sys::System, subsystem_name::AbstractString) =
IS.get_subsystem_components(sys.data, subsystem_name)

"""
Remove a component from a subsystem.

Throws ArgumentError if the subsystem name or component is not stored.
"""
function remove_component_from_subsystem!(
sys::System,
subsystem_name::AbstractString,
component::Component,
)
IS.remove_component_from_subsystem!(sys.data, subsystem_name, component)
handle_component_removal_from_subsystem!(sys, subsystem_name, component)
return
end

remove_component_from_subsystems!(
sys::System,
component::Component,
) = remove_component_from_subsystems!(sys.data, component)

"""
Return true if the component is in the subsystem.
"""
has_component(
sys::System,
subsystem_name::AbstractString,
component::Component,
) = IS.has_component(sys.data, subsystem_name, component)

"""
Return a Vector of subsystem names that contain the component.
"""
get_assigned_subsystems(
sys::System,
component::Component,
) = IS.get_assigned_subsystems(sys.data, component)

"""
Return true if the component is assigned to any subsystems.
"""
is_assigned_to_subsystem(sys::System, component::Component) =
IS.is_assigned_to_subsystem(sys.data, component)

"""
Return true if the component is assigned to the subsystem.
"""
is_assigned_to_subsystem(
sys::System,
component::Component,
subsystem_name::AbstractString,
) = IS.is_assigned_to_subsystem(sys.data, component, subsystem_name)

# Utility function, not for export
get_component_uuids(sys::System, subsystem_name::AbstractString) =
IS.get_component_uuids(sys.data, subsystem_name)

function check_subsystems(sys::System, component::Component)
_check_arc_consistency(sys, component)
_check_branch_consistency(sys, component)
_check_device_service_consistency(sys, component)
_check_subcomponent_consistency(sys, component)
_check_topological_consistency(sys, component)
return
end

function _check_num_subsystems(sys::System)
num_buses = length(sys.bus_numbers)
if num_buses == get_num_subsystems(sys)
daniel-thom marked this conversation as resolved.
Show resolved Hide resolved
throw(
IS.InvalidValue(
"The number of subsystems cannot exceed the number of buses: $num_buses",
),
)
end
end

_check_arc_consistency(::System, ::Component) = nothing
_check_branch_consistency(::System, ::Component) = nothing
_check_device_service_consistency(::System, ::Component) = nothing
_check_subcomponent_consistency(::System, ::Component) = nothing

function _check_arc_consistency(sys::System, arc::Arc)
msg = "An arc must be assigned to the same subystems as its buses."
_check_subsystem_assignments(sys, arc, get_from(arc), msg; symmetric_diff = false)
_check_subsystem_assignments(sys, arc, get_to(arc), msg; symmetric_diff = false)
end

function _check_branch_consistency(sys::System, branch::Branch)
msg = "A branch must be assigned to the same subystems as its arc."
_check_subsystem_assignments(sys, branch, get_arc(branch), msg; symmetric_diff = true)
end

function _check_subcomponent_consistency(sys::System, component::StaticInjectionSubsystem)
for subcomponent in get_subcomponents(component)
_check_subsystem_assignments(
sys,
component,
subcomponent,
"StaticInjectionSubsystems and their subcomponents be assigned to the same subsystems.";
symmetric_diff = true,
)
end
end

function _check_subcomponent_consistency(sys::System, component::RegulationDevice)
_check_subsystem_assignments(
sys,
component,
component.device,
"RegulationDevice and its device must be assigned to the same subsystems.";
symmetric_diff = true,
)
end

function _check_topological_consistency(sys::System, component::Component)
for name in fieldnames(typeof(component))
val = getproperty(component, name)
if val isa Topology
_check_subsystem_assignments(
sys,
component,
val,
"A component must be assigned to the same subsystems as its topological component(s).";
symmetric_diff = true,
)
end
end
end

function _check_device_service_consistency(sys::System, device::Device)
for service in get_services(device)
_check_subsystem_assignments(
sys,
device,
service,
"A service must be assigned to the same subsystems as its contributing devices.";
symmetric_diff = true,
)
end
end

function _check_subsystem_assignments(
sys::System,
component1::Component,
component2::Component,
message::AbstractString;
symmetric_diff::Bool,
)
subsys1 = get_assigned_subsystems(sys, component1)
subsys2 = get_assigned_subsystems(sys, component2)
diff_method = symmetric_diff ? symdiff : setdiff
diff = diff_method(subsys1, subsys2)
if !isempty(diff)
throw(
IS.InvalidValue(
message *
"$(summary(component1)): $subsys1 " *
"$(summary(component2)): $subsys2 " *
"diff: $diff",
),
)
end
end
Loading
Loading