From 600bfc2adcd0f0cd3994e7f9ffe60ec04b6827f5 Mon Sep 17 00:00:00 2001 From: Daniel Thom Date: Thu, 28 Dec 2023 14:43:58 -0700 Subject: [PATCH] Fix issues with printing of the system This also changes the return type of the function get_time_series_counts, which will break parent packages, such as PowerSystems.jl. --- src/components.jl | 2 +- src/containers.jl | 2 ++ src/supplemental_attributes.jl | 2 +- src/system_data.jl | 58 +++++++++++++++++++++++++++++---- src/time_series_container.jl | 2 ++ src/utils/print.jl | 59 ++++++++++++++++++++-------------- test/test_time_series.jl | 12 +++++++ 7 files changed, 103 insertions(+), 34 deletions(-) diff --git a/src/components.jl b/src/components.jl index 508c49225..2efb6d505 100644 --- a/src/components.jl +++ b/src/components.jl @@ -7,7 +7,7 @@ struct Components <: InfrastructureSystemsContainer validation_descriptors::Vector end -get_display_string(::Components) = "components" +get_member_string(::Components) = "components" function Components( time_series_storage::TimeSeriesStorage, diff --git a/src/containers.jl b/src/containers.jl index afe3ad1f9..c3705ae7b 100644 --- a/src/containers.jl +++ b/src/containers.jl @@ -1,5 +1,7 @@ abstract type InfrastructureSystemsContainer end +get_display_string(x::InfrastructureSystemsContainer) = string(nameof(typeof(x))) + function serialize(container::InfrastructureSystemsContainer) # time_series_storage and validation_descriptors are serialized elsewhere. return [serialize(x) for y in values(container.data) for x in values(y)] diff --git a/src/supplemental_attributes.jl b/src/supplemental_attributes.jl index 739e934f8..fe6457d23 100644 --- a/src/supplemental_attributes.jl +++ b/src/supplemental_attributes.jl @@ -8,7 +8,7 @@ struct SupplementalAttributes <: InfrastructureSystemsContainer time_series_storage::TimeSeriesStorage end -get_display_string(::SupplementalAttributes) = "SupplementalAttributes" +get_member_string(::SupplementalAttributes) = "supplemental attributes" function SupplementalAttributes(time_series_storage::TimeSeriesStorage) return SupplementalAttributes(SupplementalAttributesByType(), time_series_storage) diff --git a/src/system_data.jl b/src/system_data.jl index a96822971..e69bfca3b 100644 --- a/src/system_data.jl +++ b/src/system_data.jl @@ -386,6 +386,10 @@ function iterate_components_with_time_series(data::SystemData) )) end +function iterate_supplemental_attributes_with_time_series(data::SystemData) + iterate_supplemental_attributes_with_time_series(data.attributes) +end + """ Removes all time series of a particular type from a System. @@ -403,13 +407,15 @@ function remove_time_series!(data::SystemData, ::Type{T}) where {T <: TimeSeries end end counts = get_time_series_counts(data) - if counts[3] == 0 # no more forecast objects - if counts[2] == 0 # no more static time series objects + if counts.forecast_count == 0 + if counts.static_time_series_count == 0 reset_info!(data.time_series_params) else reset_info!(data.time_series_params.forecast_params) end end + + return end """ @@ -484,21 +490,59 @@ function check_time_series_consistency(data::SystemData, ::Type{SingleTimeSeries return first_initial_timestamp, first_len end +struct TimeSeriesCounts + components_with_time_series::Int + supplemental_attributes_with_time_series::Int + static_time_series_count::Int + forecast_count::Int +end + """ -Return a tuple of counts of components with time series and total time series and forecasts. +Build an instance of TimeSeriesCounts by scanning the system. """ function get_time_series_counts(data::SystemData) component_count = 0 + attribute_count = 0 static_time_series_count = 0 forecast_count = 0 + # Note that the same time series UUID can exist in in multiple types, such as with + # SingleTimeSeries and DeterministicSingleTimeSeries. + uuids_by_type = Dict{DataType, Set{Base.UUID}}() + + function update_time_series_counts(object) + for metadata in iterate_time_series_metadata(get_time_series_container(object)) + ts_type = time_series_metadata_to_data(metadata) + if !haskey(uuids_by_type, ts_type) + uuids_by_type[ts_type] = Set{Base.UUID}() + end + uuid = get_time_series_uuid(metadata) + if !in(uuid, uuids_by_type[ts_type]) + if ts_type <: StaticTimeSeries + static_time_series_count += 1 + elseif ts_type <: Forecast + forecast_count += 1 + end + push!(uuids_by_type[ts_type], uuid) + end + end + end + for component in iterate_components_with_time_series(data) component_count += 1 - _ts_count, _forecast_count = get_num_time_series(component) - static_time_series_count += _ts_count - forecast_count += _forecast_count + update_time_series_counts(component) end - return (component_count, static_time_series_count, forecast_count) + for attr in iterate_supplemental_attributes_with_time_series(data) + attribute_count += 1 + update_time_series_counts(attr) + end + + return TimeSeriesCounts( + component_count, + attribute_count, + static_time_series_count, + forecast_count, + ) end """ diff --git a/src/time_series_container.jl b/src/time_series_container.jl index 5db13fa27..f0e297bab 100644 --- a/src/time_series_container.jl +++ b/src/time_series_container.jl @@ -132,3 +132,5 @@ function deserialize(::Type{TimeSeriesContainer}, data::Vector) return container end + +iterate_time_series_metadata(container::TimeSeriesContainer) = values(container.data) diff --git a/src/utils/print.jl b/src/utils/print.jl index 8a78e9e00..3873809aa 100644 --- a/src/utils/print.jl +++ b/src/utils/print.jl @@ -24,22 +24,25 @@ function Base.show(io::IO, container::InfrastructureSystemsContainer) end function Base.show(io::IO, ::MIME"text/plain", container::InfrastructureSystemsContainer) - num_components = get_num_members(container) - println(io, "$(get_display_string(container))") - println(io, "==========") - println(io, "Num components: $num_components") - if num_components > 0 + num_members = get_num_members(container) + title = get_display_string(container) + member_str = get_member_string(container) + println(io, title) + println(io, "="^length(title)) + println(io, "Num $member_str: $num_members") + if num_members > 0 println(io) - show_components_table(io, container; backend = Val(:auto)) + show_container_table(io, container; backend = Val(:auto)) end end -function Base.show(io::IO, ::MIME"text/html", components::Components) - num_components = get_num_components(components) - println(io, "

Components

") - println(io, "

Num components: $num_components

") - if num_components > 0 - show_components_table(io, components; backend = Val(:html), standalone = false) +function Base.show(io::IO, ::MIME"text/html", container::InfrastructureSystemsContainer) + num_members = get_num_members(container) + member_str = get_member_string(container) + println(io, "

Members

") + println(io, "

Num $member_str: $num_members

") + if num_members > 0 + show_container_table(io, container; backend = Val(:html), standalone = false) end end @@ -71,6 +74,8 @@ end function Base.show(io::IO, ::MIME"text/plain", data::SystemData) show(io, MIME"text/plain"(), data.components) println(io, "\n") + show(io, MIME"text/plain"(), data.attributes) + println(io, "\n") show_time_series_data(io, data; backend = Val(:auto)) show(io, data.time_series_params) end @@ -78,27 +83,31 @@ end function Base.show(io::IO, ::MIME"text/html", data::SystemData) show(io, MIME"text/html"(), data.components) println(io, "\n") + show(io, MIME"text/html"(), data.attributes) + println(io, "\n") show_time_series_data(io, data; backend = Val(:html), standalone = false) show(io, data.time_series_params) end function show_time_series_data(io::IO, data::SystemData; kwargs...) - component_count, ts_count, forecast_count = get_time_series_counts(data) + counts = get_time_series_counts(data) + if counts.static_time_series_count == 0 && counts.forecast_count == 0 + return + end + res = get_time_series_resolution(data) res = res <= Dates.Minute(1) ? Dates.Second(res) : Dates.Minute(res) header = ["Property", "Value"] table = [ - "Components with time series data" string(component_count) - "Total StaticTimeSeries" string(ts_count) - "Total Forecasts" string(forecast_count) + "Components with time series data" string(counts.components_with_time_series) + "Supplemental attributes with time series data" string(counts.supplemental_attributes_with_time_series) + "Total StaticTimeSeries" string(counts.static_time_series_count) + "Total Forecasts" string(counts.forecast_count) "Resolution" string(res) ] - if component_count == 0 - return - end - if forecast_count > 0 + if counts.forecast_count > 0 initial_times = get_forecast_initial_times(data) table2 = [ "First initial time" string(first(initial_times)) @@ -137,7 +146,7 @@ end function Base.show(io::IO, ::MIME"text/plain", ist::InfrastructureSystemsComponent) print(io, summary(ist), ":") - for (name, field_type) in zip(fieldnames(typeof(ist)), fieldtypes(typeof(ist))) + for name in fieldnames(typeof(ist)) obj = getfield(ist, name) if obj isa InfrastructureSystemsInternal continue @@ -197,14 +206,14 @@ function _get_type_counts(it::FlattenIteratorWrapper) return data end -function show_components_table(io::IO, components::Components; kwargs...) +function show_container_table(io::IO, container::InfrastructureSystemsContainer; kwargs...) header = ["Type", "Count", "Has Static Time Series", "Has Forecasts"] - data = Array{Any, 2}(undef, length(components.data), length(header)) + data = Array{Any, 2}(undef, length(container.data), length(header)) - type_names = [(strip_module_name(string(x)), x) for x in keys(components.data)] + type_names = [(strip_module_name(string(x)), x) for x in keys(container.data)] sort!(type_names; by = x -> x[1]) for (i, (type_name, type)) in enumerate(type_names) - vals = components.data[type] + vals = container.data[type] has_sts = false has_forecasts = false for val in values(vals) diff --git a/test/test_time_series.jl b/test/test_time_series.jl index a7654576f..2290901a6 100644 --- a/test/test_time_series.jl +++ b/test/test_time_series.jl @@ -349,6 +349,12 @@ end IS.Deterministic(; data = fdata, name = "bystander", resolution = resolution) IS.add_time_series!(sys, component, bystander) + counts = IS.get_time_series_counts(sys) + @test counts.components_with_time_series == 1 + @test counts.supplemental_attributes_with_time_series == 0 + @test counts.static_time_series_count == 1 + @test counts.forecast_count == 1 + # This interval is greater than the max possible. @test_throws IS.ConflictingInputsError IS.transform_single_time_series!( sys, @@ -365,6 +371,12 @@ end ) verify_show(sys) + counts = IS.get_time_series_counts(sys) + @test counts.components_with_time_series == 1 + @test counts.supplemental_attributes_with_time_series == 0 + @test counts.static_time_series_count == 1 + @test counts.forecast_count == 2 + # The original should still be readable. single_vals = IS.get_time_series_values(IS.SingleTimeSeries, component, name) @test single_vals == data