From 8830ee63a45b2bbe82a454eb36d0584876d894df Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Tue, 3 May 2022 10:32:16 +0100 Subject: [PATCH 1/3] restructre system types with shared time series data --- src/FullNetworkSystems.jl | 1 + src/system.jl | 136 ++++++++++++++++++++++++-------------- test/system.jl | 43 +++++------- 3 files changed, 104 insertions(+), 76 deletions(-) diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index 01d8241..6124e04 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -8,6 +8,7 @@ using InlineStrings export System, SystemDA, SystemRT export Zone, Generator, Bus, Branch +export GeneratorTimeSeries, GeneratorStatus, GeneratorStatusDA, GeneratorStatusRT export gens_per_zone, branches_by_breakpoints, get_datetimes include("system.jl") diff --git a/src/system.jl b/src/system.jl index 11248c3..c332a7f 100644 --- a/src/system.jl +++ b/src/system.jl @@ -102,6 +102,78 @@ struct Branch penalties::Tuple{Float64, Float64} end +###### Time Series types ###### + +""" + $TYPEDEF + +Generator related time series data that is needed for both the day-ahead and real-time formulations. + +Fields: +$TYPEDFIELDS +""" +struct GeneratorTimeSeries + "Generation of the generator at the start of the time period (MW)" + initial_generation::KeyedArray{Float64, 1} + "Generator offer curves. `KeyedArray` where the axis keys are `generator names x datetimes`" + offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} + "Generator minimum output in the ancillary services market (MW)" + regulation_min::KeyedArray{Float64, 2} + "Generator maximum output in the ancillary services market (MW)" + regulation_max::KeyedArray{Float64, 2} + "Generator minimum output (MW)" + pmin::KeyedArray{Float64, 2} + "Generator maximum output (MW)" + pmax::KeyedArray{Float64, 2} + "Ancillary services regulation offer prices (\$ /MW)" + asm_regulation::KeyedArray{Float64, 2} + "Ancillary services spinning offer prices (\$ /MW)" + asm_spin::KeyedArray{Float64, 2} + "Ancillary services supplemental on offer prices (\$ /MW)" + asm_sup_on::KeyedArray{Float64, 2} + "Ancillary services supplemental off offer prices (\$ /MW)" + asm_sup_off::KeyedArray{Float64, 2} +end + +""" + $TYPEDEF + +Abstract type for storing time series of generator status information. +""" +abstract type GeneratorStatus end + +""" + $TYPEDEF + +Generator status time series data needed for the day-ahead formulation. + +Fields: +$TYPEDFIELDS +""" +struct GeneratorStatusDA + "Hours each generator has been at its current status at the start of the day" + hours_at_status::KeyedArray{Float64, 1} + "Generator availability" + availability::KeyedArray{Bool, 2} + "Generator must run flag indicating that the generator has to be committed at that hour" + must_run::KeyedArray{Bool, 2} +end + +""" + $TYPEDEF + +Generator status time series data needed for the real-time formulation. + +Fields: +$TYPEDFIELDS +""" +struct GeneratorStatusRT + "Generator status indicated by a `Bool`" + status::KeyedArray{Bool, 2} + "Generator regulation status indicated by a `Bool`" + status_regulation::KeyedArray{Bool, 2} +end + """ System @@ -159,32 +231,10 @@ struct SystemDA <: System PTDF::KeyedArray{Float64, 2} # Generator related time series - "Generation of the generators at the start of the day (MW)" - initial_generation::KeyedArray{Float64, 1} - "Hours each generator has been at its current status at the start of the day" - hours_at_status::KeyedArray{Float64, 1} - "Generator offer curves. `KeyedArray` where the axis keys are `generator names x datetimes`" - offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} - "Generator availability" - availability::KeyedArray{Bool, 2} - "Generator must run flag indicating that the generator has to be committed at that hour" - must_run::KeyedArray{Bool, 2} - "Generator minimum output in the ancillary services market (MW)" - regulation_min::KeyedArray{Float64, 2} - "Generator maximum output in the ancillary services market (MW)" - regulation_max::KeyedArray{Float64, 2} - "Generator minimum output (MW)" - pmin::KeyedArray{Float64, 2} - "Generator maximum output (MW)" - pmax::KeyedArray{Float64, 2} - "Ancillary services regulation offer prices (\$ /MW)" - asm_regulation::KeyedArray{Float64, 2} - "Ancillary services spinning offer prices (\$ /MW)" - asm_spin::KeyedArray{Float64, 2} - "Ancillary services supplemental on offer prices (\$ /MW)" - asm_sup_on::KeyedArray{Float64, 2} - "Ancillary services supplemental off offer prices (\$ /MW)" - asm_sup_off::KeyedArray{Float64, 2} + "Generator related time series data" + generator_time_series::GeneratorTimeSeries + "Generator status time series needed for the day-ahead formulation" + generator_status::GeneratorStatusDA # Load time series "Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" @@ -234,30 +284,10 @@ struct SystemRT <: System PTDF::KeyedArray{Float64, 2} # Generator related time series - "Generation of the generator at the start of the time period (MW)" - initial_generation::KeyedArray{Float64, 1} - "Generator offer curves. `KeyedArray` where the axis keys are `generator names x datetimes`" - offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} - "Generator status indicated by a `Bool`" - status::KeyedArray{Bool, 2} - "Generator regulation status indicated by a `Bool`" - status_regulation::KeyedArray{Bool, 2} - "Generator minimum output in the ancillary services market (MW)" - regulation_min::KeyedArray{Float64, 2} - "Generator maximum output in the ancillary services market (MW)" - regulation_max::KeyedArray{Float64, 2} - "Generator minimum output (MW)" - pmin::KeyedArray{Float64, 2} - "Generator maximum output (MW)" - pmax::KeyedArray{Float64, 2} - "Ancillary services regulation offer prices (\$ /MW)" - asm_regulation::KeyedArray{Float64, 2} - "Ancillary services spinning offer prices (\$ /MW)" - asm_spin::KeyedArray{Float64, 2} - "Ancillary services supplemental on offer prices (\$ /MW)" - asm_sup_on::KeyedArray{Float64, 2} - "Ancillary services supplemental off offer prices (\$ /MW)" - asm_sup_off::KeyedArray{Float64, 2} + "Generator related time series data" + generator_time_series::GeneratorTimeSeries + "Generator status time series needed for the real-time formulation" + generator_status::GeneratorStatusRT # Load time series "Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" @@ -278,6 +308,10 @@ function Base.show(io::IO, ::MIME"text/plain", system::T) where {T <: System} for (name, type) in zip(fieldnames(T), fieldtypes(T)) if name == last(fieldnames(T)) print(io, "$name") + elseif type <: Union{GeneratorTimeSeries, GeneratorStatus} + for name in fieldnames(type) + print(io, "$name, ") + end elseif type <: KeyedArray && name != :PTDF print(io, "$name, ") end @@ -292,7 +326,7 @@ Extract datetimes from a `System`. """ function get_datetimes(system::System) # use offer_curve axiskeys because all subtypes of System have offer_curve - return axiskeys(system.offer_curve, 2) + return axiskeys(system.generator_time_series.offer_curve, 2) end """ diff --git a/test/system.jl b/test/system.jl index 8c1d0a7..8417365 100644 --- a/test/system.jl +++ b/test/system.jl @@ -70,6 +70,19 @@ ) PTDF = KeyedArray(rand(3, 3); row=branch_names, col=bus_names) + generator_time_series = GeneratorTimeSeries( + fake_vec_ts, + fake_offer_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts + ) + da_gen_status = GeneratorStatusDA(fake_vec_ts, fake_bool_ts, fake_bool_ts) da_system = SystemDA( gens_per_bus, incs_per_bus, @@ -82,19 +95,8 @@ branches, LODF, PTDF, - fake_vec_ts, - fake_vec_ts, - fake_offer_ts, - fake_bool_ts, - fake_bool_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, + generator_time_series, + da_gen_status, fake_gen_ts, fake_offer_ts, fake_offer_ts, @@ -112,6 +114,7 @@ @test one_bp == String[] #unmonitored @test two_bp == ["1"] + rt_gen_status = GeneratorStatusRT(fake_bool_ts, fake_bool_ts) rt_system = SystemRT( gens_per_bus, loads_per_bus, @@ -121,18 +124,8 @@ branches, LODF, PTDF, - fake_vec_ts, - fake_offer_ts, - fake_bool_ts, - fake_bool_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, + generator_time_series, + rt_gen_status, fake_gen_ts ) From 2e241f67e3905c48fb825ae8454bf7625083fef9 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Tue, 3 May 2022 10:44:54 +0100 Subject: [PATCH 2/3] fix show --- src/system.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/system.jl b/src/system.jl index c332a7f..3006e74 100644 --- a/src/system.jl +++ b/src/system.jl @@ -150,7 +150,7 @@ Generator status time series data needed for the day-ahead formulation. Fields: $TYPEDFIELDS """ -struct GeneratorStatusDA +struct GeneratorStatusDA <: GeneratorStatus "Hours each generator has been at its current status at the start of the day" hours_at_status::KeyedArray{Float64, 1} "Generator availability" @@ -167,7 +167,7 @@ Generator status time series data needed for the real-time formulation. Fields: $TYPEDFIELDS """ -struct GeneratorStatusRT +struct GeneratorStatusRT <: GeneratorStatus "Generator status indicated by a `Bool`" status::KeyedArray{Bool, 2} "Generator regulation status indicated by a `Bool`" @@ -308,7 +308,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::T) where {T <: System} for (name, type) in zip(fieldnames(T), fieldtypes(T)) if name == last(fieldnames(T)) print(io, "$name") - elseif type <: Union{GeneratorTimeSeries, GeneratorStatus} + elseif type <: Union{GeneratorTimeSeries, <:GeneratorStatus} for name in fieldnames(type) print(io, "$name, ") end From f732a5aee485484c1b5e59b164d0f54c696d38c0 Mon Sep 17 00:00:00 2001 From: BSnelling Date: Wed, 4 May 2022 14:44:38 +0100 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Raphael Saavedra --- src/system.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/system.jl b/src/system.jl index 3006e74..81fc2c8 100644 --- a/src/system.jl +++ b/src/system.jl @@ -153,9 +153,9 @@ $TYPEDFIELDS struct GeneratorStatusDA <: GeneratorStatus "Hours each generator has been at its current status at the start of the day" hours_at_status::KeyedArray{Float64, 1} - "Generator availability" + "Flag indicating if generator is available to be committed in each hour" availability::KeyedArray{Bool, 2} - "Generator must run flag indicating that the generator has to be committed at that hour" + "Flag indicating if the generator must be committed in each hour" must_run::KeyedArray{Bool, 2} end