From 3f38992da0a428e9af3f64524791d4305dd47790 Mon Sep 17 00:00:00 2001 From: Daniel Thom Date: Thu, 1 Feb 2024 17:45:15 -0700 Subject: [PATCH 1/2] Add support for subsystems --- src/PowerSystems.jl | 15 ++ src/base.jl | 33 ++++- src/subsystems.jl | 283 +++++++++++++++++++++++++++++++++++++ test/common.jl | 61 ++++++++ test/test_serialization.jl | 52 ++----- test/test_subsystems.jl | 225 +++++++++++++++++++++++++++++ 6 files changed, 620 insertions(+), 49 deletions(-) create mode 100644 src/subsystems.jl create mode 100644 test/test_subsystems.jl diff --git a/src/PowerSystems.jl b/src/PowerSystems.jl index 9d61947807..f4add98089 100644 --- a/src/PowerSystems.jl +++ b/src/PowerSystems.jl @@ -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 +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 @@ -578,6 +592,7 @@ include("outages.jl") # Definitions of PowerSystem include("base.jl") +include("subsystems.jl") include("data_format_conversions.jl") #Data Checks diff --git a/src/base.jl b/src/base.jl index 1d48217820..b6c48d33b0 100644 --- a/src/base.jl +++ b/src/base.jl @@ -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 @@ -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 """ @@ -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 """ @@ -1408,6 +1415,7 @@ function check(sys::System) buscheck(buses) critical_components_check(sys) adequacy_check(sys) + check_components(sys) return end @@ -1415,8 +1423,21 @@ 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 diff --git a/src/subsystems.jl b/src/subsystems.jl new file mode 100644 index 0000000000..8a5c2f1baa --- /dev/null +++ b/src/subsystems.jl @@ -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. + +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) + 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 diff --git a/test/common.jl b/test/common.jl index 037635f167..88c3975dcd 100644 --- a/test/common.jl +++ b/test/common.jl @@ -156,6 +156,51 @@ function create_system_with_dynamic_inverter() return sys end +function create_system_with_regulation_device() + sys = PSB.build_system(PSITestSystems, "test_RTS_GMLC_sys"; add_forecasts = false) + # Add an AGC service to cover its special serialization. + control_area = get_component(Area, sys, "1") + AGC_service = PSY.AGC(; + name = "AGC_Area1", + available = true, + bias = 739.0, + K_p = 2.5, + K_i = 0.1, + K_d = 0.0, + delta_t = 4, + area = control_area, + ) + initial_time = Dates.DateTime("2020-01-01T00:00:00") + end_time = Dates.DateTime("2020-01-01T23:00:00") + dates = collect(initial_time:Dates.Hour(1):end_time) + data = collect(1:24) + name = "active_power" + contributing_devices = Vector{Device}() + for g in get_components( + x -> (get_prime_mover_type(x) ∈ [PrimeMovers.ST, PrimeMovers.CC, PrimeMovers.CT]), + ThermalStandard, + sys, + ) + if get_area(get_bus(g)) != control_area + continue + end + ta = TimeSeries.TimeArray(dates, data, [Symbol(get_name(g))]) + time_series = IS.SingleTimeSeries(; + name = name, + data = ta, + scaling_factor_multiplier = get_active_power, + ) + add_time_series!(sys, g, time_series) + + t = RegulationDevice(g; participation_factor = (up = 1.0, dn = 1.0), droop = 0.04) + add_component!(sys, t) + @test isnothing(get_component(ThermalStandard, sys, get_name(g))) + push!(contributing_devices, t) + end + add_service!(sys, AGC_service, contributing_devices) + return sys +end + """ Create a system with supplemental attributes with the criteria below. @@ -200,6 +245,22 @@ function create_system_with_outages() return sys end +function create_system_with_subsystems() + sys = PSB.build_system( + PSITestSystems, + "test_RTS_GMLC_sys"; + add_forecasts = true, + time_series_read_only = false, + ) + add_subsystem!(sys, "subsystem_1") + for component in iterate_components(sys) + add_component_to_subsystem!(sys, "subsystem_1", component) + end + + # TODO: Replace with multiple valid subsystems + return sys +end + function test_accessors(component) ps_type = typeof(component) diff --git a/test/test_serialization.jl b/test/test_serialization.jl index e69739b041..16e5d0b056 100644 --- a/test/test_serialization.jl +++ b/test/test_serialization.jl @@ -1,52 +1,11 @@ @testset "Test JSON serialization of RTS data with RegulationDevice" begin - sys = PSB.build_system(PSITestSystems, "test_RTS_GMLC_sys"; add_forecasts = false) - # Add an AGC service to cover its special serialization. - control_area = get_component(Area, sys, "1") - AGC_service = PSY.AGC(; - name = "AGC_Area1", - available = true, - bias = 739.0, - K_p = 2.5, - K_i = 0.1, - K_d = 0.0, - delta_t = 4, - area = control_area, - ) - initial_time = Dates.DateTime("2020-01-01T00:00:00") - end_time = Dates.DateTime("2020-01-01T23:00:00") - dates = collect(initial_time:Dates.Hour(1):end_time) - data = collect(1:24) - name = "active_power" - contributing_devices = Vector{Device}() - for g in get_components( - x -> (get_prime_mover_type(x) ∈ [PrimeMovers.ST, PrimeMovers.CC, PrimeMovers.CT]), - ThermalStandard, - sys, - ) - if get_area(get_bus(g)) != control_area - continue - end - ta = TimeSeries.TimeArray(dates, data, [Symbol(get_name(g))]) - time_series = IS.SingleTimeSeries(; - name = name, - data = ta, - scaling_factor_multiplier = get_active_power, - ) - add_time_series!(sys, g, time_series) - - t = RegulationDevice(g; participation_factor = (up = 1.0, dn = 1.0), droop = 0.04) - add_component!(sys, t) - @test isnothing(get_component(ThermalStandard, sys, get_name(g))) - push!(contributing_devices, t) - end - add_service!(sys, AGC_service, contributing_devices) - + sys = create_system_with_regulation_device() sys2, result = validate_serialization(sys; time_series_read_only = false) @test result # Ensure the time_series attached to the ThermalStandard got deserialized. for rd in get_components(RegulationDevice, sys2) - @test get_time_series(SingleTimeSeries, rd, name) isa SingleTimeSeries + @test get_time_series(SingleTimeSeries, rd, "active_power") isa SingleTimeSeries end clear_time_series!(sys2) @@ -273,3 +232,10 @@ end @test sys2.metadata.name == name @test sys2.metadata.description == description end + +@testset "Test serialization of subsystems" begin + sys = create_system_with_subsystems() + sys2, result = validate_serialization(sys) + @test result + @test sort!(collect(get_subsystems(sys))) == ["subsystem_1"] +end diff --git a/test/test_subsystems.jl b/test/test_subsystems.jl new file mode 100644 index 0000000000..197ca928cb --- /dev/null +++ b/test/test_subsystems.jl @@ -0,0 +1,225 @@ +function create_system_with_test_subsystems() + sys = PSB.build_system( + PSITestSystems, + "c_sys5_uc"; + add_forecasts = false, + time_series_read_only = true, + ) + + components = collect(get_components(ThermalStandard, sys)) + @test length(components) >= 5 + + subsystems = String[] + for i in 1:3 + name = "subsystem_$i" + add_subsystem!(sys, name) + push!(subsystems, name) + end + + add_component_to_subsystem!(sys, subsystems[1], components[1]) + add_component_to_subsystem!(sys, subsystems[1], components[2]) + add_component_to_subsystem!(sys, subsystems[2], components[2]) + add_component_to_subsystem!(sys, subsystems[2], components[3]) + add_component_to_subsystem!(sys, subsystems[3], components[3]) + add_component_to_subsystem!(sys, subsystems[3], components[4]) + return (sys, components) +end + +@testset "Test get subsystems and components" begin + sys, components = create_system_with_test_subsystems() + @test sort!(collect(get_subsystems(sys))) == + ["subsystem_1", "subsystem_2", "subsystem_3"] + @test has_component(sys, "subsystem_1", components[1]) + @test has_component(sys, "subsystem_1", components[2]) + @test has_component(sys, "subsystem_2", components[2]) + @test has_component(sys, "subsystem_2", components[3]) + @test has_component(sys, "subsystem_3", components[3]) + @test has_component(sys, "subsystem_3", components[4]) + @test !has_component(sys, "subsystem_3", components[5]) + @test sort!(get_name.(get_subsystem_components(sys, "subsystem_2"))) == + sort!([get_name(components[2]), get_name(components[3])]) + @test get_assigned_subsystems(sys, components[1]) == ["subsystem_1"] + @test is_assigned_to_subsystem(sys, components[1]) + @test !is_assigned_to_subsystem(sys, components[5]) + @test is_assigned_to_subsystem(sys, components[1], "subsystem_1") + @test !is_assigned_to_subsystem(sys, components[5], "subsystem_1") + @test_throws ArgumentError add_subsystem!(sys, "subsystem_1") +end + +@testset "Test get_components" begin + sys, components = create_system_with_test_subsystems() + @test length( + get_components(ThermalStandard, sys; subsystem_name = "subsystem_1"), + ) == 2 + name = get_name(components[1]) + @test collect( + get_components( + x -> x.name == name, + ThermalStandard, + sys; + subsystem_name = "subsystem_1", + ), + )[1].name == name +end + +@testset "Test subsystem after remove_component" begin + sys, components = create_system_with_test_subsystems() + remove_component!(sys, components[3]) + @test !has_component(sys, "subsystem_2", components[3]) + @test !has_component(sys, "subsystem_3", components[3]) + @test has_component(sys, "subsystem_2", components[2]) + @test has_component(sys, "subsystem_3", components[4]) +end + +@testset "Test removal of subsystem" begin + sys, components = create_system_with_test_subsystems() + remove_subsystem!(sys, "subsystem_2") + @test sort!(collect(get_subsystems(sys))) == ["subsystem_1", "subsystem_3"] + @test get_assigned_subsystems(sys, components[2]) == ["subsystem_1"] + @test_throws ArgumentError remove_subsystem!(sys, "subsystem_2") +end + +@testset "Test removal of subsystem component" begin + sys, components = create_system_with_test_subsystems() + remove_component_from_subsystem!(sys, "subsystem_2", components[2]) + @test get_name.(get_subsystem_components(sys, "subsystem_2")) == + [get_name(components[3])] + @test_throws ArgumentError remove_component_from_subsystem!( + sys, + "subsystem_2", + components[2], + ) +end + +@testset "Test addition of component to invalid subsystem" begin + sys, components = create_system_with_test_subsystems() + @test_throws ArgumentError add_component_to_subsystem!(sys, "invalid", components[1]) +end + +@testset "Test addition of duplicate component to subsystem" begin + sys, components = create_system_with_test_subsystems() + @test_throws ArgumentError add_component_to_subsystem!( + sys, + "subsystem_1", + components[1], + ) +end + +@testset "Test addition of non-system component" begin + sys, components = create_system_with_test_subsystems() + remove_component!(sys, components[1]) + @test_throws ArgumentError add_component_to_subsystem!( + sys, + "subsystem_1", + components[1], + ) +end + +@testset "Test invalid subsystem count" begin + sys = PSB.build_system( + PSITestSystems, + "c_sys5_uc"; + add_forecasts = false, + time_series_read_only = true, + ) + num_buses = length(get_components(Bus, sys)) + for i in 1:num_buses + add_subsystem!(sys, "subsystem_$i") + end + @test_throws "cannot exceed the number of buses" add_subsystem!(sys, "subsystem") +end + +@testset "Test valid subsystem" begin + sys = create_system_with_subsystems() + check_components(sys) +end + +@testset "Test inconsistent component-subsystem membership" begin + sys, components = create_system_with_test_subsystems() + @test_throws IS.InvalidValue check_components(sys) +end + +@testset "Test mismatch component-topological membership" begin + sys = create_system_with_subsystems() + add_subsystem!(sys, "incomplete_subsystem") + gen = first(get_components(ThermalStandard, sys)) + bus = get_bus(gen) + remove_component_from_subsystem!(sys, "subsystem_1", bus) + add_component_to_subsystem!(sys, "incomplete_subsystem", bus) + @test_throws IS.InvalidValue PSY._check_topological_consistency(sys, gen) +end + +@testset "Test mismatch branch-arc membership" begin + sys = create_system_with_subsystems() + add_subsystem!(sys, "incomplete_subsystem") + branch = first(get_components(Branch, sys)) + arc = get_arc(branch) + remove_component_from_subsystem!(sys, "subsystem_1", arc) + add_component_to_subsystem!(sys, "incomplete_subsystem", arc) + @test_throws IS.InvalidValue PSY._check_branch_consistency(sys, branch) +end + +@testset "Test service-contributing-device consistency" begin + sys = create_system_with_subsystems() + add_subsystem!(sys, "incomplete_subsystem") + device = nothing + service = nothing + for dev in get_components(Device, sys) + services = get_services(dev) + if !isempty(services) + device = dev + service = first(services) + break + end + end + @test !isnothing(device) && !isnothing(service) + + remove_component_from_subsystem!(sys, "subsystem_1", device) + add_subsystem!(sys, "subsystem_2") + add_component_to_subsystem!(sys, "subsystem_2", device) + + @test_throws IS.InvalidValue PSY._check_device_service_consistency(sys, device) +end + +@testset "Test subsystems with HybridSystem" begin + sys = PSB.build_system(PSB.PSITestSystems, "test_RTS_GMLC_sys_with_hybrid") + h_sys = first(get_components(HybridSystem, sys)) + subcomponents = collect(get_subcomponents(h_sys)) + + subsystem_name = "subsystem_1" + add_subsystem!(sys, subsystem_name) + add_component_to_subsystem!(sys, subsystem_name, h_sys) + # Ensure that subcomponents get added automatically. + for subcomponent in subcomponents + @test is_assigned_to_subsystem(sys, subcomponent, subsystem_name) + end + + remove_component_from_subsystem!(sys, subsystem_name, subcomponents[1]) + @test_throws IS.InvalidValue PSY._check_subcomponent_consistency(sys, h_sys) + + add_component_to_subsystem!(sys, subsystem_name, subcomponents[1]) + remove_component_from_subsystem!(sys, subsystem_name, h_sys) + # Ensure that subcomponents get removed automatically. + for subcomponent in subcomponents + @test !is_assigned_to_subsystem(sys, subcomponent, subsystem_name) + end +end + +@testset "Test subsystems with RegulationDevice" begin + sys = create_system_with_regulation_device() + rd = first(get_components(RegulationDevice, sys)) + + subsystem_name = "subsystem_1" + add_subsystem!(sys, subsystem_name) + add_component_to_subsystem!(sys, subsystem_name, rd) + # Ensure that its device gets added automatically. + @test is_assigned_to_subsystem(sys, rd.device, subsystem_name) + + remove_component_from_subsystem!(sys, subsystem_name, rd.device) + @test_throws IS.InvalidValue PSY._check_subcomponent_consistency(sys, rd) + + add_component_to_subsystem!(sys, subsystem_name, rd.device) + remove_component_from_subsystem!(sys, subsystem_name, rd) + # Ensure that its device gets removed automatically. + @test !is_assigned_to_subsystem(sys, rd.device, subsystem_name) +end From 0b94d9b268bbcc817e0058fbc585da97dba05192 Mon Sep 17 00:00:00 2001 From: Daniel Thom Date: Mon, 5 Feb 2024 13:58:50 -0700 Subject: [PATCH 2/2] Make check more robust --- src/subsystems.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subsystems.jl b/src/subsystems.jl index 8a5c2f1baa..49331abb71 100644 --- a/src/subsystems.jl +++ b/src/subsystems.jl @@ -185,7 +185,7 @@ end function _check_num_subsystems(sys::System) num_buses = length(sys.bus_numbers) - if num_buses == get_num_subsystems(sys) + if get_num_subsystems(sys) >= num_buses throw( IS.InvalidValue( "The number of subsystems cannot exceed the number of buses: $num_buses",