From 18910f7d36566d183e2d68d56df54345afff34d3 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:31:32 -0600 Subject: [PATCH 1/5] Add `get_components`, `get_component` methods for results --- src/PowerSimulations.jl | 4 ++ src/simulation/simulation_problem_results.jl | 48 +++++++++++++++++++- src/utils/printing.jl | 1 - test/test_model_decision.jl | 2 + test/test_simulation_results.jl | 5 ++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 099a5f50a5..6dad5ddeee 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -396,6 +396,10 @@ import InfrastructureSystems.Optimization: read_results_with_keys, deserialize_k # IS.Optimization imports that stay private, may or may not be additional methods in PowerSimulations +# PowerSystems imports +import PowerSystems: + get_components, get_available_components, get_component + export get_name export get_model_base_power export get_optimizer_stats diff --git a/src/simulation/simulation_problem_results.jl b/src/simulation/simulation_problem_results.jl index faa327d540..ac33296cef 100644 --- a/src/simulation/simulation_problem_results.jl +++ b/src/simulation/simulation_problem_results.jl @@ -60,6 +60,8 @@ function SimulationProblemResults{T}( ) end +const ProblemResultsTypes = Union{OptimizationProblemResults, SimulationProblemResults} + get_model_name(res::SimulationProblemResults) = res.problem get_system(res::SimulationProblemResults) = res.system get_resolution(res::SimulationProblemResults) = res.resolution @@ -153,7 +155,7 @@ will include all data. If that was not configured then the returned system will all data except time series data. """ function get_system!( - results::Union{OptimizationProblemResults, SimulationProblemResults}; + results::ProblemResultsTypes; kwargs..., ) !isnothing(get_system(results)) && return get_system(results) @@ -696,3 +698,47 @@ end try_resolve_store(user::SimulationStore, results::Union{Nothing, SimulationStore}) = user try_resolve_store(user::Nothing, results::SimulationStore) = results try_resolve_store(user::Nothing, results::Nothing) = nothing + +_validate_source_data_type(::Nothing) = + throw(ArgumentError("No system attached, need to call set_system!")) + +# In the case of OptimizationProblemResults, our "system" might be an IS type rather than a +# PSY.System. If `IS.get_components` were the same as `PSY.get_components` etc., this +# wouldn't be a problem, but in the status quo it is +# (see https://github.com/NREL-Sienna/InfrastructureSystems.jl/issues/388#issuecomment-2438344086) +_validate_source_data_type(::IS.InfrastructureSystemsType) = + throw( + IS.NotImplementedError( + "Currently can only call get_components on a set of results whose source data/system is a PowerSystems.jl System", + ), + ) + +_validate_source_data_type(data::PSY.System) = data # Pass through on success + +function get_components(::Type{T}, res::ProblemResultsTypes) where {T <: PSY.Component} + system = _validate_source_data_type(get_system(res)) + return get_available_components(T, system) +end + +function get_components( + filter_func::Function, + ::Type{T}, + res::ProblemResultsTypes, +) where {T <: PSY.Component} + system = _validate_source_data_type(get_system(res)) + return get_available_components(filter_func, T, system) +end + +function get_components(selector::PSY.ComponentSelector, res::ProblemResultsTypes) + system = _validate_source_data_type(get_system(res)) + return get_available_components(selector, system) +end + +function get_component( + ::Type{T}, + res::ProblemResultsTypes, + name::AbstractString, +) where {T <: PSY.Component} + system = _validate_source_data_type(get_system(res)) + return get_available_component(T, system, name) +end diff --git a/src/utils/printing.jl b/src/utils/printing.jl index ae3f97fb1c..c7bf7e931d 100644 --- a/src/utils/printing.jl +++ b/src/utils/printing.jl @@ -487,7 +487,6 @@ function _show_method(io::IO, results::SimulationResults, backend::Symbol; kwarg ) end -ProblemResultsTypes = Union{OptimizationProblemResults, SimulationProblemResults} function Base.show(io::IO, ::MIME"text/plain", input::ProblemResultsTypes) _show_method(io, input, :auto) end diff --git a/test/test_model_decision.jl b/test/test_model_decision.jl index 6b1d1abc97..b40ffa840e 100644 --- a/test/test_model_decision.jl +++ b/test/test_model_decision.jl @@ -236,6 +236,8 @@ end @test isa(get_realized_timestamps(res), StepRange{DateTime}) @test isa(IS.Optimization.get_source_data(res), PSY.System) @test length(get_timestamps(res)) == 24 + @test collect(get_components(ThermalStandard, res)) == + collect(get_available_components(ThermalStandard, get_system(res))) end @testset "Solve DecisionModelModel with auto-build" begin diff --git a/test/test_simulation_results.jl b/test/test_simulation_results.jl index d3ec744777..dc614d95e9 100644 --- a/test/test_simulation_results.jl +++ b/test/test_simulation_results.jl @@ -313,6 +313,11 @@ function test_decision_problem_results_values( @test IS.get_uuid(get_system(results_uc)) === IS.get_uuid(c_sys5_hy_uc) @test IS.get_uuid(get_system(results_ed)) === IS.get_uuid(c_sys5_hy_ed) + @test collect(get_components(ThermalStandard, results_uc)) == + collect(get_available_components(ThermalStandard, get_system(results_uc))) + @test collect(get_components(ThermalStandard, results_ed)) == + collect(get_available_components(ThermalStandard, get_system(results_ed))) + @test isempty(setdiff(UC_EXPECTED_VARS, list_variable_names(results_uc))) @test isempty(setdiff(ED_EXPECTED_VARS, list_variable_names(results_ed))) From e5313fdca44dc9e6337e475202169cb6aec3e2b5 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:53:39 -0600 Subject: [PATCH 2/5] Add `get_groups` `ComponentSelector` methods for results --- src/PowerSimulations.jl | 3 ++- src/simulation/simulation_problem_results.jl | 5 +++++ test/test_model_decision.jl | 5 +++++ test/test_simulation_results.jl | 12 ++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 6dad5ddeee..9c1177faee 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -398,7 +398,8 @@ import InfrastructureSystems.Optimization: read_results_with_keys, deserialize_k # PowerSystems imports import PowerSystems: - get_components, get_available_components, get_component + get_components, get_available_components, get_component, + get_groups, get_available_groups export get_name export get_model_base_power diff --git a/src/simulation/simulation_problem_results.jl b/src/simulation/simulation_problem_results.jl index ac33296cef..60b2bed113 100644 --- a/src/simulation/simulation_problem_results.jl +++ b/src/simulation/simulation_problem_results.jl @@ -742,3 +742,8 @@ function get_component( system = _validate_source_data_type(get_system(res)) return get_available_component(T, system, name) end + +function get_groups(selector::PSY.ComponentSelector, res::ProblemResultsTypes) + system = _validate_source_data_type(get_system(res)) + return get_available_groups(selector, system) +end diff --git a/test/test_model_decision.jl b/test/test_model_decision.jl index b40ffa840e..3407c3d0d6 100644 --- a/test/test_model_decision.jl +++ b/test/test_model_decision.jl @@ -236,8 +236,13 @@ end @test isa(get_realized_timestamps(res), StepRange{DateTime}) @test isa(IS.Optimization.get_source_data(res), PSY.System) @test length(get_timestamps(res)) == 24 + + PSY.set_available!(first(get_components(ThermalStandard, get_system(res))), false) @test collect(get_components(ThermalStandard, res)) == collect(get_available_components(ThermalStandard, get_system(res))) + sel = PSY.make_selector(ThermalStandard; groupby = :each) + @test collect(get_groups(sel, res)) == + collect(get_available_groups(sel, get_system(res))) end @testset "Solve DecisionModelModel with auto-build" begin diff --git a/test/test_simulation_results.jl b/test/test_simulation_results.jl index dc614d95e9..4bca6f8ac3 100644 --- a/test/test_simulation_results.jl +++ b/test/test_simulation_results.jl @@ -313,10 +313,22 @@ function test_decision_problem_results_values( @test IS.get_uuid(get_system(results_uc)) === IS.get_uuid(c_sys5_hy_uc) @test IS.get_uuid(get_system(results_ed)) === IS.get_uuid(c_sys5_hy_ed) + # Temporarily mark some stuff unavailable + unav_uc = first(PSY.get_available_components(ThermalStandard, get_system(results_uc))) + PSY.set_available!(unav_uc, false) + unav_ed = first(PSY.get_available_components(ThermalStandard, get_system(results_ed))) + PSY.set_available!(unav_ed, false) + sel = PSY.make_selector(ThermalStandard; groupby = :each) @test collect(get_components(ThermalStandard, results_uc)) == collect(get_available_components(ThermalStandard, get_system(results_uc))) @test collect(get_components(ThermalStandard, results_ed)) == collect(get_available_components(ThermalStandard, get_system(results_ed))) + @test collect(get_groups(sel, results_uc)) == + collect(get_available_groups(sel, get_system(results_uc))) + @test collect(get_groups(sel, results_ed)) == + collect(get_available_groups(sel, get_system(results_ed))) + PSY.set_available!(unav_uc, true) + PSY.set_available!(unav_ed, true) @test isempty(setdiff(UC_EXPECTED_VARS, list_variable_names(results_uc))) @test isempty(setdiff(ED_EXPECTED_VARS, list_variable_names(results_ed))) From 819a49a537011530c92c0039520cc49c3b49d609 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:14:07 -0700 Subject: [PATCH 3/5] Revert previous changes, solve `get_components` issues the new way --- src/PowerSimulations.jl | 4 +- src/simulation/get_components_interface.jl | 53 +++++++++++++++++++ src/simulation/simulation_problem_results.jl | 54 +------------------- src/utils/printing.jl | 1 + 4 files changed, 59 insertions(+), 53 deletions(-) create mode 100644 src/simulation/get_components_interface.jl diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 9c1177faee..c319cf5f93 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -393,12 +393,13 @@ import InfrastructureSystems.Optimization: import InfrastructureSystems.Optimization: read_results_with_keys, deserialize_key, encode_key_as_string, encode_keys_as_strings, should_write_resulting_value, convert_result_to_natural_units, to_matrix, get_store_container_type +import InfrastructureSystems.Optimization: get_source_data # IS.Optimization imports that stay private, may or may not be additional methods in PowerSimulations # PowerSystems imports import PowerSystems: - get_components, get_available_components, get_component, + get_components, get_component, get_available_components, get_available_component, get_groups, get_available_groups export get_name @@ -535,6 +536,7 @@ include("simulation/simulation_store_params.jl") include("simulation/hdf_simulation_store.jl") include("simulation/in_memory_simulation_store.jl") include("simulation/simulation_problem_results.jl") +include("simulation/get_components_interface.jl") include("simulation/decision_model_simulation_results.jl") include("simulation/emulation_model_simulation_results.jl") include("simulation/realized_meta.jl") diff --git a/src/simulation/get_components_interface.jl b/src/simulation/get_components_interface.jl new file mode 100644 index 0000000000..32635bc4b5 --- /dev/null +++ b/src/simulation/get_components_interface.jl @@ -0,0 +1,53 @@ +# Analogous to `src/get_components_interface.jl` in PowerSystems.jl, see comments there. + +# get_components +""" +Calling `get_components` on a `Results` is the same as calling +[`get_available_components`](@ref) on the system attached to the results. +""" +PSY.get_components( + ::Type{T}, + res::IS.Results; + subsystem_name = nothing, +) where {T <: IS.InfrastructureSystemsComponent} = + IS.get_components(T, res; subsystem_name = subsystem_name) + +PSY.get_components(res::IS.Results, attribute::IS.SupplementalAttribute) = + IS.get_components(res, attribute) + +PSY.get_components( + filter_func::Function, + ::Type{T}, + res::IS.Results; + subsystem_name = nothing, +) where {T <: IS.InfrastructureSystemsComponent} = + IS.get_components(filter_func, T, res; subsystem_name = subsystem_name) + +PSY.get_components(selector::IS.ComponentSelector, res::IS.Results; kwargs...) = + IS.get_components(selector, res; kwargs...) + +# get_component +""" +Calling `get_component` on a `Results` is the same as calling +[`get_available_component`](@ref) on the system attached to the results. +""" +PSY.get_component(res::IS.Results, uuid::Base.UUID) = IS.get_component(res, uuid) +PSY.get_component(res::IS.Results, uuid::String) = IS.get_component(res, uuid) + +PSY.get_component( + ::Type{T}, + res::IS.Results, + name::AbstractString, +) where {T <: IS.InfrastructureSystemsComponent} = + IS.get_component(T, res, name) + +PSY.get_component(selector::IS.SingularComponentSelector, res::IS.Results; kwargs...) = + IS.get_component(selector, res; kwargs...) + +# get_groups +""" +Calling `get_groups` on a `Results` is the same as calling [`get_available_groups`](@ref) on +the system attached to the results. +""" +PSY.get_groups(selector::IS.ComponentSelector, res::IS.Results; kwargs...) = + IS.get_groups(selector, res; kwargs...) diff --git a/src/simulation/simulation_problem_results.jl b/src/simulation/simulation_problem_results.jl index 60b2bed113..6bff5e0612 100644 --- a/src/simulation/simulation_problem_results.jl +++ b/src/simulation/simulation_problem_results.jl @@ -60,10 +60,9 @@ function SimulationProblemResults{T}( ) end -const ProblemResultsTypes = Union{OptimizationProblemResults, SimulationProblemResults} - get_model_name(res::SimulationProblemResults) = res.problem get_system(res::SimulationProblemResults) = res.system +get_source_data(res::SimulationProblemResults) = get_system(res) # Needed for compatibility with the IS.Results interface get_resolution(res::SimulationProblemResults) = res.resolution get_execution_path(res::SimulationProblemResults) = res.execution_path get_model_base_power(res::SimulationProblemResults) = res.base_power @@ -155,7 +154,7 @@ will include all data. If that was not configured then the returned system will all data except time series data. """ function get_system!( - results::ProblemResultsTypes; + results::Union{OptimizationProblemResults, SimulationProblemResults}; kwargs..., ) !isnothing(get_system(results)) && return get_system(results) @@ -698,52 +697,3 @@ end try_resolve_store(user::SimulationStore, results::Union{Nothing, SimulationStore}) = user try_resolve_store(user::Nothing, results::SimulationStore) = results try_resolve_store(user::Nothing, results::Nothing) = nothing - -_validate_source_data_type(::Nothing) = - throw(ArgumentError("No system attached, need to call set_system!")) - -# In the case of OptimizationProblemResults, our "system" might be an IS type rather than a -# PSY.System. If `IS.get_components` were the same as `PSY.get_components` etc., this -# wouldn't be a problem, but in the status quo it is -# (see https://github.com/NREL-Sienna/InfrastructureSystems.jl/issues/388#issuecomment-2438344086) -_validate_source_data_type(::IS.InfrastructureSystemsType) = - throw( - IS.NotImplementedError( - "Currently can only call get_components on a set of results whose source data/system is a PowerSystems.jl System", - ), - ) - -_validate_source_data_type(data::PSY.System) = data # Pass through on success - -function get_components(::Type{T}, res::ProblemResultsTypes) where {T <: PSY.Component} - system = _validate_source_data_type(get_system(res)) - return get_available_components(T, system) -end - -function get_components( - filter_func::Function, - ::Type{T}, - res::ProblemResultsTypes, -) where {T <: PSY.Component} - system = _validate_source_data_type(get_system(res)) - return get_available_components(filter_func, T, system) -end - -function get_components(selector::PSY.ComponentSelector, res::ProblemResultsTypes) - system = _validate_source_data_type(get_system(res)) - return get_available_components(selector, system) -end - -function get_component( - ::Type{T}, - res::ProblemResultsTypes, - name::AbstractString, -) where {T <: PSY.Component} - system = _validate_source_data_type(get_system(res)) - return get_available_component(T, system, name) -end - -function get_groups(selector::PSY.ComponentSelector, res::ProblemResultsTypes) - system = _validate_source_data_type(get_system(res)) - return get_available_groups(selector, system) -end diff --git a/src/utils/printing.jl b/src/utils/printing.jl index c7bf7e931d..ae3f97fb1c 100644 --- a/src/utils/printing.jl +++ b/src/utils/printing.jl @@ -487,6 +487,7 @@ function _show_method(io::IO, results::SimulationResults, backend::Symbol; kwarg ) end +ProblemResultsTypes = Union{OptimizationProblemResults, SimulationProblemResults} function Base.show(io::IO, ::MIME"text/plain", input::ProblemResultsTypes) _show_method(io, input, :auto) end From bbaf11eff68f1fe0fb6a4667222128366b0ed99f Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:11:28 -0700 Subject: [PATCH 4/5] `ComponentSelector`: make `scope_limiter` an optional first argument --- src/simulation/get_components_interface.jl | 33 ++++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/simulation/get_components_interface.jl b/src/simulation/get_components_interface.jl index 32635bc4b5..0daf7257e2 100644 --- a/src/simulation/get_components_interface.jl +++ b/src/simulation/get_components_interface.jl @@ -23,8 +23,15 @@ PSY.get_components( ) where {T <: IS.InfrastructureSystemsComponent} = IS.get_components(filter_func, T, res; subsystem_name = subsystem_name) -PSY.get_components(selector::IS.ComponentSelector, res::IS.Results; kwargs...) = - IS.get_components(selector, res; kwargs...) +PSY.get_components( + scope_limiter::Union{Function, Nothing}, + selector::IS.ComponentSelector, + res::IS.Results, +) = + IS.get_components(scope_limiter, selector, res) + +PSY.get_components(selector::IS.ComponentSelector, res::IS.Results) = + IS.get_components(selector, res) # get_component """ @@ -41,13 +48,27 @@ PSY.get_component( ) where {T <: IS.InfrastructureSystemsComponent} = IS.get_component(T, res, name) -PSY.get_component(selector::IS.SingularComponentSelector, res::IS.Results; kwargs...) = - IS.get_component(selector, res; kwargs...) +PSY.get_component( + scope_limiter::Union{Function, Nothing}, + selector::IS.SingularComponentSelector, + res::IS.Results, +) = + IS.get_component(scope_limiter, selector, res) + +PSY.get_component(selector::IS.SingularComponentSelector, res::IS.Results) = + IS.get_component(selector, res) # get_groups """ Calling `get_groups` on a `Results` is the same as calling [`get_available_groups`](@ref) on the system attached to the results. """ -PSY.get_groups(selector::IS.ComponentSelector, res::IS.Results; kwargs...) = - IS.get_groups(selector, res; kwargs...) +PSY.get_groups( + scope_limiter::Union{Function, Nothing}, + selector::IS.ComponentSelector, + res::IS.Results, +) = + IS.get_groups(scope_limiter, selector, res) + +PSY.get_groups(selector::IS.ComponentSelector, res::IS.Results) = + IS.get_groups(selector, res) From 3d5b029af95e0145b851a7ec0268c77e6b5c7ab8 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:08:17 -0700 Subject: [PATCH 5/5] Remove problematic Documenter links --- src/simulation/get_components_interface.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/simulation/get_components_interface.jl b/src/simulation/get_components_interface.jl index 0daf7257e2..4f91707f99 100644 --- a/src/simulation/get_components_interface.jl +++ b/src/simulation/get_components_interface.jl @@ -3,7 +3,7 @@ # get_components """ Calling `get_components` on a `Results` is the same as calling -[`get_available_components`](@ref) on the system attached to the results. +[`get_available_components`] on the system attached to the results. """ PSY.get_components( ::Type{T}, @@ -36,7 +36,7 @@ PSY.get_components(selector::IS.ComponentSelector, res::IS.Results) = # get_component """ Calling `get_component` on a `Results` is the same as calling -[`get_available_component`](@ref) on the system attached to the results. +[`get_available_component`] on the system attached to the results. """ PSY.get_component(res::IS.Results, uuid::Base.UUID) = IS.get_component(res, uuid) PSY.get_component(res::IS.Results, uuid::String) = IS.get_component(res, uuid) @@ -60,7 +60,7 @@ PSY.get_component(selector::IS.SingularComponentSelector, res::IS.Results) = # get_groups """ -Calling `get_groups` on a `Results` is the same as calling [`get_available_groups`](@ref) on +Calling `get_groups` on a `Results` is the same as calling [`get_available_groups`] on the system attached to the results. """ PSY.get_groups(