From bc4fa5de63b2fc62f4d4a5d2d8b0b3325bd47688 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Tue, 19 Apr 2022 17:31:34 +0100 Subject: [PATCH 01/14] Define core types --- Project.toml | 6 ++ src/FullNetworkSystems.jl | 10 +- src/accessors.jl | 77 +++++++++++++++ src/system.jl | 192 ++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 104 ++++++++++++++++++++- 5 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 src/accessors.jl create mode 100644 src/system.jl diff --git a/Project.toml b/Project.toml index 0e9f349..b97fc7e 100644 --- a/Project.toml +++ b/Project.toml @@ -3,7 +3,13 @@ uuid = "877b7152-b508-43dc-81fb-72341a693988" authors = ["Invenia Technical Computing Corporation"] version = "1.0.0" +[deps] +AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" + + [compat] +AxisKeys = "0.2" julia = "1" [extras] diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index 5adb3b8..849f2c1 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -1,5 +1,13 @@ module FullNetworkSystems -# Write your package code here. +using AxisKeys +using Dates + +export System, SystemRT +export ServicesTimeSeries, Zone, StaticComponent, Generators, Buses, Branches +export gens_per_zone, branches_by_breakpoints, get_datetimes + +include("system.jl") +include("accessors.jl") end diff --git a/src/accessors.jl b/src/accessors.jl new file mode 100644 index 0000000..3af3935 --- /dev/null +++ b/src/accessors.jl @@ -0,0 +1,77 @@ +""" + get_datetimes(system) + +Extract datetimes from a `System`. +""" +function get_datetimes(system::System) + return axiskeys(system.offer_curve, 2) +end + +""" + gens_per_zone(gens::Generators) + +Returns a `Dict` with keys of `Zone` numbers and values of generator names in that zone. +""" +function gens_per_zone(gens::Generators) + gens_per_zone = Dict{Int, Vector{Int}}() + for (name, zone) in zip(gens.name, gens.zone) + if haskey(gens_per_zone, zone) + push!(gens_per_zone[zone], name) + else + gens_per_zone[zone] = [name] + end + end + return gens_per_zone +end + +""" + branches_by_breakpoints(branches::Branches) + +Returns three vectors containing of the names of branches which have 0, 1, and 2 breakpoints. +""" +function branches_by_breakpoints(branches::Branches) + zero_bp, one_bp, two_bp = String[], String[], String[] + for (name, breaks, mon) in zip(branches.name, branches.break_points, branches.is_monitored) + if mon + if length(breaks) == 0 + push!(zero_bp, name) + elseif length(breaks) == 1 + push!(one_bp, name) + else + push!(two_bp, name) + end + end + end + return zero_bp, one_bp, two_bp +end + +function get_regulation_ts(system::System) + return get_ancillary_ts(system, :reg) +end + +function get_spinning_ts(system::System) + return get_ancillary_ts(system, :spin) +end + +function get_on_sup_ts(system::System) + return get_ancillary_ts(system, :sup_on) +end + +function get_off_sup_ts(system::System) + return get_ancillary_ts(system, :sup_off) +end + +""" + get_ancillary_ts(system, service) + +Returns a `KeyedArray` with all the generators and their contribution to the specified +`service`. If a generator does not contribute to the `service` it will appear with a `0.0`. +""" +function get_ancillary_ts(system::System, service::Symbol) + all_gens = system.generators.name + all_gens_array = KeyedArray(zeros(length(all_gens), 24); ids=all_gens, datetimes=get_datetimes(system)) + for g in axiskeys(getproperty(system.ancillary_services, service), 1) + all_gens_array(g, :) .+= getproperty(system.ancillary_services, service)(g, :) + end + return all_gens_array +end diff --git a/src/system.jl b/src/system.jl new file mode 100644 index 0000000..0a0f68e --- /dev/null +++ b/src/system.jl @@ -0,0 +1,192 @@ +""" + ServicesTimeSeries(reg, spin, sup_on, sup_off) + +Type defining services time series. Fields are KeyedArray where the keys are generator names +x datetimes. Only generators that provide each service are included in the array. +""" +struct ServicesTimeSeries + reg::KeyedArray{Float64} + spin::KeyedArray{Float64} + sup_on::KeyedArray{Float64} + sup_off::KeyedArray{Float64} +end + +""" + Zone(number, reg, spin, sup_on, sup_off) + +Type defining a market zone. The `Zone` is identified by a number. The other fields contain +the service requirements for the zone. +""" +struct Zone + number::Int64 + reg::Float64 + spin::Float64 + sup_on::Float64 + sup_off::Float64 +end + +###### Static Component Types ###### + +abstract type StaticComponent end + +Base.length(components::StaticComponent) = length(getfield(components, 1)) +# define iterators interface? or Tables interface? + +""" + Generators( + name::Vector{Int} + zone::Vector{Int} + startup_cost::Vector{Float64} + shutdown_cost::Vector{Float64} + no_load_cost::Vector{Float64} + time_at_status::Vector{Float64} + min_uptime::Vector{Float64} + min_downtime::Vector{Float64} + ramp_up::Vector{Float64} + ramp_down::Vector{Float64} + initial_gen::Vector{Float64} + technology::Vector{Symbol} + ) + +Type for static generator component attributes (i.e. things that describe a generator that +are not time series data). +""" +struct Generators <: StaticComponent + name::Vector{Int} + zone::Vector{Int} + startup_cost::Vector{Float64} + shutdown_cost::Vector{Float64} + no_load_cost::Vector{Float64} + time_at_status::Vector{Float64} + min_uptime::Vector{Float64} + min_downtime::Vector{Float64} + ramp_up::Vector{Float64} + ramp_down::Vector{Float64} + initial_gen::Vector{Float64} # this one changes in RT with _update_system_generation - but is that necessary - it could be a mutable time series + technology::Vector{Symbol} # or we could define some types for this if we like +end + +""" + Buses(name, base_voltage) + +Type for static bus component attributes. +""" +struct Buses <: StaticComponent + name::Vector{String} + base_voltage::Vector{Float64} +end + +""" + Branches( + name::Vector{String} + to_bus::Vector{String} + from_bus::Vector{String} + rate_a::Vector{Float64} + rate_b::Vector{Float64} + is_monitored::Vector{Bool} + break_points::Vector{Tuple{Vararg{Float64}}} + penalties::Vector{Tuple{Vararg{Float64}}} + ) + +Type for static branch component attributes. Branches may have between 0 and 2 break points +which is why the `break_points` and `penalties` fields contain variable length `Tuple`s. +""" +struct Branches <: StaticComponent + name::Vector{String} + to_bus::Vector{String} + from_bus::Vector{String} + rate_a::Vector{Float64} + rate_b::Vector{Float64} + is_monitored::Vector{Bool} + break_points::Vector{Tuple{Vararg{Float64}}} # variable length (0, 1, 2) + penalties::Vector{Tuple{Vararg{Float64}}} # length corresponding to number of break points +end + +""" + System + +The big type that represents the whole power system. + +Topology section: `Dict`s linking generators, loads, and bids to buses. +System wide static attributes: zones, buses, generators, branches, LODF and PTDF. +Time series data: all the time series associated with generators, loads and bids. All stored +as `KeyedArray`s of `names x datetimes`. +""" +struct System + gens_per_bus::Dict{Int, Vector{Int}} # Are buses going to be named by string or number? + incs_per_bus::Dict{Int, Vector{String}} + decs_per_bus::Dict{Int, Vector{String}} + psds_per_bus::Dict{Int, Vector{String}} + loads_per_bus::Dict{Int, Vector{String}} + + zones::Vector{Zone} # zones contain the time series data for services + buses::Buses + generators::Generators + branches::Branches + LODF::Dict{String, KeyedArray} + PTDF::KeyedArray + + # Generator related time series + offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}} + availability::KeyedArray{Bool} + must_run::KeyedArray{Bool} + regulation_min::KeyedArray{Float64} + regulation_max::KeyedArray{Float64} + pmin::KeyedArray{Float64} + pmax::KeyedArray{Float64} + ancillary_services::ServicesTimeSeries + + # Load time series + loads::KeyedArray{Float64} + + # Virtuals/PSD time series + increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} # in DA but not in RT + decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} + price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}} +end + +struct SystemRT + gens_per_bus::Dict{Int, Vector{Int}} # Are buses going to be named by string or number? + loads_per_bus::Dict{Int, Vector{String}} + + zones::Vector{Zone} # zones contain the time series data for services + buses::Buses + generators::Generators + branches::Branches + LODF::Dict{String, KeyedArray} + PTDF::KeyedArray + + # Generator related time series + offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}} + status::KeyedArray{Bool} # used in RT but not in DA + status_regulation::KeyedArray{Bool} # do we need this or is it contained in ancillary_services? + regulation_min::KeyedArray{Float64} + regulation_max::KeyedArray{Float64} + pmin::KeyedArray{Float64} + pmax::KeyedArray{Float64} + ancillary_services::ServicesTimeSeries + + # Load time series + loads::KeyedArray{Float64} +end + +function Base.show(io::IO, ::MIME"text/plain", system::T) where {T <: System} + Base.summary(io, system) + get(io, :compact, false) && return nothing + z = length(system.zones) - 1 + print(io, " with $z Zones") + for c in [:buses, :generators, :branches] + l = length(getproperty(getproperty(system, c), :name)) + print(io, ", $l $(c)") + end + print(io, "\n") + print(io, "Included time series: ") + for (name, type) in zip(fieldnames(T), fieldtypes(T)) + if name == last(fieldnames(T)) + print(io, "$name") + elseif type <: KeyedArray + print(io, "$name, ") + end + end + return nothing +end diff --git a/test/runtests.jl b/test/runtests.jl index 3dc2e09..054c62f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,108 @@ +using AxisKeys +using Dates using FullNetworkSystems using Test @testset "FullNetworkSystems.jl" begin - # Write your tests here. + datetimes = DateTime(2017, 12, 15):Hour(1):DateTime(2017, 12, 15, 23) + gen_ids = collect(111:1:120) + l = length(gen_ids) + fake_gen_ts = KeyedArray(rand(10, 24); ids=gen_ids, datetimes=datetimes) + fake_offer_ts = KeyedArray( + repeat([[(1.0, 100.0)]], inner=(1, 24), outer=(10, 1)); + ids=gen_ids, datetimes=datetimes + ) + fake_bool_ts = KeyedArray(rand(Bool, 10, 24); ids=gen_ids, datetimes=datetimes) + + bus_nums = [111, 222, 333] + branch_names = string.([1, 2, 3]) + bus_names = ["A", "B", "C"] + + @testset "System" begin + sts = ServicesTimeSeries(fake_gen_ts, fake_gen_ts, fake_gen_ts, fake_gen_ts) + @test sts isa ServicesTimeSeries + + zone1 = Zone(1, 1.0, 1.0, 1.0, 1.0) + zone2 = Zone(1, 4.0, 2.0, 4.0, 2.0) + zone_market = Zone(-9999, 3.0, 3.0, 3.0, 3.0) + @test zone1 isa Zone + + gens = Generators( + gen_ids, + fill(zone1.number, l), # zone + fill(0.0, l), # start_up_cost + fill(1.0, l), # shut_down_cost + fill(1.0, l), # no_load_cost + fill(24.0, l), # time_at_status + fill(24.0, l), # min_uptime + fill(24.0, l), # min_downtime + fill(2.0, l), # ramp_up + fill(2.0, l), # ramp_down + fill(100.0, l), # initial_gen + fill(:tech, l) + ) + expected_gens_zones = Dict(1 => gen_ids) + @test gens isa Generators + @test length(gens) == l + @test gens_per_zone(gens) == expected_gens_zones + + buses = Buses(bus_names, rand(length(bus_names))) + @test buses isa Buses + @test length(buses) == length(bus_names) + + branches = Branches( + branch_names, + bus_names, + reverse(bus_names), + rand(3), + rand(3), + [true, true, false], + [(100.0, 102.0), (100.0,), ()], + [(5.0, 6.0), (5.0,), ()] + ) + @test branches isa Branches + @test length(branches) == length(branch_names) + + zero_bp, one_bp, two_bp = branches_by_breakpoints(branches) + @test zero_bp == String[] # unmonitored + @test one_bp == ["2"] + @test two_bp == ["1"] + + gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_nums) + incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + decs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + psds_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + loads_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + + LODF = Dict("CONTIN_1" => KeyedArray(rand(3, 1); buses=bus_names, branch=[first(branch_names)])) + PTDF = KeyedArray(rand(3, 3); row=bus_names, col=bus_names) + + system = System( + gens_per_bus, + incs_per_bus, + decs_per_bus, + psds_per_bus, + loads_per_bus, + [zone1, zone2, zone_market], + buses, + gens, + branches, + LODF, + PTDF, + fake_offer_ts, + fake_bool_ts, + fake_bool_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + sts, + fake_gen_ts, + fake_offer_ts, + fake_offer_ts, + fake_offer_ts + ) + + @test get_datetimes(system) == datetimes + end end From a14f3137a755f4c89a23969850fab91d8d844e68 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Wed, 20 Apr 2022 09:42:43 +0100 Subject: [PATCH 02/14] comment julia 1.0 job --- .github/workflows/CI.yml | 2 +- Project.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 47f9e93..1c5603d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -16,7 +16,7 @@ jobs: matrix: version: - '1' - - '1.0' + # - '1.0' os: - ubuntu-latest arch: diff --git a/Project.toml b/Project.toml index b97fc7e..6e649f0 100644 --- a/Project.toml +++ b/Project.toml @@ -7,7 +7,6 @@ version = "1.0.0" AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" - [compat] AxisKeys = "0.2" julia = "1" From 68f9ed72b60c183bfc49e4cde0e6fdced4f5316e Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Wed, 20 Apr 2022 10:19:06 +0100 Subject: [PATCH 03/14] restructure src/tests witg same file names --- src/FullNetworkSystems.jl | 1 - src/accessors.jl | 77 ------------------------- src/system.jl | 47 ++++++++++++++++ test/runtests.jl | 102 +-------------------------------- test/system.jl | 115 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 179 deletions(-) delete mode 100644 src/accessors.jl create mode 100644 test/system.jl diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index 849f2c1..16b0e11 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -8,6 +8,5 @@ export ServicesTimeSeries, Zone, StaticComponent, Generators, Buses, Branches export gens_per_zone, branches_by_breakpoints, get_datetimes include("system.jl") -include("accessors.jl") end diff --git a/src/accessors.jl b/src/accessors.jl deleted file mode 100644 index 3af3935..0000000 --- a/src/accessors.jl +++ /dev/null @@ -1,77 +0,0 @@ -""" - get_datetimes(system) - -Extract datetimes from a `System`. -""" -function get_datetimes(system::System) - return axiskeys(system.offer_curve, 2) -end - -""" - gens_per_zone(gens::Generators) - -Returns a `Dict` with keys of `Zone` numbers and values of generator names in that zone. -""" -function gens_per_zone(gens::Generators) - gens_per_zone = Dict{Int, Vector{Int}}() - for (name, zone) in zip(gens.name, gens.zone) - if haskey(gens_per_zone, zone) - push!(gens_per_zone[zone], name) - else - gens_per_zone[zone] = [name] - end - end - return gens_per_zone -end - -""" - branches_by_breakpoints(branches::Branches) - -Returns three vectors containing of the names of branches which have 0, 1, and 2 breakpoints. -""" -function branches_by_breakpoints(branches::Branches) - zero_bp, one_bp, two_bp = String[], String[], String[] - for (name, breaks, mon) in zip(branches.name, branches.break_points, branches.is_monitored) - if mon - if length(breaks) == 0 - push!(zero_bp, name) - elseif length(breaks) == 1 - push!(one_bp, name) - else - push!(two_bp, name) - end - end - end - return zero_bp, one_bp, two_bp -end - -function get_regulation_ts(system::System) - return get_ancillary_ts(system, :reg) -end - -function get_spinning_ts(system::System) - return get_ancillary_ts(system, :spin) -end - -function get_on_sup_ts(system::System) - return get_ancillary_ts(system, :sup_on) -end - -function get_off_sup_ts(system::System) - return get_ancillary_ts(system, :sup_off) -end - -""" - get_ancillary_ts(system, service) - -Returns a `KeyedArray` with all the generators and their contribution to the specified -`service`. If a generator does not contribute to the `service` it will appear with a `0.0`. -""" -function get_ancillary_ts(system::System, service::Symbol) - all_gens = system.generators.name - all_gens_array = KeyedArray(zeros(length(all_gens), 24); ids=all_gens, datetimes=get_datetimes(system)) - for g in axiskeys(getproperty(system.ancillary_services, service), 1) - all_gens_array(g, :) .+= getproperty(system.ancillary_services, service)(g, :) - end - return all_gens_array -end diff --git a/src/system.jl b/src/system.jl index 0a0f68e..9780409 100644 --- a/src/system.jl +++ b/src/system.jl @@ -66,6 +66,23 @@ struct Generators <: StaticComponent technology::Vector{Symbol} # or we could define some types for this if we like end +""" + gens_per_zone(gens::Generators) + +Returns a `Dict` with keys of `Zone` numbers and values of generator names in that zone. +""" +function gens_per_zone(gens::Generators) + gens_per_zone = Dict{Int, Vector{Int}}() + for (name, zone) in zip(gens.name, gens.zone) + if haskey(gens_per_zone, zone) + push!(gens_per_zone[zone], name) + else + gens_per_zone[zone] = [name] + end + end + return gens_per_zone +end + """ Buses(name, base_voltage) @@ -102,6 +119,27 @@ struct Branches <: StaticComponent penalties::Vector{Tuple{Vararg{Float64}}} # length corresponding to number of break points end +""" + branches_by_breakpoints(branches::Branches) + +Returns three vectors containing of the names of branches which have 0, 1, and 2 breakpoints. +""" +function branches_by_breakpoints(branches::Branches) + zero_bp, one_bp, two_bp = String[], String[], String[] + for (name, breaks, mon) in zip(branches.name, branches.break_points, branches.is_monitored) + if mon + if length(breaks) == 0 + push!(zero_bp, name) + elseif length(breaks) == 1 + push!(one_bp, name) + else + push!(two_bp, name) + end + end + end + return zero_bp, one_bp, two_bp +end + """ System @@ -190,3 +228,12 @@ function Base.show(io::IO, ::MIME"text/plain", system::T) where {T <: System} end return nothing end + +""" + get_datetimes(system) + +Extract datetimes from a `System`. +""" +function get_datetimes(system::System) + return axiskeys(system.offer_curve, 2) +end diff --git a/test/runtests.jl b/test/runtests.jl index 054c62f..aa56f6f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,105 +4,5 @@ using FullNetworkSystems using Test @testset "FullNetworkSystems.jl" begin - datetimes = DateTime(2017, 12, 15):Hour(1):DateTime(2017, 12, 15, 23) - gen_ids = collect(111:1:120) - l = length(gen_ids) - fake_gen_ts = KeyedArray(rand(10, 24); ids=gen_ids, datetimes=datetimes) - fake_offer_ts = KeyedArray( - repeat([[(1.0, 100.0)]], inner=(1, 24), outer=(10, 1)); - ids=gen_ids, datetimes=datetimes - ) - fake_bool_ts = KeyedArray(rand(Bool, 10, 24); ids=gen_ids, datetimes=datetimes) - - bus_nums = [111, 222, 333] - branch_names = string.([1, 2, 3]) - bus_names = ["A", "B", "C"] - - @testset "System" begin - sts = ServicesTimeSeries(fake_gen_ts, fake_gen_ts, fake_gen_ts, fake_gen_ts) - @test sts isa ServicesTimeSeries - - zone1 = Zone(1, 1.0, 1.0, 1.0, 1.0) - zone2 = Zone(1, 4.0, 2.0, 4.0, 2.0) - zone_market = Zone(-9999, 3.0, 3.0, 3.0, 3.0) - @test zone1 isa Zone - - gens = Generators( - gen_ids, - fill(zone1.number, l), # zone - fill(0.0, l), # start_up_cost - fill(1.0, l), # shut_down_cost - fill(1.0, l), # no_load_cost - fill(24.0, l), # time_at_status - fill(24.0, l), # min_uptime - fill(24.0, l), # min_downtime - fill(2.0, l), # ramp_up - fill(2.0, l), # ramp_down - fill(100.0, l), # initial_gen - fill(:tech, l) - ) - expected_gens_zones = Dict(1 => gen_ids) - @test gens isa Generators - @test length(gens) == l - @test gens_per_zone(gens) == expected_gens_zones - - buses = Buses(bus_names, rand(length(bus_names))) - @test buses isa Buses - @test length(buses) == length(bus_names) - - branches = Branches( - branch_names, - bus_names, - reverse(bus_names), - rand(3), - rand(3), - [true, true, false], - [(100.0, 102.0), (100.0,), ()], - [(5.0, 6.0), (5.0,), ()] - ) - @test branches isa Branches - @test length(branches) == length(branch_names) - - zero_bp, one_bp, two_bp = branches_by_breakpoints(branches) - @test zero_bp == String[] # unmonitored - @test one_bp == ["2"] - @test two_bp == ["1"] - - gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_nums) - incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - decs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - psds_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - loads_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - - LODF = Dict("CONTIN_1" => KeyedArray(rand(3, 1); buses=bus_names, branch=[first(branch_names)])) - PTDF = KeyedArray(rand(3, 3); row=bus_names, col=bus_names) - - system = System( - gens_per_bus, - incs_per_bus, - decs_per_bus, - psds_per_bus, - loads_per_bus, - [zone1, zone2, zone_market], - buses, - gens, - branches, - LODF, - PTDF, - fake_offer_ts, - fake_bool_ts, - fake_bool_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - fake_gen_ts, - sts, - fake_gen_ts, - fake_offer_ts, - fake_offer_ts, - fake_offer_ts - ) - - @test get_datetimes(system) == datetimes - end + include("system.jl") end diff --git a/test/system.jl b/test/system.jl new file mode 100644 index 0000000..ddef470 --- /dev/null +++ b/test/system.jl @@ -0,0 +1,115 @@ +@testset "system.jl" begin + datetimes = DateTime(2017, 12, 15):Hour(1):DateTime(2017, 12, 15, 23) + gen_ids = collect(111:1:120) + l = length(gen_ids) + fake_gen_ts = KeyedArray(rand(10, 24); ids=gen_ids, datetimes=datetimes) + fake_offer_ts = KeyedArray( + repeat([[(1.0, 100.0)]], inner=(1, 24), outer=(10, 1)); + ids=gen_ids, datetimes=datetimes + ) + fake_bool_ts = KeyedArray(rand(Bool, 10, 24); ids=gen_ids, datetimes=datetimes) + + bus_nums = [111, 222, 333] + branch_names = string.([1, 2, 3]) + bus_names = ["A", "B", "C"] + + @testset "System" begin + sts = ServicesTimeSeries(fake_gen_ts, fake_gen_ts, fake_gen_ts, fake_gen_ts) + @test sts isa ServicesTimeSeries + + zone1 = Zone(1, 1.0, 1.0, 1.0, 1.0) + zone2 = Zone(1, 4.0, 2.0, 4.0, 2.0) + zone_market = Zone(-9999, 3.0, 3.0, 3.0, 3.0) + @testset "Zone" begin + @test zone1 isa Zone + end + + gens = Generators( + gen_ids, + fill(zone1.number, l), # zone + fill(0.0, l), # start_up_cost + fill(1.0, l), # shut_down_cost + fill(1.0, l), # no_load_cost + fill(24.0, l), # time_at_status + fill(24.0, l), # min_uptime + fill(24.0, l), # min_downtime + fill(2.0, l), # ramp_up + fill(2.0, l), # ramp_down + fill(100.0, l), # initial_gen + fill(:tech, l) + ) + expected_gens_zones = Dict(1 => gen_ids) + @testset "Generators" begin + @test gens isa Generators + @test length(gens) == l + @test gens_per_zone(gens) == expected_gens_zones + end + + buses = Buses(bus_names, rand(length(bus_names))) + @testset "Buses" begin + @test buses isa Buses + @test length(buses) == length(bus_names) + end + + branches = Branches( + branch_names, + bus_names, + reverse(bus_names), + rand(3), + rand(3), + [true, true, false], + [(100.0, 102.0), (100.0,), ()], + [(5.0, 6.0), (5.0,), ()] + ) + @testset "Branches" begin + @test branches isa Branches + @test length(branches) == length(branch_names) + + zero_bp, one_bp, two_bp = branches_by_breakpoints(branches) + @test zero_bp == String[] # unmonitored + @test one_bp == ["2"] + @test two_bp == ["1"] + end + + gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_nums) + incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + decs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + psds_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + loads_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + + LODF = Dict( + "CONTIN_1" => KeyedArray(rand(3, 1); + buses=bus_names, branch=[first(branch_names)]) + ) + PTDF = KeyedArray(rand(3, 3); row=bus_names, col=bus_names) + + system = System( + gens_per_bus, + incs_per_bus, + decs_per_bus, + psds_per_bus, + loads_per_bus, + [zone1, zone2, zone_market], + buses, + gens, + branches, + LODF, + PTDF, + fake_offer_ts, + fake_bool_ts, + fake_bool_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + sts, + fake_gen_ts, + fake_offer_ts, + fake_offer_ts, + fake_offer_ts + ) + + @test system isa System + @test get_datetimes(system) == datetimes + end +end From 88a8a24b3dbd6663b54cf34b76d86f94f1d8878c Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Wed, 20 Apr 2022 11:28:34 +0100 Subject: [PATCH 04/14] test rt system --- src/FullNetworkSystems.jl | 2 +- src/system.jl | 24 +++++++++++++----------- test/system.jl | 29 ++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index 16b0e11..705f8a0 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -3,7 +3,7 @@ module FullNetworkSystems using AxisKeys using Dates -export System, SystemRT +export System, SystemDA, SystemRT export ServicesTimeSeries, Zone, StaticComponent, Generators, Buses, Branches export gens_per_zone, branches_by_breakpoints, get_datetimes diff --git a/src/system.jl b/src/system.jl index 9780409..eae3777 100644 --- a/src/system.jl +++ b/src/system.jl @@ -62,8 +62,8 @@ struct Generators <: StaticComponent min_downtime::Vector{Float64} ramp_up::Vector{Float64} ramp_down::Vector{Float64} - initial_gen::Vector{Float64} # this one changes in RT with _update_system_generation - but is that necessary - it could be a mutable time series - technology::Vector{Symbol} # or we could define some types for this if we like + initial_gen::Vector{Float64} # this one changes in RT with _update_system_generation - but is that necessary - could be a mutable time series? + technology::Vector{Symbol} end """ @@ -141,7 +141,7 @@ function branches_by_breakpoints(branches::Branches) end """ - System + SystemDA The big type that represents the whole power system. @@ -150,14 +150,16 @@ System wide static attributes: zones, buses, generators, branches, LODF and PTDF Time series data: all the time series associated with generators, loads and bids. All stored as `KeyedArray`s of `names x datetimes`. """ -struct System +abstract type System end + +struct SystemDA <: System gens_per_bus::Dict{Int, Vector{Int}} # Are buses going to be named by string or number? incs_per_bus::Dict{Int, Vector{String}} decs_per_bus::Dict{Int, Vector{String}} psds_per_bus::Dict{Int, Vector{String}} loads_per_bus::Dict{Int, Vector{String}} - zones::Vector{Zone} # zones contain the time series data for services + zones::Vector{Zone} buses::Buses generators::Generators branches::Branches @@ -178,16 +180,16 @@ struct System loads::KeyedArray{Float64} # Virtuals/PSD time series - increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} # in DA but not in RT + increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}} end -struct SystemRT +struct SystemRT <: System gens_per_bus::Dict{Int, Vector{Int}} # Are buses going to be named by string or number? loads_per_bus::Dict{Int, Vector{String}} - zones::Vector{Zone} # zones contain the time series data for services + zones::Vector{Zone} buses::Buses generators::Generators branches::Branches @@ -196,8 +198,8 @@ struct SystemRT # Generator related time series offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}} - status::KeyedArray{Bool} # used in RT but not in DA - status_regulation::KeyedArray{Bool} # do we need this or is it contained in ancillary_services? + status::KeyedArray{Bool} + status_regulation::KeyedArray{Bool} regulation_min::KeyedArray{Float64} regulation_max::KeyedArray{Float64} pmin::KeyedArray{Float64} @@ -222,7 +224,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 <: KeyedArray + elseif type <: KeyedArray && name != :PTDF print(io, "$name, ") end end diff --git a/test/system.jl b/test/system.jl index ddef470..b918586 100644 --- a/test/system.jl +++ b/test/system.jl @@ -83,7 +83,7 @@ ) PTDF = KeyedArray(rand(3, 3); row=bus_names, col=bus_names) - system = System( + da_system = SystemDA( gens_per_bus, incs_per_bus, decs_per_bus, @@ -109,7 +109,30 @@ fake_offer_ts ) - @test system isa System - @test get_datetimes(system) == datetimes + @test da_system isa SystemDA + @test get_datetimes(da_system) == datetimes + + rt_system = SystemRT( + gens_per_bus, + loads_per_bus, + [zone1, zone2, zone_market], + buses, + gens, + branches, + LODF, + PTDF, + fake_offer_ts, + fake_bool_ts, + fake_bool_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + sts, + fake_gen_ts + ) + + @test rt_system isa SystemRT + @test get_datetimes(rt_system) == datetimes end end From 01c923c4c6566ec33b327f492dd12e0cc1d0329c Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Wed, 20 Apr 2022 11:42:01 +0100 Subject: [PATCH 05/14] use bus names not nums --- src/system.jl | 14 +++++++------- test/system.jl | 11 +++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/system.jl b/src/system.jl index eae3777..7357eea 100644 --- a/src/system.jl +++ b/src/system.jl @@ -153,11 +153,11 @@ as `KeyedArray`s of `names x datetimes`. abstract type System end struct SystemDA <: System - gens_per_bus::Dict{Int, Vector{Int}} # Are buses going to be named by string or number? - incs_per_bus::Dict{Int, Vector{String}} - decs_per_bus::Dict{Int, Vector{String}} - psds_per_bus::Dict{Int, Vector{String}} - loads_per_bus::Dict{Int, Vector{String}} + gens_per_bus::Dict{String, Vector{Int}} + incs_per_bus::Dict{String, Vector{String}} + decs_per_bus::Dict{String, Vector{String}} + psds_per_bus::Dict{String, Vector{String}} + loads_per_bus::Dict{String, Vector{String}} zones::Vector{Zone} buses::Buses @@ -186,8 +186,8 @@ struct SystemDA <: System end struct SystemRT <: System - gens_per_bus::Dict{Int, Vector{Int}} # Are buses going to be named by string or number? - loads_per_bus::Dict{Int, Vector{String}} + gens_per_bus::Dict{String, Vector{Int}} + loads_per_bus::Dict{String, Vector{String}} zones::Vector{Zone} buses::Buses diff --git a/test/system.jl b/test/system.jl index b918586..d6803f3 100644 --- a/test/system.jl +++ b/test/system.jl @@ -9,7 +9,6 @@ ) fake_bool_ts = KeyedArray(rand(Bool, 10, 24); ids=gen_ids, datetimes=datetimes) - bus_nums = [111, 222, 333] branch_names = string.([1, 2, 3]) bus_names = ["A", "B", "C"] @@ -71,11 +70,11 @@ @test two_bp == ["1"] end - gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_nums) - incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - decs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - psds_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) - loads_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_nums) + gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_names) + incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) + decs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) + psds_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) + loads_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) LODF = Dict( "CONTIN_1" => KeyedArray(rand(3, 1); From f1583c40a10bf25233123f5a81dfc7334a2c6d78 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Wed, 20 Apr 2022 12:19:07 +0100 Subject: [PATCH 06/14] update docs --- docs/Manifest.toml | 288 +++++++++++++++++++++++++++++++++++++++++++++ src/system.jl | 73 +++++++++++- 2 files changed, 355 insertions(+), 6 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 70771e5..be330bf 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -8,13 +8,95 @@ git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" +[[deps.AbstractFFTs]] +deps = ["ChainRulesCore", "LinearAlgebra"] +git-tree-sha1 = "6f1d9bc1c08f9f4a8fa92e3ea3cb50153a1b40d4" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.1.0" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[deps.ArrayInterface]] +deps = ["Compat", "IfElse", "LinearAlgebra", "Requires", "SparseArrays", "Static"] +git-tree-sha1 = "c933ce606f6535a7c7b98e1d86d5d1014f730596" +uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +version = "5.0.7" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.AxisKeys]] +deps = ["AbstractFFTs", "ChainRulesCore", "CovarianceEstimation", "IntervalSets", "InvertedIndices", "LazyStack", "LinearAlgebra", "NamedDims", "OffsetArrays", "Statistics", "StatsBase", "Tables"] +git-tree-sha1 = "8380654f50f0d73731060da163a5ae31aa29347e" +uuid = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" +version = "0.2.1" + [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +[[deps.ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "9950387274246d08af38f6eef8cb5480862a435f" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.14.0" + +[[deps.ChangesOfVariables]] +deps = ["ChainRulesCore", "LinearAlgebra", "Test"] +git-tree-sha1 = "bf98fa45a0a4cee295de98d4c1462be26345b9a1" +uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" +version = "0.1.2" + +[[deps.Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "b153278a25dd42c65abbf4e62344f9d22e59191b" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.43.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[deps.CovarianceEstimation]] +deps = ["LinearAlgebra", "Statistics", "StatsBase"] +git-tree-sha1 = "a3e070133acab996660d31dcf479ea42849e368f" +uuid = "587fd27a-f159-11e8-2dae-1979310e6154" +version = "0.2.7" + +[[deps.DataAPI]] +git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.9.0" + +[[deps.DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "3daef5523dd2e769dad2365274f760ff5f282c7d" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.11" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + [[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +[[deps.DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + [[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" @@ -27,7 +109,18 @@ git-tree-sha1 = "6edbf28671b4df4f692e54ae72f1e35851cfbf38" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" version = "0.27.16" +[[deps.Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[deps.EllipsisNotation]] +deps = ["ArrayInterface"] +git-tree-sha1 = "d064b0340db45d48893e7604ec95e7a2dc9da904" +uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949" +version = "1.5.0" + [[deps.FullNetworkSystems]] +deps = ["AxisKeys", "Dates"] path = ".." uuid = "877b7152-b508-43dc-81fb-72341a693988" version = "1.0.0" @@ -38,39 +131,146 @@ git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" version = "0.2.2" +[[deps.IfElse]] +git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" +uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" +version = "0.1.1" + [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[deps.IntervalSets]] +deps = ["Dates", "EllipsisNotation", "Statistics"] +git-tree-sha1 = "bcf640979ee55b652f3b01650444eb7bbe3ea837" +uuid = "8197267c-284f-5f27-9208-e0e47529a953" +version = "0.5.4" + +[[deps.InverseFunctions]] +deps = ["Test"] +git-tree-sha1 = "91b5dcf362c5add98049e6c29ee756910b03051d" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.3" + +[[deps.InvertedIndices]] +git-tree-sha1 = "bee5f1ef5bf65df56bdd2e40447590b272a5471f" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.1.0" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.1" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.3" +[[deps.LazyStack]] +deps = ["LinearAlgebra", "NamedDims", "OffsetArrays", "Test", "ZygoteRules"] +git-tree-sha1 = "a8bf67afad3f1ee59d367267adb7c44ccac7fdee" +uuid = "1fad7336-0346-5a1a-a56f-a06ba010965b" +version = "0.0.7" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + [[deps.LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.LogExpFunctions]] +deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "a970d55c2ad8084ca317a4658ba6ce99b7523571" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.12" + [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +[[deps.MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.9" + [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.0.2" + [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[deps.NamedDims]] +deps = ["AbstractFFTs", "ChainRulesCore", "CovarianceEstimation", "LinearAlgebra", "Pkg", "Requires", "Statistics"] +git-tree-sha1 = "0856b62716585eb90cc1dada226ac9eab5f69aa5" +uuid = "356022a1-0364-5f58-8944-0da4b18d706f" +version = "0.2.47" + [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +[[deps.OffsetArrays]] +deps = ["Adapt"] +git-tree-sha1 = "043017e0bdeff61cfbb7afeb558ab29536bbb5ed" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "1.10.8" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" + +[[deps.OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + [[deps.Parsers]] deps = ["Dates"] git-tree-sha1 = "621f4f3b4977325b9128d5fae7a8b4829a0c2222" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.2.4" +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -83,18 +283,106 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" deps = ["SHA", "Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[deps.SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.0.1" + +[[deps.SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.Static]] +deps = ["IfElse"] +git-tree-sha1 = "87e9954dfa33fd145694e42337bdd3d5b07021a6" +uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +version = "0.6.0" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StatsAPI]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "8d7530a38dbd2c397be7ddd01a424e4f411dcc41" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.2.2" + +[[deps.StatsBase]] +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "8977b17906b0a1cc74ab2e3a05faa16cf08a8291" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.33.16" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] +git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.7.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[deps.ZygoteRules]] +deps = ["MacroTools"] +git-tree-sha1 = "8c1a8e4dfacb1fd631745552c8db35d0deb09ea0" +uuid = "700de1a5-db45-46bc-99cf-38207098b444" +version = "0.2.2" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/src/system.jl b/src/system.jl index 7357eea..61eace2 100644 --- a/src/system.jl +++ b/src/system.jl @@ -30,7 +30,7 @@ end abstract type StaticComponent end Base.length(components::StaticComponent) = length(getfield(components, 1)) -# define iterators interface? or Tables interface? +# define interfaces? Iterator? Table? """ Generators( @@ -141,17 +141,51 @@ function branches_by_breakpoints(branches::Branches) end """ - SystemDA + System -The big type that represents the whole power system. +The abstract type for representing the whole power system including topology, static +components and their attributes, and time series data. -Topology section: `Dict`s linking generators, loads, and bids to buses. -System wide static attributes: zones, buses, generators, branches, LODF and PTDF. +Topology: `Dict`s linking generators, loads, and bids (if present) to buses. +System wide static components and grid matrices: zones, buses, generators, branches, LODF and PTDF. Time series data: all the time series associated with generators, loads and bids. All stored -as `KeyedArray`s of `names x datetimes`. +as `KeyedArray`s of `ids x datetimes`. """ abstract type System end +""" + struct SystemDA <: System + +Subtype of a `System` for modelling the day-ahead market. + +Fields: + - Topology + - `gens_per_bus::Dict{String, Vector{Int}}` + - `incs_per_bus::Dict{String, Vector{String}}` + - `decs_per_bus::Dict{String, Vector{String}}` + - `psds_per_bus::Dict{String, Vector{String}}` + - `loads_per_bus::Dict{String, Vector{String}}` + - Static components + - `zones::Vector{Zone}` + - `buses::Buses` + - `generators::Generators` + - `branches::Branches` + - `LODF::Dict{String, KeyedArray}` + - `PTDF::KeyedArray` + - Time series + - `offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}}` + - `availability::KeyedArray{Bool}` + - `must_run::KeyedArray{Bool}` + - `regulation_min::KeyedArray{Float64}` + - `regulation_max::KeyedArray{Float64}` + - `pmin::KeyedArray{Float64}` + - `pmax::KeyedArray{Float64}` + - `ancillary_services::ServicesTimeSeries` + - `loads::KeyedArray{Float64}` + - `increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}}` + - `decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}}` + - `price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}}` +""" struct SystemDA <: System gens_per_bus::Dict{String, Vector{Int}} incs_per_bus::Dict{String, Vector{String}} @@ -185,6 +219,33 @@ struct SystemDA <: System price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}} end +""" + struct SystemRT <: System + +Subtype of a `System` for modelling the real-time market. + +Fields: + - Topology + - `gens_per_bus::Dict{String, Vector{Int}}` + - `loads_per_bus::Dict{String, Vector{String}}` + - Static components + - `zones::Vector{Zone}` + - `buses::Buses` + - `generators::Generators` + - `branches::Branches` + - `LODF::Dict{String, KeyedArray}` + - `PTDF::KeyedArray` + - Time series + - `offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}}` + - `status::KeyedArray{Bool}` + - `status_regulation::KeyedArray{Bool}` + - `regulation_min::KeyedArray{Float64}` + - `regulation_max::KeyedArray{Float64}` + - `pmin::KeyedArray{Float64}` + - `pmax::KeyedArray{Float64}` + - `ancillary_services::ServicesTimeSeries` + - `loads::KeyedArray{Float64}` +""" struct SystemRT <: System gens_per_bus::Dict{String, Vector{Int}} loads_per_bus::Dict{String, Vector{String}} From fc4672fb49650ea9930a4694e436326e0b14671f Mon Sep 17 00:00:00 2001 From: BSnelling Date: Thu, 21 Apr 2022 13:47:21 +0100 Subject: [PATCH 07/14] Apply suggestions from code review Co-authored-by: Nick Robinson --- .github/workflows/CI.yml | 2 +- Project.toml | 2 +- src/system.jl | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1c5603d..a975195 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -16,7 +16,7 @@ jobs: matrix: version: - '1' - # - '1.0' + - '1.6' os: - ubuntu-latest arch: diff --git a/Project.toml b/Project.toml index 6e649f0..e3bd79d 100644 --- a/Project.toml +++ b/Project.toml @@ -9,7 +9,7 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" [compat] AxisKeys = "0.2" -julia = "1" +julia = "1.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/system.jl b/src/system.jl index 61eace2..41d0f3c 100644 --- a/src/system.jl +++ b/src/system.jl @@ -8,7 +8,7 @@ struct ServicesTimeSeries reg::KeyedArray{Float64} spin::KeyedArray{Float64} sup_on::KeyedArray{Float64} - sup_off::KeyedArray{Float64} + sup_off::KeyedArray{Float64, 2} end """ @@ -198,7 +198,7 @@ struct SystemDA <: System generators::Generators branches::Branches LODF::Dict{String, KeyedArray} - PTDF::KeyedArray + PTDF::KeyedArray{Float32, 2} # Generator related time series offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}} @@ -277,7 +277,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::T) where {T <: System} z = length(system.zones) - 1 print(io, " with $z Zones") for c in [:buses, :generators, :branches] - l = length(getproperty(getproperty(system, c), :name)) + l = length(getproperty(system, c)) print(io, ", $l $(c)") end print(io, "\n") From 81a5c5c074a9696b670d0e6d91b06c0aacdb1344 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Mon, 25 Apr 2022 11:59:44 +0100 Subject: [PATCH 08/14] Lots of docs updates --- Project.toml | 4 + docs/Manifest.toml | 8 +- docs/Project.toml | 2 + docs/make.jl | 2 + src/FullNetworkSystems.jl | 2 + src/system.jl | 261 +++++++++++++++++++++----------------- test/system.jl | 4 +- 7 files changed, 164 insertions(+), 119 deletions(-) diff --git a/Project.toml b/Project.toml index e3bd79d..e14dc1d 100644 --- a/Project.toml +++ b/Project.toml @@ -6,9 +6,13 @@ version = "1.0.0" [deps] AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +InlineStrings = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" [compat] AxisKeys = "0.2" +DocStringExtensions = "0.8" +InlineStrings = "1" julia = "1.6" [extras] diff --git a/docs/Manifest.toml b/docs/Manifest.toml index be330bf..1b9d8e6 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -120,7 +120,7 @@ uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949" version = "1.5.0" [[deps.FullNetworkSystems]] -deps = ["AxisKeys", "Dates"] +deps = ["AxisKeys", "Dates", "DocStringExtensions", "InlineStrings"] path = ".." uuid = "877b7152-b508-43dc-81fb-72341a693988" version = "1.0.0" @@ -136,6 +136,12 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" +[[deps.InlineStrings]] +deps = ["Parsers"] +git-tree-sha1 = "61feba885fac3a407465726d0c330b3055df897f" +uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" +version = "1.1.2" + [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" diff --git a/docs/Project.toml b/docs/Project.toml index 2d47ccb..8b362d5 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,5 @@ [deps] +AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" FullNetworkSystems = "877b7152-b508-43dc-81fb-72341a693988" +InlineStrings = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" diff --git a/docs/make.jl b/docs/make.jl index fd75231..f910622 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,4 +1,6 @@ +using AxisKeys: KeyedArray using FullNetworkSystems +using InlineStrings: String15, String31 using Documenter DocMeta.setdocmeta!(FullNetworkSystems, :DocTestSetup, :(using FullNetworkSystems); recursive=true) diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index 705f8a0..b00776e 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -2,6 +2,8 @@ module FullNetworkSystems using AxisKeys using Dates +using DocStringExtensions +using InlineStrings export System, SystemDA, SystemRT export ServicesTimeSeries, Zone, StaticComponent, Generators, Buses, Branches diff --git a/src/system.jl b/src/system.jl index 41d0f3c..a9639e2 100644 --- a/src/system.jl +++ b/src/system.jl @@ -1,27 +1,42 @@ """ - ServicesTimeSeries(reg, spin, sup_on, sup_off) + $TYPEDEF -Type defining services time series. Fields are KeyedArray where the keys are generator names -x datetimes. Only generators that provide each service are included in the array. +Type defining ancillary services time series. Fields are `KeyedArray` where the keys are +`generator names x datetimes`. Dollar symbol \$. + +Fields: +$TYPEDFIELDS """ struct ServicesTimeSeries - reg::KeyedArray{Float64} - spin::KeyedArray{Float64} - sup_on::KeyedArray{Float64} + "Regulation offer prices (\$ /MW)" + reg::KeyedArray{Float64, 2} + "Spinning offer prices (\$ /MW)" + spin::KeyedArray{Float64, 2} + "Supplemental on offer prices (\$ /MW)" + sup_on::KeyedArray{Float64, 2} + "Supplemental off offer prices (\$ /MW)" sup_off::KeyedArray{Float64, 2} end """ - Zone(number, reg, spin, sup_on, sup_off) + $TYPEDEF Type defining a market zone. The `Zone` is identified by a number. The other fields contain the service requirements for the zone. + +Fields: +$TYPEDFIELDS """ struct Zone + "Zone number" number::Int64 + "Zonal regulation requirement (MWs)" reg::Float64 + "Zonal spinning requirement (MWs)" spin::Float64 + "Zonal supplemental on requirement (MWs)" sup_on::Float64 + "Zonal supplemental off requirement (MWs)" sup_off::Float64 end @@ -33,36 +48,38 @@ Base.length(components::StaticComponent) = length(getfield(components, 1)) # define interfaces? Iterator? Table? """ - Generators( - name::Vector{Int} - zone::Vector{Int} - startup_cost::Vector{Float64} - shutdown_cost::Vector{Float64} - no_load_cost::Vector{Float64} - time_at_status::Vector{Float64} - min_uptime::Vector{Float64} - min_downtime::Vector{Float64} - ramp_up::Vector{Float64} - ramp_down::Vector{Float64} - initial_gen::Vector{Float64} - technology::Vector{Symbol} - ) + $TYPEDEF Type for static generator component attributes (i.e. things that describe a generator that are not time series data). + +Fields: +$TYPEDFIELDS """ struct Generators <: StaticComponent + "Generator ids/unit codes" name::Vector{Int} + "Number of the zone the generator is located in" zone::Vector{Int} + "Cost of turning on the generator (\$)" startup_cost::Vector{Float64} + "Cost of turning off the generator (\$)" shutdown_cost::Vector{Float64} + "Cost of the generator being on but not producing any MW (\$ /hour)" no_load_cost::Vector{Float64} - time_at_status::Vector{Float64} + "Hours each generator has been at its current status at the start of the day" + hours_at_status::Vector{Float64} + "Minimum time a generator has to be committed for (hours)" min_uptime::Vector{Float64} + "Minimum time a generator has to be off for (hours)" min_downtime::Vector{Float64} + "Rate at which a generator can increase generation (MW/minute)" ramp_up::Vector{Float64} + "Rate at which a generator can decrease generation (MW/minute)" ramp_down::Vector{Float64} + "Generation of generators at the start of the day (MWs)" initial_gen::Vector{Float64} # this one changes in RT with _update_system_generation - but is that necessary - could be a mutable time series? + "Symbol describing the technology of a generator" technology::Vector{Symbol} end @@ -84,38 +101,45 @@ function gens_per_zone(gens::Generators) end """ - Buses(name, base_voltage) + $TYPEDEF Type for static bus component attributes. + +Fields: +$TYPEDFIELDS """ struct Buses <: StaticComponent - name::Vector{String} + "Bus name" + name::Vector{InlineString15} + "Base volatge (kV)" base_voltage::Vector{Float64} end """ - Branches( - name::Vector{String} - to_bus::Vector{String} - from_bus::Vector{String} - rate_a::Vector{Float64} - rate_b::Vector{Float64} - is_monitored::Vector{Bool} - break_points::Vector{Tuple{Vararg{Float64}}} - penalties::Vector{Tuple{Vararg{Float64}}} - ) + $TYPEDEF Type for static branch component attributes. Branches may have between 0 and 2 break points which is why the `break_points` and `penalties` fields contain variable length `Tuple`s. + +Fields: +$TYPEDFIELDS """ struct Branches <: StaticComponent - name::Vector{String} - to_bus::Vector{String} - from_bus::Vector{String} + "Branch long name" + name::Vector{InlineString31} + "Name of the bus the branch goes to" + to_bus::Vector{InlineString15} + "Name of the bus the branche goes from" + from_bus::Vector{InlineString15} + "Power flow limit for the base case (MVA)" rate_a::Vector{Float64} + "Power flow limit for contingency scenario (MVA)" rate_b::Vector{Float64} + "Boolean defining whether the branch is monitored" is_monitored::Vector{Bool} + "Break points of the branch. Branches can have 0, 1, or 2 break points" break_points::Vector{Tuple{Vararg{Float64}}} # variable length (0, 1, 2) + "Price penalties for each of the break points of the branch (\$)" penalties::Vector{Tuple{Vararg{Float64}}} # length corresponding to number of break points end @@ -154,121 +178,125 @@ as `KeyedArray`s of `ids x datetimes`. abstract type System end """ - struct SystemDA <: System + $TYPEDEF Subtype of a `System` for modelling the day-ahead market. Fields: - - Topology - - `gens_per_bus::Dict{String, Vector{Int}}` - - `incs_per_bus::Dict{String, Vector{String}}` - - `decs_per_bus::Dict{String, Vector{String}}` - - `psds_per_bus::Dict{String, Vector{String}}` - - `loads_per_bus::Dict{String, Vector{String}}` - - Static components - - `zones::Vector{Zone}` - - `buses::Buses` - - `generators::Generators` - - `branches::Branches` - - `LODF::Dict{String, KeyedArray}` - - `PTDF::KeyedArray` - - Time series - - `offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}}` - - `availability::KeyedArray{Bool}` - - `must_run::KeyedArray{Bool}` - - `regulation_min::KeyedArray{Float64}` - - `regulation_max::KeyedArray{Float64}` - - `pmin::KeyedArray{Float64}` - - `pmax::KeyedArray{Float64}` - - `ancillary_services::ServicesTimeSeries` - - `loads::KeyedArray{Float64}` - - `increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}}` - - `decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}}` - - `price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}}` +$TYPEDFIELDS """ struct SystemDA <: System - gens_per_bus::Dict{String, Vector{Int}} - incs_per_bus::Dict{String, Vector{String}} - decs_per_bus::Dict{String, Vector{String}} - psds_per_bus::Dict{String, Vector{String}} - loads_per_bus::Dict{String, Vector{String}} - + "`Dict` where the keys are bus names and the values are generator ids at that bus" + gens_per_bus::Dict{InlineString15, Vector{Int}} + "`Dict` where the keys are bus names and the values are increment bid ids at that bus" + incs_per_bus::Dict{InlineString15, Vector{String}} + "`Dict` where the keys are bus names and the values are decrement bid ids at that bus" + decs_per_bus::Dict{InlineString15, Vector{String}} + "`Dict` where the keys are bus names and the values are price sensitive demand ids at that bus" + psds_per_bus::Dict{InlineString15, Vector{String}} + "`Dict` where the keys are bus names and the values are load ids at that bus" + loads_per_bus::Dict{InlineString15, Vector{String}} + + "Zones in the `System`, which will also include a `Zone` entry for the market wide zone" zones::Vector{Zone} buses::Buses generators::Generators branches::Branches - LODF::Dict{String, KeyedArray} - PTDF::KeyedArray{Float32, 2} + """ + The line outage distribution factor matrix of the system for a set of contingencies given + by the keys of the `Dict`. Each entry is a `KeyedArray` with axis keys + `branch names x branch on outage` + """ + LODF::Dict{String, KeyedArray{Float64, 2}} + """ + Power transfer distribution factor of the system. `KeyedArray` where the axis keys are + `branch names x bus names` + """ + PTDF::KeyedArray{Float64, 2} # Generator related time series - offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}} - availability::KeyedArray{Bool} - must_run::KeyedArray{Bool} - regulation_min::KeyedArray{Float64} - regulation_max::KeyedArray{Float64} - pmin::KeyedArray{Float64} - pmax::KeyedArray{Float64} + "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 (MWs)" + regulation_min::KeyedArray{Float64, 2} + "Generator maximum output in the ancillary services market (MWs)" + regulation_max::KeyedArray{Float64, 2} + "Generator minimum output (MWs)" + pmin::KeyedArray{Float64, 2} + "Generator maximum output (MWs)" + pmax::KeyedArray{Float64, 2} + "Time series data for ancillary services provided by generators" ancillary_services::ServicesTimeSeries # Load time series - loads::KeyedArray{Float64} + "Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" + loads::KeyedArray{Float64, 2} # Virtuals/PSD time series - increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} - decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}} - price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}} + "Increment bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" + increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} + "Decrement bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" + decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} + "Price sensitive demand time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" + price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} end """ - struct SystemRT <: System + $TYPEDEF Subtype of a `System` for modelling the real-time market. Fields: - - Topology - - `gens_per_bus::Dict{String, Vector{Int}}` - - `loads_per_bus::Dict{String, Vector{String}}` - - Static components - - `zones::Vector{Zone}` - - `buses::Buses` - - `generators::Generators` - - `branches::Branches` - - `LODF::Dict{String, KeyedArray}` - - `PTDF::KeyedArray` - - Time series - - `offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}}` - - `status::KeyedArray{Bool}` - - `status_regulation::KeyedArray{Bool}` - - `regulation_min::KeyedArray{Float64}` - - `regulation_max::KeyedArray{Float64}` - - `pmin::KeyedArray{Float64}` - - `pmax::KeyedArray{Float64}` - - `ancillary_services::ServicesTimeSeries` - - `loads::KeyedArray{Float64}` +$TYPEDFIELDS """ struct SystemRT <: System - gens_per_bus::Dict{String, Vector{Int}} - loads_per_bus::Dict{String, Vector{String}} + "`Dict` where the keys are bus names and the values are generator ids at that bus" + gens_per_bus::Dict{InlineString15, Vector{Int}} + "`Dict` where the keys are bus names and the values are load ids at that bus" + loads_per_bus::Dict{InlineString15, Vector{String}} + "Zones in the `System`, which will also include a `Zone` entry for the market wide zone" zones::Vector{Zone} buses::Buses generators::Generators branches::Branches - LODF::Dict{String, KeyedArray} - PTDF::KeyedArray + """ + The line outage distribution factor matrix of the system for a set of contingencies given + by the keys of the `Dict`. Each entry is a `KeyedArray` with axis keys + `branch names x branch on outage` + """ + LODF::Dict{String, KeyedArray{Float64, 2}} + """ + Power transfer distribution factor of the system. `KeyedArray` where the axis keys are + `branch names x bus names` + """ + PTDF::KeyedArray{Float64, 2} # Generator related time series - offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}} - status::KeyedArray{Bool} - status_regulation::KeyedArray{Bool} - regulation_min::KeyedArray{Float64} - regulation_max::KeyedArray{Float64} - pmin::KeyedArray{Float64} - pmax::KeyedArray{Float64} + "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 ancillary service status indicated by a `Bool`" + status_regulation::KeyedArray{Bool, 2} + "Generator minimum output in the ancillary services market (MWs)" + regulation_min::KeyedArray{Float64, 2} + "Generator maximum output in the ancillary services market (MWs)" + regulation_max::KeyedArray{Float64, 2} + "Generator minimum output (MWs)" + pmin::KeyedArray{Float64, 2} + "Generator maximum output (MWs)" + pmax::KeyedArray{Float64, 2} + "Time series data for ancillary services provided by generators" ancillary_services::ServicesTimeSeries # Load time series - loads::KeyedArray{Float64} + "Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" + loads::KeyedArray{Float64, 2} end function Base.show(io::IO, ::MIME"text/plain", system::T) where {T <: System} @@ -298,5 +326,6 @@ end 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) end diff --git a/test/system.jl b/test/system.jl index d6803f3..337e716 100644 --- a/test/system.jl +++ b/test/system.jl @@ -78,9 +78,9 @@ LODF = Dict( "CONTIN_1" => KeyedArray(rand(3, 1); - buses=bus_names, branch=[first(branch_names)]) + branches=branch_names, branch=[first(branch_names)]) ) - PTDF = KeyedArray(rand(3, 3); row=bus_names, col=bus_names) + PTDF = KeyedArray(rand(3, 3); row=branch_names, col=bus_names) da_system = SystemDA( gens_per_bus, From 01337b8694479e99590f06ba5af1a197cc03be6f Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Mon, 25 Apr 2022 12:08:43 +0100 Subject: [PATCH 09/14] ServicesTimeSeries -> asm time series data in System --- src/FullNetworkSystems.jl | 2 +- src/system.jl | 40 ++++++++++++++++----------------------- test/system.jl | 13 ++++++++----- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index b00776e..f32147d 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -6,7 +6,7 @@ using DocStringExtensions using InlineStrings export System, SystemDA, SystemRT -export ServicesTimeSeries, Zone, StaticComponent, Generators, Buses, Branches +export Zone, StaticComponent, Generators, Buses, Branches export gens_per_zone, branches_by_breakpoints, get_datetimes include("system.jl") diff --git a/src/system.jl b/src/system.jl index a9639e2..a6c863c 100644 --- a/src/system.jl +++ b/src/system.jl @@ -1,23 +1,3 @@ -""" - $TYPEDEF - -Type defining ancillary services time series. Fields are `KeyedArray` where the keys are -`generator names x datetimes`. Dollar symbol \$. - -Fields: -$TYPEDFIELDS -""" -struct ServicesTimeSeries - "Regulation offer prices (\$ /MW)" - reg::KeyedArray{Float64, 2} - "Spinning offer prices (\$ /MW)" - spin::KeyedArray{Float64, 2} - "Supplemental on offer prices (\$ /MW)" - sup_on::KeyedArray{Float64, 2} - "Supplemental off offer prices (\$ /MW)" - sup_off::KeyedArray{Float64, 2} -end - """ $TYPEDEF @@ -229,8 +209,14 @@ struct SystemDA <: System pmin::KeyedArray{Float64, 2} "Generator maximum output (MWs)" pmax::KeyedArray{Float64, 2} - "Time series data for ancillary services provided by generators" - ancillary_services::ServicesTimeSeries + "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} # Load time series "Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" @@ -291,8 +277,14 @@ struct SystemRT <: System pmin::KeyedArray{Float64, 2} "Generator maximum output (MWs)" pmax::KeyedArray{Float64, 2} - "Time series data for ancillary services provided by generators" - ancillary_services::ServicesTimeSeries + "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} # Load time series "Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" diff --git a/test/system.jl b/test/system.jl index 337e716..e2a18ac 100644 --- a/test/system.jl +++ b/test/system.jl @@ -13,9 +13,6 @@ bus_names = ["A", "B", "C"] @testset "System" begin - sts = ServicesTimeSeries(fake_gen_ts, fake_gen_ts, fake_gen_ts, fake_gen_ts) - @test sts isa ServicesTimeSeries - zone1 = Zone(1, 1.0, 1.0, 1.0, 1.0) zone2 = Zone(1, 4.0, 2.0, 4.0, 2.0) zone_market = Zone(-9999, 3.0, 3.0, 3.0, 3.0) @@ -101,7 +98,10 @@ fake_gen_ts, fake_gen_ts, fake_gen_ts, - sts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, fake_gen_ts, fake_offer_ts, fake_offer_ts, @@ -127,7 +127,10 @@ fake_gen_ts, fake_gen_ts, fake_gen_ts, - sts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, + fake_gen_ts, fake_gen_ts ) From 0874024feea912763fc5ddb24a07bcc91ccc4632 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Wed, 27 Apr 2022 11:52:58 +0100 Subject: [PATCH 10/14] convert static comps to rows, use Dictionaries as collections --- Project.toml | 2 + src/FullNetworkSystems.jl | 3 +- src/system.jl | 188 ++++++++++++++++++++------------------ test/runtests.jl | 1 + test/system.jl | 94 +++++++++---------- 5 files changed, 150 insertions(+), 138 deletions(-) diff --git a/Project.toml b/Project.toml index e14dc1d..0337056 100644 --- a/Project.toml +++ b/Project.toml @@ -6,11 +6,13 @@ version = "1.0.0" [deps] AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" InlineStrings = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" [compat] AxisKeys = "0.2" +Dictionaries = "0.3" DocStringExtensions = "0.8" InlineStrings = "1" julia = "1.6" diff --git a/src/FullNetworkSystems.jl b/src/FullNetworkSystems.jl index f32147d..01d8241 100644 --- a/src/FullNetworkSystems.jl +++ b/src/FullNetworkSystems.jl @@ -2,11 +2,12 @@ module FullNetworkSystems using AxisKeys using Dates +using Dictionaries using DocStringExtensions using InlineStrings export System, SystemDA, SystemRT -export Zone, StaticComponent, Generators, Buses, Branches +export Zone, Generator, Bus, Branch export gens_per_zone, branches_by_breakpoints, get_datetimes include("system.jl") diff --git a/src/system.jl b/src/system.jl index a6c863c..ab2f31d 100644 --- a/src/system.jl +++ b/src/system.jl @@ -22,126 +22,82 @@ end ###### Static Component Types ###### -abstract type StaticComponent end - -Base.length(components::StaticComponent) = length(getfield(components, 1)) -# define interfaces? Iterator? Table? - """ $TYPEDEF -Type for static generator component attributes (i.e. things that describe a generator that -are not time series data). +Type for static generator attribute (i.e. things that describe a generator that are not time +series data). Fields: $TYPEDFIELDS """ -struct Generators <: StaticComponent - "Generator ids/unit codes" - name::Vector{Int} +struct Generator + "Generator id/unit code" + unit_code::Int "Number of the zone the generator is located in" - zone::Vector{Int} + zone::Int "Cost of turning on the generator (\$)" - startup_cost::Vector{Float64} + startup_cost::Float64 "Cost of turning off the generator (\$)" - shutdown_cost::Vector{Float64} + shutdown_cost::Float64 "Cost of the generator being on but not producing any MW (\$ /hour)" - no_load_cost::Vector{Float64} - "Hours each generator has been at its current status at the start of the day" - hours_at_status::Vector{Float64} - "Minimum time a generator has to be committed for (hours)" - min_uptime::Vector{Float64} - "Minimum time a generator has to be off for (hours)" - min_downtime::Vector{Float64} - "Rate at which a generator can increase generation (MW/minute)" - ramp_up::Vector{Float64} - "Rate at which a generator can decrease generation (MW/minute)" - ramp_down::Vector{Float64} - "Generation of generators at the start of the day (MWs)" - initial_gen::Vector{Float64} # this one changes in RT with _update_system_generation - but is that necessary - could be a mutable time series? - "Symbol describing the technology of a generator" - technology::Vector{Symbol} -end - -""" - gens_per_zone(gens::Generators) - -Returns a `Dict` with keys of `Zone` numbers and values of generator names in that zone. -""" -function gens_per_zone(gens::Generators) - gens_per_zone = Dict{Int, Vector{Int}}() - for (name, zone) in zip(gens.name, gens.zone) - if haskey(gens_per_zone, zone) - push!(gens_per_zone[zone], name) - else - gens_per_zone[zone] = [name] - end - end - return gens_per_zone + no_load_cost::Float64 + "Minimum time the generator has to be committed for (hours)" + min_uptime::Float64 + "Minimum time the generator has to be off for (hours)" + min_downtime::Float64 + "Rate at which the generator can increase generation (MW/minute)" + ramp_up::Float64 + "Rate at which the generator can decrease generation (MW/minute)" + ramp_down::Float64 + "Symbol describing the technology of the generator" + technology::Symbol end """ $TYPEDEF -Type for static bus component attributes. +Type for static bus attributes. Fields: $TYPEDFIELDS """ -struct Buses <: StaticComponent +struct Bus "Bus name" - name::Vector{InlineString15} - "Base volatge (kV)" - base_voltage::Vector{Float64} + name::InlineString15 + "Base voltage (kV)" + base_voltage::Float64 end """ $TYPEDEF -Type for static branch component attributes. Branches may have between 0 and 2 break points +Type for static branch attributes. Branches may have between 0 and 2 break points which is why the `break_points` and `penalties` fields contain variable length `Tuple`s. Fields: $TYPEDFIELDS """ -struct Branches <: StaticComponent +struct Branch "Branch long name" - name::Vector{InlineString31} + name::InlineString31 "Name of the bus the branch goes to" - to_bus::Vector{InlineString15} - "Name of the bus the branche goes from" - from_bus::Vector{InlineString15} + to_bus::InlineString15 + "Name of the bus the branch goes from" + from_bus::InlineString15 "Power flow limit for the base case (MVA)" - rate_a::Vector{Float64} + rate_a::Float64 "Power flow limit for contingency scenario (MVA)" - rate_b::Vector{Float64} + rate_b::Float64 "Boolean defining whether the branch is monitored" - is_monitored::Vector{Bool} - "Break points of the branch. Branches can have 0, 1, or 2 break points" - break_points::Vector{Tuple{Vararg{Float64}}} # variable length (0, 1, 2) + is_monitored::Bool + """ + Break points of the branch. Branches can have 0, 1, or 2 break points. Zeros indicate + no break point + """ + break_points::Tuple{Float64, Float64} "Price penalties for each of the break points of the branch (\$)" - penalties::Vector{Tuple{Vararg{Float64}}} # length corresponding to number of break points -end - -""" - branches_by_breakpoints(branches::Branches) - -Returns three vectors containing of the names of branches which have 0, 1, and 2 breakpoints. -""" -function branches_by_breakpoints(branches::Branches) - zero_bp, one_bp, two_bp = String[], String[], String[] - for (name, breaks, mon) in zip(branches.name, branches.break_points, branches.is_monitored) - if mon - if length(breaks) == 0 - push!(zero_bp, name) - elseif length(breaks) == 1 - push!(one_bp, name) - else - push!(two_bp, name) - end - end - end - return zero_bp, one_bp, two_bp + penalties::Tuple{Float64, Float64} end """ @@ -178,10 +134,13 @@ struct SystemDA <: System loads_per_bus::Dict{InlineString15, Vector{String}} "Zones in the `System`, which will also include a `Zone` entry for the market wide zone" - zones::Vector{Zone} - buses::Buses - generators::Generators - branches::Branches + zones::Dictionary{Int, Zone} + "Buses in the `System` indexed by bus name" + buses::Dictionary{InlineString15, Bus} + "Generators in the `System` indexed by unit code" + generators::Dictionary{Int, Generator} + "Branches in the `System` indexed by branch name" + branches::Dictionary{InlineString31, Branch} """ The line outage distribution factor matrix of the system for a set of contingencies given by the keys of the `Dict`. Each entry is a `KeyedArray` with axis keys @@ -195,6 +154,10 @@ struct SystemDA <: System PTDF::KeyedArray{Float64, 2} # Generator related time series + "Generation of the generators at the start of the day (MWs)" + 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" @@ -246,10 +209,13 @@ struct SystemRT <: System loads_per_bus::Dict{InlineString15, Vector{String}} "Zones in the `System`, which will also include a `Zone` entry for the market wide zone" - zones::Vector{Zone} - buses::Buses - generators::Generators - branches::Branches + zones::Dictionary{Int, Zone} + "Buses in the `System` indexed by bus name" + buses::Dictionary{InlineString15, Bus} + "Generators in the `System` indexed by unit code" + generators::Dictionary{Int, Generator} + "Branches in the `System` indexed by branch name" + branches::Dictionary{InlineString31, Branch} """ The line outage distribution factor matrix of the system for a set of contingencies given by the keys of the `Dict`. Each entry is a `KeyedArray` with axis keys @@ -263,6 +229,8 @@ struct SystemRT <: System PTDF::KeyedArray{Float64, 2} # Generator related time series + "Generation of the generator at the start of the time period (MWs)" + 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`" @@ -321,3 +289,41 @@ function get_datetimes(system::System) # use offer_curve axiskeys because all subtypes of System have offer_curve return axiskeys(system.offer_curve, 2) end + +""" + gens_per_zone(system::System) + +Returns a `Dict` with keys of `Zone` numbers and values of generator names in that zone. +""" +function gens_per_zone(system::System) + gens_per_zone = Dict{Int, Vector{Int}}() + for gen in system.generators + if haskey(gens_per_zone, gen.zone) + push!(gens_per_zone[gen.zone], gen.unit_code) + else + gens_per_zone[gen.zone] = [gen.unit_code] + end + end + return gens_per_zone +end + +""" + branches_by_breakpoints(system::System) + +Returns three vectors containing of the names of branches which have 0, 1, and 2 breakpoints. +""" +function branches_by_breakpoints(system::System) + zero_bp, one_bp, two_bp = String[], String[], String[] + for branch in system.branches + if branch.is_monitored + if all(branch.break_points .== 0.0) + push!(zero_bp, branch.name) + elseif last(branch.break_points) == 0.0 + push!(one_bp, branch.name) + else + push!(two_bp, branch.name) + end + end + end + return zero_bp, one_bp, two_bp +end diff --git a/test/runtests.jl b/test/runtests.jl index aa56f6f..0973bdb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,6 @@ using AxisKeys using Dates +using Dictionaries using FullNetworkSystems using Test diff --git a/test/system.jl b/test/system.jl index e2a18ac..68e1784 100644 --- a/test/system.jl +++ b/test/system.jl @@ -2,6 +2,7 @@ datetimes = DateTime(2017, 12, 15):Hour(1):DateTime(2017, 12, 15, 23) gen_ids = collect(111:1:120) l = length(gen_ids) + fake_vec_ts = KeyedArray(rand(10); ids=gen_ids) fake_gen_ts = KeyedArray(rand(10, 24); ids=gen_ids, datetimes=datetimes) fake_offer_ts = KeyedArray( repeat([[(1.0, 100.0)]], inner=(1, 24), outer=(10, 1)); @@ -12,60 +13,50 @@ branch_names = string.([1, 2, 3]) bus_names = ["A", "B", "C"] + @testset "Zone" begin + zone1 = Zone(1, 1.0, 1.0, 1.0, 1.0) + @test zone1 isa Zone + end + + @testset "Generator" begin + gen1 = Generator(111, 1, 0.0, 1.0, 1.0, 24.0, 24.0, 2.0, 2.0, :tech) + @test gen1 isa Generator + end + + @testset "Bus" begin + bus1 = Bus("A", 100.0) + @test bus1 isa Bus + end + + @testset "Branch" begin + branch1 = Branch("1", "A", "C", 10.0, 10.0, true, (100.0, 102.0), (5.0, 6.0)) + @test branch1 isa Branch + end + @testset "System" begin zone1 = Zone(1, 1.0, 1.0, 1.0, 1.0) - zone2 = Zone(1, 4.0, 2.0, 4.0, 2.0) + zone2 = Zone(2, 4.0, 2.0, 4.0, 2.0) zone_market = Zone(-9999, 3.0, 3.0, 3.0, 3.0) - @testset "Zone" begin - @test zone1 isa Zone - end + zones = Dictionary([1, 2, -9999], [zone1, zone2, zone_market]) - gens = Generators( - gen_ids, - fill(zone1.number, l), # zone - fill(0.0, l), # start_up_cost - fill(1.0, l), # shut_down_cost - fill(1.0, l), # no_load_cost - fill(24.0, l), # time_at_status - fill(24.0, l), # min_uptime - fill(24.0, l), # min_downtime - fill(2.0, l), # ramp_up - fill(2.0, l), # ramp_down - fill(100.0, l), # initial_gen - fill(:tech, l) - ) - expected_gens_zones = Dict(1 => gen_ids) - @testset "Generators" begin - @test gens isa Generators - @test length(gens) == l - @test gens_per_zone(gens) == expected_gens_zones + gen_types = map(gen_ids) do id + Generator(id, zone1.number, 0.0, 1.0, 1.0, 24.0, 24.0, 2.0, 2.0, :tech) end + gens = Dictionary(gen_ids, gen_types) - buses = Buses(bus_names, rand(length(bus_names))) - @testset "Buses" begin - @test buses isa Buses - @test length(buses) == length(bus_names) + bus_types = map(bus_names) do name + Bus(name, 100.0) end + buses = Dictionary(bus_names, bus_types) - branches = Branches( + branches = Dictionary( branch_names, - bus_names, - reverse(bus_names), - rand(3), - rand(3), - [true, true, false], - [(100.0, 102.0), (100.0,), ()], - [(5.0, 6.0), (5.0,), ()] + [ + Branch("1", "A", "B", 10.0, 10.0, true, (100.0, 102.0), (5.0, 6.0)), + Branch("2", "B", "C", 10.0, 10.0, false, (100.0, 0.0), (5.0, 0.0)), + Branch("3", "C", "A", 10.0, 10.0, true, (0.0, 0.0), (0.0, 0.0)), + ] ) - @testset "Branches" begin - @test branches isa Branches - @test length(branches) == length(branch_names) - - zero_bp, one_bp, two_bp = branches_by_breakpoints(branches) - @test zero_bp == String[] # unmonitored - @test one_bp == ["2"] - @test two_bp == ["1"] - end gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_names) incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) @@ -85,12 +76,14 @@ decs_per_bus, psds_per_bus, loads_per_bus, - [zone1, zone2, zone_market], + zones, buses, gens, branches, LODF, PTDF, + fake_vec_ts, + fake_vec_ts, fake_offer_ts, fake_bool_ts, fake_bool_ts, @@ -111,15 +104,24 @@ @test da_system isa SystemDA @test get_datetimes(da_system) == datetimes + expected_gens_zones = Dict(1 => gen_ids) + @test gens_per_zone(da_system) == expected_gens_zones + + zero_bp, one_bp, two_bp = branches_by_breakpoints(da_system) + @test zero_bp == ["3"] + @test one_bp == String[] #unmonitored + @test two_bp == ["1"] + rt_system = SystemRT( gens_per_bus, loads_per_bus, - [zone1, zone2, zone_market], + zones, buses, gens, branches, LODF, PTDF, + fake_vec_ts, fake_offer_ts, fake_bool_ts, fake_bool_ts, From 4e7c986f538f4abbb29189f014193e51e0880cf6 Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Thu, 28 Apr 2022 10:39:56 +0100 Subject: [PATCH 11/14] type alias for inlinestrings and use Dictionaries in more places --- src/system.jl | 84 ++++++++++++++++++++++++++------------------------ test/system.jl | 18 +++++------ 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/src/system.jl b/src/system.jl index ab2f31d..21fa2e7 100644 --- a/src/system.jl +++ b/src/system.jl @@ -10,17 +10,19 @@ $TYPEDFIELDS struct Zone "Zone number" number::Int64 - "Zonal regulation requirement (MWs)" + "Zonal regulation requirement (MW)" reg::Float64 - "Zonal spinning requirement (MWs)" + "Zonal spinning requirement (MW)" spin::Float64 - "Zonal supplemental on requirement (MWs)" + "Zonal supplemental on requirement (MW)" sup_on::Float64 - "Zonal supplemental off requirement (MWs)" + "Zonal supplemental off requirement (MW)" sup_off::Float64 end ###### Static Component Types ###### +const BusName = InlineString15 +const BranchName = InlineString31 """ $TYPEDEF @@ -64,7 +66,7 @@ $TYPEDFIELDS """ struct Bus "Bus name" - name::InlineString15 + name::BusName "Base voltage (kV)" base_voltage::Float64 end @@ -80,11 +82,11 @@ $TYPEDFIELDS """ struct Branch "Branch long name" - name::InlineString31 + name::BranchName "Name of the bus the branch goes to" - to_bus::InlineString15 + to_bus::BusName "Name of the bus the branch goes from" - from_bus::InlineString15 + from_bus::BusName "Power flow limit for the base case (MVA)" rate_a::Float64 "Power flow limit for contingency scenario (MVA)" @@ -106,7 +108,7 @@ end The abstract type for representing the whole power system including topology, static components and their attributes, and time series data. -Topology: `Dict`s linking generators, loads, and bids (if present) to buses. +Topology: `Dictionary`s linking generators, loads, and bids (if present) to buses. System wide static components and grid matrices: zones, buses, generators, branches, LODF and PTDF. Time series data: all the time series associated with generators, loads and bids. All stored as `KeyedArray`s of `ids x datetimes`. @@ -122,31 +124,31 @@ Fields: $TYPEDFIELDS """ struct SystemDA <: System - "`Dict` where the keys are bus names and the values are generator ids at that bus" - gens_per_bus::Dict{InlineString15, Vector{Int}} - "`Dict` where the keys are bus names and the values are increment bid ids at that bus" - incs_per_bus::Dict{InlineString15, Vector{String}} - "`Dict` where the keys are bus names and the values are decrement bid ids at that bus" - decs_per_bus::Dict{InlineString15, Vector{String}} - "`Dict` where the keys are bus names and the values are price sensitive demand ids at that bus" - psds_per_bus::Dict{InlineString15, Vector{String}} - "`Dict` where the keys are bus names and the values are load ids at that bus" - loads_per_bus::Dict{InlineString15, Vector{String}} + "`Dictionary` where the keys are bus names and the values are generator ids at that bus" + gens_per_bus::Dictionary{BusName, Vector{Int}} + "`Dictionary` where the keys are bus names and the values are increment bid ids at that bus" + incs_per_bus::Dictionary{BusName, Vector{String}} + "`Dictionary` where the keys are bus names and the values are decrement bid ids at that bus" + decs_per_bus::Dictionary{BusName, Vector{String}} + "`Dictionary` where the keys are bus names and the values are price sensitive demand ids at that bus" + psds_per_bus::Dictionary{BusName, Vector{String}} + "`Dictionary` where the keys are bus names and the values are load ids at that bus" + loads_per_bus::Dictionary{BusName, Vector{String}} "Zones in the `System`, which will also include a `Zone` entry for the market wide zone" zones::Dictionary{Int, Zone} "Buses in the `System` indexed by bus name" - buses::Dictionary{InlineString15, Bus} + buses::Dictionary{BusName, Bus} "Generators in the `System` indexed by unit code" generators::Dictionary{Int, Generator} "Branches in the `System` indexed by branch name" - branches::Dictionary{InlineString31, Branch} + branches::Dictionary{BranchName, Branch} """ The line outage distribution factor matrix of the system for a set of contingencies given - by the keys of the `Dict`. Each entry is a `KeyedArray` with axis keys + by the keys of the `Dictionary`. Each entry is a `KeyedArray` with axis keys `branch names x branch on outage` """ - LODF::Dict{String, KeyedArray{Float64, 2}} + LODF::Dictionary{String, KeyedArray{Float64, 2}} """ Power transfer distribution factor of the system. `KeyedArray` where the axis keys are `branch names x bus names` @@ -154,7 +156,7 @@ struct SystemDA <: System PTDF::KeyedArray{Float64, 2} # Generator related time series - "Generation of the generators at the start of the day (MWs)" + "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} @@ -164,13 +166,13 @@ struct SystemDA <: System 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 (MWs)" + "Generator minimum output in the ancillary services market (MW)" regulation_min::KeyedArray{Float64, 2} - "Generator maximum output in the ancillary services market (MWs)" + "Generator maximum output in the ancillary services market (MW)" regulation_max::KeyedArray{Float64, 2} - "Generator minimum output (MWs)" + "Generator minimum output (MW)" pmin::KeyedArray{Float64, 2} - "Generator maximum output (MWs)" + "Generator maximum output (MW)" pmax::KeyedArray{Float64, 2} "Ancillary services regulation offer prices (\$ /MW)" asm_regulation::KeyedArray{Float64, 2} @@ -203,25 +205,25 @@ Fields: $TYPEDFIELDS """ struct SystemRT <: System - "`Dict` where the keys are bus names and the values are generator ids at that bus" - gens_per_bus::Dict{InlineString15, Vector{Int}} - "`Dict` where the keys are bus names and the values are load ids at that bus" - loads_per_bus::Dict{InlineString15, Vector{String}} + "`Dictionary` where the keys are bus names and the values are generator ids at that bus" + gens_per_bus::Dictionary{BusName, Vector{Int}} + "`Dictionary` where the keys are bus names and the values are load ids at that bus" + loads_per_bus::Dictionary{BusName, Vector{String}} "Zones in the `System`, which will also include a `Zone` entry for the market wide zone" zones::Dictionary{Int, Zone} "Buses in the `System` indexed by bus name" - buses::Dictionary{InlineString15, Bus} + buses::Dictionary{BusName, Bus} "Generators in the `System` indexed by unit code" generators::Dictionary{Int, Generator} "Branches in the `System` indexed by branch name" - branches::Dictionary{InlineString31, Branch} + branches::Dictionary{BranchName, Branch} """ The line outage distribution factor matrix of the system for a set of contingencies given - by the keys of the `Dict`. Each entry is a `KeyedArray` with axis keys + by the keys of the `Dictionary`. Each entry is a `KeyedArray` with axis keys `branch names x branch on outage` """ - LODF::Dict{String, KeyedArray{Float64, 2}} + LODF::Dictionary{String, KeyedArray{Float64, 2}} """ Power transfer distribution factor of the system. `KeyedArray` where the axis keys are `branch names x bus names` @@ -229,7 +231,7 @@ struct SystemRT <: System PTDF::KeyedArray{Float64, 2} # Generator related time series - "Generation of the generator at the start of the time period (MWs)" + "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} @@ -237,13 +239,13 @@ struct SystemRT <: System status::KeyedArray{Bool, 2} "Generator ancillary service status indicated by a `Bool`" status_regulation::KeyedArray{Bool, 2} - "Generator minimum output in the ancillary services market (MWs)" + "Generator minimum output in the ancillary services market (MW)" regulation_min::KeyedArray{Float64, 2} - "Generator maximum output in the ancillary services market (MWs)" + "Generator maximum output in the ancillary services market (MW)" regulation_max::KeyedArray{Float64, 2} - "Generator minimum output (MWs)" + "Generator minimum output (MW)" pmin::KeyedArray{Float64, 2} - "Generator maximum output (MWs)" + "Generator maximum output (MW)" pmax::KeyedArray{Float64, 2} "Ancillary services regulation offer prices (\$ /MW)" asm_regulation::KeyedArray{Float64, 2} diff --git a/test/system.jl b/test/system.jl index 68e1784..8c1d0a7 100644 --- a/test/system.jl +++ b/test/system.jl @@ -58,15 +58,15 @@ ] ) - gens_per_bus = Dict(b => rand(gen_ids, 3) for b in bus_names) - incs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) - decs_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) - psds_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) - loads_per_bus = Dict(b => string.(rand('A':'Z', 3)) for b in bus_names) - - LODF = Dict( - "CONTIN_1" => KeyedArray(rand(3, 1); - branches=branch_names, branch=[first(branch_names)]) + gens_per_bus = Dictionary(bus_names, rand(gen_ids, 3) for _ in bus_names) + incs_per_bus = Dictionary(bus_names, string.(rand('A':'Z', 3)) for _ in bus_names) + decs_per_bus = Dictionary(bus_names, string.(rand('A':'Z', 3)) for _ in bus_names) + psds_per_bus = Dictionary(bus_names, string.(rand('A':'Z', 3)) for _ in bus_names) + loads_per_bus = Dictionary(bus_names, string.(rand('A':'Z', 3)) for _ in bus_names) + + LODF = Dictionary( + ["CONTIN_1"], + [KeyedArray(rand(3, 1); branches=branch_names, branch=[first(branch_names)])] ) PTDF = KeyedArray(rand(3, 3); row=branch_names, col=bus_names) From 0080a6b59d948f84ace8d918206fbbdd1ef5637d Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Thu, 28 Apr 2022 10:40:28 +0100 Subject: [PATCH 12/14] -DEV version --- Project.toml | 2 +- src/system.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 0337056..6aed9fb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FullNetworkSystems" uuid = "877b7152-b508-43dc-81fb-72341a693988" authors = ["Invenia Technical Computing Corporation"] -version = "1.0.0" +version = "1.0.0-DEV" [deps] AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" diff --git a/src/system.jl b/src/system.jl index 21fa2e7..2deb498 100644 --- a/src/system.jl +++ b/src/system.jl @@ -108,7 +108,7 @@ end The abstract type for representing the whole power system including topology, static components and their attributes, and time series data. -Topology: `Dictionary`s linking generators, loads, and bids (if present) to buses. +Topology: `Dictionaries` linking generators, loads, and bids (if present) to buses. System wide static components and grid matrices: zones, buses, generators, branches, LODF and PTDF. Time series data: all the time series associated with generators, loads and bids. All stored as `KeyedArray`s of `ids x datetimes`. From 5160f7c508760005d72f88c862e24fff8954ddac Mon Sep 17 00:00:00 2001 From: Branwen Snelling Date: Thu, 28 Apr 2022 10:46:33 +0100 Subject: [PATCH 13/14] rename psds/bid fields --- src/system.jl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/system.jl b/src/system.jl index 2deb498..bd9bc73 100644 --- a/src/system.jl +++ b/src/system.jl @@ -130,7 +130,10 @@ struct SystemDA <: System incs_per_bus::Dictionary{BusName, Vector{String}} "`Dictionary` where the keys are bus names and the values are decrement bid ids at that bus" decs_per_bus::Dictionary{BusName, Vector{String}} - "`Dictionary` where the keys are bus names and the values are price sensitive demand ids at that bus" + """ + `Dictionary` where the keys are bus names and the values are price sensitive demand bid + ids at that bus + """ psds_per_bus::Dictionary{BusName, Vector{String}} "`Dictionary` where the keys are bus names and the values are load ids at that bus" loads_per_bus::Dictionary{BusName, Vector{String}} @@ -189,10 +192,10 @@ struct SystemDA <: System # Virtuals/PSD time series "Increment bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" - increment_bids::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} + increment::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} "Decrement bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" - decrement_bids::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} - "Price sensitive demand time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" + decrement::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} + "Price sensitive demand bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" price_sensitive_demand::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} end From e874bc0e5b234918fb6b1310cb7f6215988d2c4f Mon Sep 17 00:00:00 2001 From: BSnelling Date: Thu, 28 Apr 2022 10:51:44 +0100 Subject: [PATCH 14/14] Update src/system.jl Co-authored-by: Raphael Saavedra --- src/system.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system.jl b/src/system.jl index bd9bc73..11248c3 100644 --- a/src/system.jl +++ b/src/system.jl @@ -240,7 +240,7 @@ struct SystemRT <: System offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} "Generator status indicated by a `Bool`" status::KeyedArray{Bool, 2} - "Generator ancillary service status indicated by a `Bool`" + "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}