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 ComponentSelector feature: IS portion #342

Open
wants to merge 52 commits into
base: main
Choose a base branch
from

Conversation

GabrielKS
Copy link
Contributor

@GabrielKS GabrielKS commented Mar 18, 2024

This draft PR will get filled in as @tengis-nrl and I move the ComponentSelector features from PowerAnalytics to InfrastructureSystems. Ready for review! Adds an immutable, lazy, system-independent representation of a grouped set of components. For more details and a demonstration, see https://github.nrel.gov/gkonars/PowerAnalytics-demos/blob/main/component_selector_pr_demo.ipynb.

src/component_selector.jl Outdated Show resolved Hide resolved
src/component_selector.jl Outdated Show resolved Hide resolved
src/component_selector.jl Show resolved Hide resolved
Copy link

codecov bot commented Mar 24, 2024

Codecov Report

Attention: Patch coverage is 90.16393% with 12 lines in your changes missing coverage. Please review.

Project coverage is 78.30%. Comparing base (be8eb50) to head (dd3bba2).
Report is 13 commits behind head on main.

Files with missing lines Patch % Lines
src/results.jl 0.00% 9 Missing ⚠️
src/time_series_storage.jl 0.00% 2 Missing ⚠️
src/component_container.jl 92.85% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #342      +/-   ##
==========================================
+ Coverage   78.28%   78.30%   +0.02%     
==========================================
  Files          71       71              
  Lines        5484     5596     +112     
==========================================
+ Hits         4293     4382      +89     
- Misses       1191     1214      +23     
Flag Coverage Δ
unittests 78.30% <90.16%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/InfrastructureSystems.jl 55.55% <ø> (-34.45%) ⬇️
src/Optimization/optimization_container_keys.jl 95.58% <100.00%> (ø)
src/common.jl 66.66% <ø> (+6.66%) ⬆️
src/component_selector.jl 100.00% <100.00%> (ø)
src/components.jl 86.39% <ø> (ø)
src/system_data.jl 91.30% <100.00%> (+0.02%) ⬆️
src/component_container.jl 92.85% <92.85%> (ø)
src/time_series_storage.jl 61.53% <0.00%> (ø)
src/results.jl 0.00% <0.00%> (ø)

... and 13 files with indirect coverage changes

@tengis-nrl tengis-nrl marked this pull request as ready for review March 25, 2024 16:22
@tengis-nrl
Copy link
Contributor

image

jl_5D19.tmp Outdated Show resolved Hide resolved
@GabrielKS
Copy link
Contributor Author

We need working implementations of get_components and get_subselectors here, not just in PowerSystems, because the ComponentSelector feature is meant to be usable without PowerSystems. You'll note a TODO here about making get_available optional; if you don't think we have time to do that now, I think the IS version of get_components should just not check availability and the PSY version should.

@jd-lara jd-lara assigned jd-lara and unassigned tengis-nrl Apr 22, 2024
@GabrielKS GabrielKS changed the base branch from gks/td/component_selector_feature to main June 4, 2024 17:53
@GabrielKS GabrielKS force-pushed the gks/td/component_selector_port branch from 1732638 to 83ea408 Compare June 10, 2024 18:44
@GabrielKS
Copy link
Contributor Author

Rebased onto main and tests pass.

@GabrielKS GabrielKS requested a review from daniel-thom October 4, 2024 17:43
src/component_selector.jl Outdated Show resolved Hide resolved
src/component_selector.jl Outdated Show resolved Hide resolved
"""
make_selector(
component_type::Type{<:InfrastructureSystemsComponent},
filter_func::Function;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a signature (line 386) where it is first. Is this compromise where I have both signatures acceptable? It just seems really inconsistent to me to have the user specify the component type first in every case except the filter function case. This way the do block is supported but I also get my consistency.

Copy link
Contributor

Choose a reason for hiding this comment

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

My concern is consistency in our API. Currently, there is only one way to call get_components with a filter function. That function has to be first. This PR is introducing a second pattern where you can call get_components and specify the filter function or scope limiter as a keyword argument. If people see this pattern being used they may try to use it with the existing get_components and then complain that it doesn't work. I don't want to have to then go back and change that method to support this new pattern. My suggestion is that we continue to provide a single way to specify filter functions - and that it follow the Julia convention.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the record, @daniel-thom, @jd-lara, @sourabhdalvi, @akrivi and I discussed this synchronously and came to the following resolution:

  1. Eliminate the scope_limiter feature. Instead:
    • When the user calls get_components(selector, system), return all components that satisfy the selector (no availability filtering)
    • When the user calls get_components(selector, results), return only the available components
    • When the user calls get_available_components(selector, system), return only the available components
  2. Follow the Julia convention of always putting the function first; remove the methods where the function is not first (I was outvoted)

Copy link
Contributor Author

@GabrielKS GabrielKS Dec 12, 2024

Choose a reason for hiding this comment

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

From the user's point of view, this is done; all function parameters should only appear in first position. There is a hidden scope_limiter kwarg to aid in implementation and extension.

abstract type ComponentContainer <: InfrastructureSystemsContainer end

# For each of these functions, `ComponentContainer` concrete subtypes MUST either implement
# appropriate methods or accept the default if it exists.
Copy link
Contributor Author

@GabrielKS GabrielKS Dec 12, 2024

Choose a reason for hiding this comment

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

@daniel-thom these methods are necessary and sufficient for the ComponentSelector design and I'm trying to avoid further scope creep. But maybe we also want iterate_components, etc. here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Mabye. I'm definitely OK leaving it out.

@@ -17,7 +17,7 @@ const SERIALIZATION_METADATA_KEY = "__serialization_metadata__"

Container for system components and time series data
"""
mutable struct SystemData <: InfrastructureSystemsType
mutable struct SystemData <: ComponentContainer
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ComponentContainer <: InfrastructureSystemsType so this is backwards compatible

@GabrielKS
Copy link
Contributor Author

GabrielKS commented Dec 12, 2024

@daniel-thom Ready for another review. The main changes since last review are:

  1. Cleanup of the scope_limiter situation. More details above, but the gist is that this is no longer part of the user interface, its functionality is served by get_components vs. get_available_components.
  2. Addition of rebuild_selector and the supporting type RegroupedComponentSelector to fulfill a request from @akrivi
  3. Addition of ComponentContainer with a standard interface for get_components-type stuff

function get_components(selector::FilterComponentSelector, sys; kwargs...)
# Short-circuit-evaluate the `scope_limiter` first so `filter_func` may refer to
# component attributes that do not exist in components outside the scope
scope_limiter = get(kwargs, :scope_limiter, nothing)
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like you want scope_limiter to be part of the public interface, or at least you may be setting it in PowerAnalytics. Is that correct?

Copy link
Contributor Author

@GabrielKS GabrielKS Dec 16, 2024

Choose a reason for hiding this comment

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

I don't want it to be part of the public interface, but it's used in the implementation of get_available_components, so I tried to strike a middle ground here with the undocumented kwarg (similar to your use_system_fallback kwarg)

Get the available components of the collection that make up the `ComponentSelector`.
"""
get_available_components(selector::ComponentSelector, sys) =
get_components(selector, sys; scope_limiter = x -> get_available(sys, x))
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit concerned that people will use this as example code. And then eventually ask why scope_limiter isn't a kwarg in the System get_components method. Wouldn't it be better to follow the Julia convention and pass the "scope_limiter" as the first argument?

Copy link
Contributor Author

@GabrielKS GabrielKS Dec 16, 2024

Choose a reason for hiding this comment

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

The goals with this design are to (a) keep scope_limiter completely out of the public interface, as we resolved earlier, while (b) keeping it in a sort of "developer interface" so that it can be used for get_available_components. Absent another layer of indirection (have each ComponentSelector subtype implement _get_components with a scope_limiter and have get_components and get_available_components call that), putting scope_limiter as the first argument would more or less force it be part of the public interface: it would explicitly appear in method lists and such. Do you think it's worth it to add that layer of indirection, or is the current solution (perhaps with a comment added here that users are not to take this source code as example code?) good enough?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants