diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 00000000..c53d5a05 --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1,7 @@ +whitespace_ops_in_indices=true +remove_extra_newlines=true +always_for_in=true +whitespace_typedefs=true +whitespace_in_kwargs=false +format_docstrings=true +always_use_return=false diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml new file mode 100644 index 00000000..cf2bed07 --- /dev/null +++ b/.github/workflows/format-check.yml @@ -0,0 +1,43 @@ +name: Format Check + +on: + push: + branches: + - 'main' + - 'release-' + tags: '*' + pull_request: + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + julia-version: [1] + julia-arch: [x86] + os: [ubuntu-latest] + steps: + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.julia-version }} + + - uses: actions/checkout@v2 + - name: Install JuliaFormatter and format + run: | + julia -e 'include("scripts/formatter/formatter_code.jl")' + - uses: reviewdog/action-suggester@v1 + if: github.event_name == 'pull_request' + with: + tool_name: JuliaFormatter + fail_on_error: true + - name: Format check + run: | + julia -e ' + out = Cmd(`git diff --name-only`) |> read |> String + if out == "" + exit(0) + else + @error "Some files have not been formatted !!!" + write(stdout, out) + exit(1) + end' diff --git a/scripts/formatter/Project.toml b/scripts/formatter/Project.toml new file mode 100644 index 00000000..a33f747b --- /dev/null +++ b/scripts/formatter/Project.toml @@ -0,0 +1,10 @@ +uuid = "c6367ca8-164d-4469-afe3-c91cf8860505" +authors = ["Jose Daniel Lara "] + +[deps] +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[compat] +JuliaFormatter = "1.0" +julia = "^1.7" diff --git a/scripts/formatter/formatter_code.jl b/scripts/formatter/formatter_code.jl new file mode 100644 index 00000000..91df6835 --- /dev/null +++ b/scripts/formatter/formatter_code.jl @@ -0,0 +1,41 @@ +using Pkg +Pkg.activate(@__DIR__) +Pkg.instantiate() +using JuliaFormatter + +main_paths = ["."] #, "./docs/src"] (until we use Documenter.jl) +for main_path in main_paths + format( + main_path; + whitespace_ops_in_indices=true, + remove_extra_newlines=true, + verbose=true, + always_for_in=true, + whitespace_typedefs=true, + whitespace_in_kwargs=false, + format_docstrings=true, + always_use_return=false, # removed since it has false positives. + ) +end + +# Documentation Formatter +main_paths = [] # "./docs/src"] +for main_path in main_paths + for folder in readdir(main_path) + @show folder_path = joinpath(main_path, folder) + if isfile(folder_path) + !occursin(".md", folder_path) && continue + end + format( + folder_path; + format_markdown=true, + whitespace_ops_in_indices=true, + remove_extra_newlines=true, + verbose=true, + always_for_in=true, + whitespace_typedefs=true, + whitespace_in_kwargs=false, + # always_use_return = true # removed since it has false positives. + ) + end +end diff --git a/src/CapacityCredit/CapacityCredit.jl b/src/CapacityCredit/CapacityCredit.jl index 49426458..0fd2d8d7 100644 --- a/src/CapacityCredit/CapacityCredit.jl +++ b/src/CapacityCredit/CapacityCredit.jl @@ -3,12 +3,12 @@ import Base: minimum, maximum, extrema import Distributions: ccdf, Normal import ..PRASBase: Generators, PowerUnit, Regions, SystemModel, unitsymbol -import ..ResourceAdequacy: assess, ReliabilityMetric, Result, Shortfall, - SimulationSpec, stderror, val +import ..ResourceAdequacy: + assess, ReliabilityMetric, Result, Shortfall, SimulationSpec, stderror, val export EFC, ELCC -abstract type CapacityValuationMethod{M<:ReliabilityMetric} end +abstract type CapacityValuationMethod{M <: ReliabilityMetric} end include("utils.jl") include("CapacityCreditResult.jl") diff --git a/src/CapacityCredit/CapacityCreditResult.jl b/src/CapacityCredit/CapacityCreditResult.jl index aeeec9b3..2f42cd29 100644 --- a/src/CapacityCredit/CapacityCreditResult.jl +++ b/src/CapacityCredit/CapacityCreditResult.jl @@ -1,24 +1,26 @@ struct CapacityCreditResult{ - S <: CapacityValuationMethod, M <: ReliabilityMetric, P <: PowerUnit} - + S <: CapacityValuationMethod, + M <: ReliabilityMetric, + P <: PowerUnit, +} target_metric::M lowerbound::Int upperbound::Int bound_capacities::Vector{Int} bound_metrics::Vector{M} - function CapacityCreditResult{S,M,P}( - target_metric::M, lowerbound::Int, upperbound::Int, - bound_capacities::Vector{Int}, bound_metrics::Vector{M}) where {S,M,P} - + function CapacityCreditResult{S, M, P}( + target_metric::M, + lowerbound::Int, + upperbound::Int, + bound_capacities::Vector{Int}, + bound_metrics::Vector{M}, + ) where {S, M, P} length(bound_capacities) == length(bound_metrics) || throw(ArgumentError("Lengths of bound_capacities and bound_metrics must match")) - new{S,M,P}(target_metric, lowerbound, upperbound, - bound_capacities, bound_metrics) - + new{S, M, P}(target_metric, lowerbound, upperbound, bound_capacities, bound_metrics) end - end minimum(x::CapacityCreditResult) = x.lowerbound diff --git a/src/CapacityCredit/EFC.jl b/src/CapacityCredit/EFC.jl index 2ff9f12d..0676ba35 100644 --- a/src/CapacityCredit/EFC.jl +++ b/src/CapacityCredit/EFC.jl @@ -1,36 +1,36 @@ struct EFC{M} <: CapacityValuationMethod{M} - capacity_max::Int capacity_gap::Int p_value::Float64 - regions::Vector{Tuple{String,Float64}} + regions::Vector{Tuple{String, Float64}} verbose::Bool function EFC{M}( - capacity_max::Int, regions::Vector{Pair{String,Float64}}; - capacity_gap::Int=1, p_value::Float64=0.05, verbose::Bool=false) where M - + capacity_max::Int, + regions::Vector{Pair{String, Float64}}; + capacity_gap::Int=1, + p_value::Float64=0.05, + verbose::Bool=false, + ) where {M} @assert capacity_max > 0 @assert capacity_gap > 0 @assert 0 < p_value < 1 @assert sum(x.second for x in regions) ≈ 1.0 return new{M}(capacity_max, capacity_gap, p_value, Tuple.(regions), verbose) - end - end -function EFC{M}( - capacity_max::Int, region::String; kwargs... -) where M - return EFC{M}(capacity_max, [region=>1.0]; kwargs...) +function EFC{M}(capacity_max::Int, region::String; kwargs...) where {M} + return EFC{M}(capacity_max, [region => 1.0]; kwargs...) end -function assess(sys_baseline::S, sys_augmented::S, - params::EFC{M}, simulationspec::SimulationSpec -) where {N, L, T, P, S <: SystemModel{N,L,T,P}, M <: ReliabilityMetric} - +function assess( + sys_baseline::S, + sys_augmented::S, + params::EFC{M}, + simulationspec::SimulationSpec, +) where {N, L, T, P, S <: SystemModel{N, L, T, P}, M <: ReliabilityMetric} _, powerunit, _ = unitsymbol(sys_baseline) regionnames = sys_baseline.regions.names @@ -58,10 +58,10 @@ function assess(sys_baseline::S, sys_augmented::S, push!(metrics, upper_bound_metric) while true - params.verbose && println( "\n$(lower_bound) $powerunit\t< EFC <\t$(upper_bound) $powerunit\n", - "$(lower_bound_metric)\t> $(target_metric) >\t$(upper_bound_metric)") + "$(lower_bound_metric)\t> $(target_metric) >\t$(upper_bound_metric)", + ) midpoint = div(lower_bound + upper_bound, 2) capacity_gap = upper_bound - lower_bound @@ -70,7 +70,8 @@ function assess(sys_baseline::S, sys_augmented::S, ## Return the bounds if they are within solution tolerance of each other if capacity_gap <= params.capacity_gap - params.verbose && @info "Capacity bound gap within tolerance, stopping bisection." + params.verbose && + @info "Capacity bound gap within tolerance, stopping bisection." break end @@ -99,30 +100,37 @@ function assess(sys_baseline::S, sys_augmented::S, upper_bound = midpoint upper_bound_metric = midpoint_metric end - end return CapacityCreditResult{typeof(params), typeof(target_metric), P}( - target_metric, lower_bound, upper_bound, capacities, metrics) - + target_metric, + lower_bound, + upper_bound, + capacities, + metrics, + ) end function add_firmcapacity( - s1::SystemModel{N,L,T,P,E}, s2::SystemModel{N,L,T,P,E}, - region_shares::Vector{Tuple{String,Float64}} -) where {N,L,T,P,E} - + s1::SystemModel{N, L, T, P, E}, + s2::SystemModel{N, L, T, P, E}, + region_shares::Vector{Tuple{String, Float64}}, +) where {N, L, T, P, E} n_regions = length(s1.regions.names) n_region_allocs = length(region_shares) region_allocations = allocate_regions(s1.regions.names, region_shares) efc_gens = similar(region_allocations) - new_gen(i::Int) = Generators{N,L,T,P}( - ["_EFC_$i"], ["_EFC Calculation Dummy Generator"], - zeros(Int, 1, N), zeros(1, N), ones(1, N)) + new_gen(i::Int) = Generators{N, L, T, P}( + ["_EFC_$i"], + ["_EFC Calculation Dummy Generator"], + zeros(Int, 1, N), + zeros(1, N), + ones(1, N), + ) - variable_gens = Generators{N,L,T,P}[] + variable_gens = Generators{N, L, T, P}[] variable_region_gen_idxs = similar(s1.region_gen_idxs) target_gens = similar(variable_gens) @@ -131,59 +139,67 @@ function add_firmcapacity( ra_idx = 0 for r in 1:n_regions - s1_range = s1.region_gen_idxs[r] s2_range = s2.region_gen_idxs[r] - if (ra_idx < n_region_allocs) && (r == first(region_allocations[ra_idx+1])) - + if (ra_idx < n_region_allocs) && (r == first(region_allocations[ra_idx + 1])) ra_idx += 1 - variable_region_gen_idxs[r] = incr_range(s1_range, ra_idx-1, ra_idx) - target_region_gen_idxs[r] = incr_range(s2_range, ra_idx-1, ra_idx) + variable_region_gen_idxs[r] = incr_range(s1_range, ra_idx - 1, ra_idx) + target_region_gen_idxs[r] = incr_range(s2_range, ra_idx - 1, ra_idx) gen = new_gen(ra_idx) push!(variable_gens, gen) push!(target_gens, gen) - efc_gens[ra_idx] = ( - first(s1_range) + ra_idx - 1, - last(region_allocations[ra_idx])) + efc_gens[ra_idx] = + (first(s1_range) + ra_idx - 1, last(region_allocations[ra_idx])) else - variable_region_gen_idxs[r] = incr_range(s1_range, ra_idx) target_region_gen_idxs[r] = incr_range(s2_range, ra_idx) - end push!(variable_gens, s1.generators[s1_range]) push!(target_gens, s2.generators[s2_range]) - end sys_variable = SystemModel( - s1.regions, s1.interfaces, - vcat(variable_gens...), variable_region_gen_idxs, - s1.storages, s1.region_stor_idxs, - s1.generatorstorages, s1.region_genstor_idxs, - s1.lines, s1.interface_line_idxs, s1.timestamps) + s1.regions, + s1.interfaces, + vcat(variable_gens...), + variable_region_gen_idxs, + s1.storages, + s1.region_stor_idxs, + s1.generatorstorages, + s1.region_genstor_idxs, + s1.lines, + s1.interface_line_idxs, + s1.timestamps, + ) sys_target = SystemModel( - s2.regions, s2.interfaces, - vcat(target_gens...), target_region_gen_idxs, - s2.storages, s2.region_stor_idxs, - s2.generatorstorages, s2.region_genstor_idxs, - s2.lines, s2.interface_line_idxs, s2.timestamps) + s2.regions, + s2.interfaces, + vcat(target_gens...), + target_region_gen_idxs, + s2.storages, + s2.region_stor_idxs, + s2.generatorstorages, + s2.region_genstor_idxs, + s2.lines, + s2.interface_line_idxs, + s2.timestamps, + ) return efc_gens, sys_variable, sys_target - end function update_firmcapacity!( - sys::SystemModel, gens::Vector{Tuple{Int,Float64}}, capacity::Int) - + sys::SystemModel, + gens::Vector{Tuple{Int, Float64}}, + capacity::Int, +) for (g, share) in gens sys.generators.capacity[g, :] .= round(Int, share * capacity) end - end diff --git a/src/CapacityCredit/ELCC.jl b/src/CapacityCredit/ELCC.jl index 746290e6..5f506886 100644 --- a/src/CapacityCredit/ELCC.jl +++ b/src/CapacityCredit/ELCC.jl @@ -1,36 +1,36 @@ struct ELCC{M} <: CapacityValuationMethod{M} - capacity_max::Int capacity_gap::Int p_value::Float64 - regions::Vector{Tuple{String,Float64}} + regions::Vector{Tuple{String, Float64}} verbose::Bool function ELCC{M}( - capacity_max::Int, regions::Vector{Pair{String,Float64}}; - capacity_gap::Int=1, p_value::Float64=0.05, verbose::Bool=false) where M - + capacity_max::Int, + regions::Vector{Pair{String, Float64}}; + capacity_gap::Int=1, + p_value::Float64=0.05, + verbose::Bool=false, + ) where {M} @assert capacity_max > 0 @assert capacity_gap > 0 @assert 0 < p_value < 1 @assert sum(x.second for x in regions) ≈ 1.0 return new{M}(capacity_max, capacity_gap, p_value, Tuple.(regions), verbose) - end - end -function ELCC{M}( - capacity_max::Int, region::String; kwargs... -) where M - return ELCC{M}(capacity_max, [region=>1.0]; kwargs...) +function ELCC{M}(capacity_max::Int, region::String; kwargs...) where {M} + return ELCC{M}(capacity_max, [region => 1.0]; kwargs...) end -function assess(sys_baseline::S, sys_augmented::S, - params::ELCC{M}, simulationspec::SimulationSpec -) where {N, L, T, P, S <: SystemModel{N,L,T,P}, M <: ReliabilityMetric} - +function assess( + sys_baseline::S, + sys_augmented::S, + params::ELCC{M}, + simulationspec::SimulationSpec, +) where {N, L, T, P, S <: SystemModel{N, L, T, P}, M <: ReliabilityMetric} _, powerunit, _ = unitsymbol(sys_baseline) regionnames = sys_baseline.regions.names @@ -42,8 +42,7 @@ function assess(sys_baseline::S, sys_augmented::S, capacities = Int[] metrics = typeof(target_metric)[] - elcc_regions, base_load, sys_variable = - copy_load(sys_augmented, params.regions) + elcc_regions, base_load, sys_variable = copy_load(sys_augmented, params.regions) lower_bound = 0 lower_bound_metric = M(first(assess(sys_variable, simulationspec, Shortfall()))) @@ -57,10 +56,10 @@ function assess(sys_baseline::S, sys_augmented::S, push!(metrics, upper_bound_metric) while true - params.verbose && println( "\n$(lower_bound) $powerunit\t< ELCC <\t$(upper_bound) $powerunit\n", - "$(lower_bound_metric)\t< $(target_metric) <\t$(upper_bound_metric)") + "$(lower_bound_metric)\t< $(target_metric) <\t$(upper_bound_metric)", + ) midpoint = div(lower_bound + upper_bound, 2) capacity_gap = upper_bound - lower_bound @@ -69,7 +68,8 @@ function assess(sys_baseline::S, sys_augmented::S, ## Return the bounds if they are within solution tolerance of each other if capacity_gap <= params.capacity_gap - params.verbose && @info "Capacity bound gap within tolerance, stopping bisection." + params.verbose && + @info "Capacity bound gap within tolerance, stopping bisection." break end @@ -98,41 +98,49 @@ function assess(sys_baseline::S, sys_augmented::S, upper_bound = midpoint upper_bound_metric = midpoint_metric end - end return CapacityCreditResult{typeof(params), typeof(target_metric), P}( - target_metric, lower_bound, upper_bound, capacities, metrics) - + target_metric, + lower_bound, + upper_bound, + capacities, + metrics, + ) end function copy_load( - sys::SystemModel{N,L,T,P,E}, - region_shares::Vector{Tuple{String,Float64}} -) where {N,L,T,P,E} - + sys::SystemModel{N, L, T, P, E}, + region_shares::Vector{Tuple{String, Float64}}, +) where {N, L, T, P, E} region_allocations = allocate_regions(sys.regions.names, region_shares) - new_regions = Regions{N,P}(sys.regions.names, copy(sys.regions.load)) - - return region_allocations, sys.regions.load, SystemModel( - new_regions, sys.interfaces, - sys.generators, sys.region_gen_idxs, - sys.storages, sys.region_stor_idxs, - sys.generatorstorages, sys.region_genstor_idxs, - sys.lines, sys.interface_line_idxs, sys.timestamps) - + new_regions = Regions{N, P}(sys.regions.names, copy(sys.regions.load)) + + return region_allocations, + sys.regions.load, + SystemModel( + new_regions, + sys.interfaces, + sys.generators, + sys.region_gen_idxs, + sys.storages, + sys.region_stor_idxs, + sys.generatorstorages, + sys.region_genstor_idxs, + sys.lines, + sys.interface_line_idxs, + sys.timestamps, + ) end function update_load!( sys::SystemModel, - region_shares::Vector{Tuple{Int,Float64}}, + region_shares::Vector{Tuple{Int, Float64}}, load_base::Matrix{Int}, - load_increase::Int + load_increase::Int, ) for (r, share) in region_shares - sys.regions.load[r, :] .= load_base[r, :] .+ - round(Int, share * load_increase) + sys.regions.load[r, :] .= load_base[r, :] .+ round(Int, share * load_increase) end - end diff --git a/src/CapacityCredit/utils.jl b/src/CapacityCredit/utils.jl index 2f09a4a8..b7651889 100644 --- a/src/CapacityCredit/utils.jl +++ b/src/CapacityCredit/utils.jl @@ -1,5 +1,4 @@ -function pvalue(lower::T, upper::T) where {T<:ReliabilityMetric} - +function pvalue(lower::T, upper::T) where {T <: ReliabilityMetric} vl = val(lower) sl = stderror(lower) @@ -15,29 +14,23 @@ function pvalue(lower::T, upper::T) where {T<:ReliabilityMetric} end return result - end function allocate_regions( region_names::Vector{String}, - regionname_shares::Vector{Tuple{String,Float64}} + regionname_shares::Vector{Tuple{String, Float64}}, ) - - region_allocations = similar(regionname_shares, Tuple{Int,Float64}) + region_allocations = similar(regionname_shares, Tuple{Int, Float64}) for (i, (name, share)) in enumerate(regionname_shares) - r = findfirst(isequal(name), region_names) - isnothing(r) && - error("$name is not a region name in the provided systems") + isnothing(r) && error("$name is not a region name in the provided systems") region_allocations[i] = (r, share) - end return sort!(region_allocations) - end incr_range(rnge::UnitRange{Int}, inc::Int) = rnge .+ inc diff --git a/src/PRAS.jl b/src/PRAS.jl index c5e729f7..8093d33d 100644 --- a/src/PRAS.jl +++ b/src/PRAS.jl @@ -8,6 +8,6 @@ include("PRASBase/PRASBase.jl") include("ResourceAdequacy/ResourceAdequacy.jl") include("CapacityCredit/CapacityCredit.jl") -import .PRASBase: rts_gmlc,toymodel +import .PRASBase: rts_gmlc, toymodel end diff --git a/src/PRASBase/PRASBase.jl b/src/PRASBase/PRASBase.jl index 75e9f971..7d301fcb 100644 --- a/src/PRASBase/PRASBase.jl +++ b/src/PRASBase/PRASBase.jl @@ -4,31 +4,68 @@ import ..PRAS_VERSION import Base: broadcastable -import Dates: @dateformat_str, AbstractDateTime, DateTime, - Period, Minute, Hour, Day, Year +import Dates: @dateformat_str, AbstractDateTime, DateTime, Period, Minute, Hour, Day, Year -import HDF5: HDF5, attributes, File, Group, Dataset, Datatype, dataspace, - h5open, create_group, create_dataset, hdf5_type_id +import HDF5: + HDF5, + attributes, + File, + Group, + Dataset, + Datatype, + dataspace, + h5open, + create_group, + create_dataset, + hdf5_type_id -import HDF5.API: h5t_create, h5t_copy, h5t_insert, h5t_set_size, H5T_COMPOUND, - h5d_write, H5S_ALL, H5P_DEFAULT +import HDF5.API: + h5t_create, + h5t_copy, + h5t_insert, + h5t_set_size, + H5T_COMPOUND, + h5d_write, + H5S_ALL, + H5P_DEFAULT import TimeZones: TimeZone, ZonedDateTime export # System assets - Regions, Interfaces, - AbstractAssets, Generators, Storages, GeneratorStorages, Lines, + Regions, + Interfaces, + AbstractAssets, + Generators, + Storages, + GeneratorStorages, + Lines, # Units - Period, Minute, Hour, Day, Year, - PowerUnit, kW, MW, GW, TW, - EnergyUnit, kWh, MWh, GWh, TWh, - unitsymbol, conversionfactor, powertoenergy, energytopower, + Period, + Minute, + Hour, + Day, + Year, + PowerUnit, + kW, + MW, + GW, + TW, + EnergyUnit, + kWh, + MWh, + GWh, + TWh, + unitsymbol, + conversionfactor, + powertoenergy, + energytopower, # Main data structure - SystemModel, savemodel + SystemModel, + savemodel include("units.jl") include("collections.jl") diff --git a/src/PRASBase/SystemModel.jl b/src/PRASBase/SystemModel.jl index 119c4657..222dbed1 100644 --- a/src/PRASBase/SystemModel.jl +++ b/src/PRASBase/SystemModel.jl @@ -1,32 +1,34 @@ -struct SystemModel{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} +struct SystemModel{N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} + regions::Regions{N, P} + interfaces::Interfaces{N, P} - regions::Regions{N,P} - interfaces::Interfaces{N,P} - - generators::Generators{N,L,T,P} + generators::Generators{N, L, T, P} region_gen_idxs::Vector{UnitRange{Int}} - storages::Storages{N,L,T,P,E} + storages::Storages{N, L, T, P, E} region_stor_idxs::Vector{UnitRange{Int}} - generatorstorages::GeneratorStorages{N,L,T,P,E} + generatorstorages::GeneratorStorages{N, L, T, P, E} region_genstor_idxs::Vector{UnitRange{Int}} - lines::Lines{N,L,T,P} + lines::Lines{N, L, T, P} interface_line_idxs::Vector{UnitRange{Int}} - timestamps::StepRange{ZonedDateTime,T} + timestamps::StepRange{ZonedDateTime, T} function SystemModel{}( - regions::Regions{N,P}, interfaces::Interfaces{N,P}, - generators::Generators{N,L,T,P}, region_gen_idxs::Vector{UnitRange{Int}}, - storages::Storages{N,L,T,P,E}, region_stor_idxs::Vector{UnitRange{Int}}, - generatorstorages::GeneratorStorages{N,L,T,P,E}, + regions::Regions{N, P}, + interfaces::Interfaces{N, P}, + generators::Generators{N, L, T, P}, + region_gen_idxs::Vector{UnitRange{Int}}, + storages::Storages{N, L, T, P, E}, + region_stor_idxs::Vector{UnitRange{Int}}, + generatorstorages::GeneratorStorages{N, L, T, P, E}, region_genstor_idxs::Vector{UnitRange{Int}}, - lines::Lines{N,L,T,P}, interface_line_idxs::Vector{UnitRange{Int}}, - timestamps::StepRange{ZonedDateTime,T} - ) where {N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} - + lines::Lines{N, L, T, P}, + interface_line_idxs::Vector{UnitRange{Int}}, + timestamps::StepRange{ZonedDateTime, T}, + ) where {N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} n_regions = length(regions) n_gens = length(generators) n_stors = length(storages) @@ -41,32 +43,43 @@ struct SystemModel{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} @assert consistent_idxs(interface_line_idxs, n_lines, n_interfaces) @assert all( - 1 <= interfaces.regions_from[i] < interfaces.regions_to[i] <= n_regions - for i in 1:n_interfaces) + 1 <= interfaces.regions_from[i] < interfaces.regions_to[i] <= n_regions for + i in 1:n_interfaces + ) @assert step(timestamps) == T(L) @assert length(timestamps) == N - new{N,L,T,P,E}( - regions, interfaces, - generators, region_gen_idxs, storages, region_stor_idxs, - generatorstorages, region_genstor_idxs, lines, interface_line_idxs, - timestamps) - + new{N, L, T, P, E}( + regions, + interfaces, + generators, + region_gen_idxs, + storages, + region_stor_idxs, + generatorstorages, + region_genstor_idxs, + lines, + interface_line_idxs, + timestamps, + ) end - end # No time zone constructor function SystemModel( - regions::Regions{N,P}, interfaces::Interfaces{N,P}, - generators::Generators{N,L,T,P}, region_gen_idxs::Vector{UnitRange{Int}}, - storages::Storages{N,L,T,P,E}, region_stor_idxs::Vector{UnitRange{Int}}, - generatorstorages::GeneratorStorages{N,L,T,P,E}, region_genstor_idxs::Vector{UnitRange{Int}}, - lines, interface_line_idxs::Vector{UnitRange{Int}}, - timestamps::StepRange{DateTime,T} -) where {N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} - + regions::Regions{N, P}, + interfaces::Interfaces{N, P}, + generators::Generators{N, L, T, P}, + region_gen_idxs::Vector{UnitRange{Int}}, + storages::Storages{N, L, T, P, E}, + region_stor_idxs::Vector{UnitRange{Int}}, + generatorstorages::GeneratorStorages{N, L, T, P, E}, + region_genstor_idxs::Vector{UnitRange{Int}}, + lines, + interface_line_idxs::Vector{UnitRange{Int}}, + timestamps::StepRange{DateTime, T}, +) where {N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} @warn "No time zone data provided - defaulting to UTC. To specify a " * "time zone for the system timestamps, provide a range of " * "`ZonedDateTime` instead of `DateTime`." @@ -77,38 +90,48 @@ function SystemModel( timestamps_tz = time_start:step(timestamps):time_end return SystemModel( - regions, interfaces, - generators, region_gen_idxs, - storages, region_stor_idxs, - generatorstorages, region_genstor_idxs, - lines, interface_line_idxs, - timestamps_tz) - + regions, + interfaces, + generators, + region_gen_idxs, + storages, + region_stor_idxs, + generatorstorages, + region_genstor_idxs, + lines, + interface_line_idxs, + timestamps_tz, + ) end # Single-node constructor function SystemModel( - generators::Generators{N,L,T,P}, - storages::Storages{N,L,T,P,E}, - generatorstorages::GeneratorStorages{N,L,T,P,E}, - timestamps::StepRange{<:AbstractDateTime,T}, - load::Vector{Int} -) where {N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} - + generators::Generators{N, L, T, P}, + storages::Storages{N, L, T, P, E}, + generatorstorages::GeneratorStorages{N, L, T, P, E}, + timestamps::StepRange{<:AbstractDateTime, T}, + load::Vector{Int}, +) where {N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} return SystemModel( - Regions{N,P}(["Region"], reshape(load, 1, :)), - Interfaces{N,P}( - Int[], Int[], - Matrix{Int}(undef, 0, N), Matrix{Int}(undef, 0, N)), - generators, [1:length(generators)], - storages, [1:length(storages)], - generatorstorages, [1:length(generatorstorages)], - Lines{N,L,T,P}( - String[], String[], - Matrix{Int}(undef, 0, N), Matrix{Int}(undef, 0, N), - Matrix{Float64}(undef, 0, N), Matrix{Float64}(undef, 0, N)), - UnitRange{Int}[], timestamps) - + Regions{N, P}(["Region"], reshape(load, 1, :)), + Interfaces{N, P}(Int[], Int[], Matrix{Int}(undef, 0, N), Matrix{Int}(undef, 0, N)), + generators, + [1:length(generators)], + storages, + [1:length(storages)], + generatorstorages, + [1:length(generatorstorages)], + Lines{N, L, T, P}( + String[], + String[], + Matrix{Int}(undef, 0, N), + Matrix{Int}(undef, 0, N), + Matrix{Float64}(undef, 0, N), + Matrix{Float64}(undef, 0, N), + ), + UnitRange{Int}[], + timestamps, + ) end Base.:(==)(x::T, y::T) where {T <: SystemModel} = @@ -126,12 +149,12 @@ Base.:(==)(x::T, y::T) where {T <: SystemModel} = broadcastable(x::SystemModel) = Ref(x) -unitsymbol(::SystemModel{N,L,T,P,E}) where { - N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} = +unitsymbol( + ::SystemModel{N, L, T, P, E}, +) where {N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} = unitsymbol(T), unitsymbol(P), unitsymbol(E) function consistent_idxs(idxss::Vector{UnitRange{Int}}, nitems::Int, ngroups::Int) - length(idxss) == ngroups || return false expected_next = 1 @@ -142,7 +165,6 @@ function consistent_idxs(idxss::Vector{UnitRange{Int}}, nitems::Int, ngroups::In expected_next == nitems + 1 || return false return true - end function rts_gmlc() @@ -153,4 +175,4 @@ end function toymodel() path = dirname(@__FILE__) return SystemModel(path * "/toymodel.pras") -end \ No newline at end of file +end diff --git a/src/PRASBase/assets.jl b/src/PRASBase/assets.jl index 14c13d14..4cd20fe7 100644 --- a/src/PRASBase/assets.jl +++ b/src/PRASBase/assets.jl @@ -1,8 +1,7 @@ -abstract type AbstractAssets{N,L,T<:Period,P<:PowerUnit} end +abstract type AbstractAssets{N, L, T <: Period, P <: PowerUnit} end Base.length(a::AbstractAssets) = length(a.names) -struct Generators{N,L,T<:Period,P<:PowerUnit} <: AbstractAssets{N,L,T,P} - +struct Generators{N, L, T <: Period, P <: PowerUnit} <: AbstractAssets{N, L, T, P} names::Vector{String} categories::Vector{String} @@ -11,11 +10,13 @@ struct Generators{N,L,T<:Period,P<:PowerUnit} <: AbstractAssets{N,L,T,P} λ::Matrix{Float64} μ::Matrix{Float64} - function Generators{N,L,T,P}( - names::Vector{<:AbstractString}, categories::Vector{<:AbstractString}, - capacity::Matrix{Int}, λ::Matrix{Float64}, μ::Matrix{Float64} - ) where {N,L,T,P} - + function Generators{N, L, T, P}( + names::Vector{<:AbstractString}, + categories::Vector{<:AbstractString}, + capacity::Matrix{Int}, + λ::Matrix{Float64}, + μ::Matrix{Float64}, + ) where {N, L, T, P} n_gens = length(names) @assert length(categories) == n_gens @assert allunique(names) @@ -28,10 +29,8 @@ struct Generators{N,L,T<:Period,P<:PowerUnit} <: AbstractAssets{N,L,T,P} @assert all(0 .<= λ .<= 1) @assert all(0 .<= μ .<= 1) - new{N,L,T,P}(string.(names), string.(categories), capacity, λ, μ) - + new{N, L, T, P}(string.(names), string.(categories), capacity, λ, μ) end - end Base.:(==)(x::T, y::T) where {T <: Generators} = @@ -42,11 +41,9 @@ Base.:(==)(x::T, y::T) where {T <: Generators} = x.μ == y.μ Base.getindex(g::G, idxs::AbstractVector{Int}) where {G <: Generators} = - G(g.names[idxs], g.categories[idxs], - g.capacity[idxs, :], g.λ[idxs, :], g.μ[idxs, :]) - -function Base.vcat(gs::Generators{N,L,T,P}...) where {N, L, T, P} + G(g.names[idxs], g.categories[idxs], g.capacity[idxs, :], g.λ[idxs, :], g.μ[idxs, :]) +function Base.vcat(gs::Generators{N, L, T, P}...) where {N, L, T, P} n_gens = sum(length(g) for g in gs) names = Vector{String}(undef, n_gens) @@ -60,7 +57,6 @@ function Base.vcat(gs::Generators{N,L,T,P}...) where {N, L, T, P} last_idx = 0 for g in gs - n = length(g) rows = last_idx .+ (1:n) @@ -71,15 +67,13 @@ function Base.vcat(gs::Generators{N,L,T,P}...) where {N, L, T, P} μ[rows, :] = g.μ last_idx += n - end - return Generators{N,L,T,P}(names, categories, capacity, λ, μ) - + return Generators{N, L, T, P}(names, categories, capacity, λ, μ) end -struct Storages{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractAssets{N,L,T,P} - +struct Storages{N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} <: + AbstractAssets{N, L, T, P} names::Vector{String} categories::Vector{String} @@ -94,14 +88,18 @@ struct Storages{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractAssets{N,L, λ::Matrix{Float64} μ::Matrix{Float64} - function Storages{N,L,T,P,E}( - names::Vector{<:AbstractString}, categories::Vector{<:AbstractString}, - chargecapacity::Matrix{Int}, dischargecapacity::Matrix{Int}, - energycapacity::Matrix{Int}, chargeefficiency::Matrix{Float64}, - dischargeefficiency::Matrix{Float64}, carryoverefficiency::Matrix{Float64}, - λ::Matrix{Float64}, μ::Matrix{Float64} - ) where {N,L,T,P,E} - + function Storages{N, L, T, P, E}( + names::Vector{<:AbstractString}, + categories::Vector{<:AbstractString}, + chargecapacity::Matrix{Int}, + dischargecapacity::Matrix{Int}, + energycapacity::Matrix{Int}, + chargeefficiency::Matrix{Float64}, + dischargeefficiency::Matrix{Float64}, + carryoverefficiency::Matrix{Float64}, + λ::Matrix{Float64}, + μ::Matrix{Float64}, + ) where {N, L, T, P, E} n_stors = length(names) @assert length(categories) == n_stors @assert allunique(names) @@ -125,13 +123,19 @@ struct Storages{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractAssets{N,L, @assert all(0 .<= λ .<= 1) @assert all(0 .<= μ .<= 1) - new{N,L,T,P,E}(string.(names), string.(categories), - chargecapacity, dischargecapacity, energycapacity, - chargeefficiency, dischargeefficiency, carryoverefficiency, - λ, μ) - + new{N, L, T, P, E}( + string.(names), + string.(categories), + chargecapacity, + dischargecapacity, + energycapacity, + chargeefficiency, + dischargeefficiency, + carryoverefficiency, + λ, + μ, + ) end - end Base.:(==)(x::T, y::T) where {T <: Storages} = @@ -146,14 +150,20 @@ Base.:(==)(x::T, y::T) where {T <: Storages} = x.λ == y.λ && x.μ == y.μ -Base.getindex(s::S, idxs::AbstractVector{Int}) where {S <: Storages} = - S(s.names[idxs], s.categories[idxs],s.charge_capacity[idxs,:], - s.discharge_capacity[idxs, :],s.energy_capacity[idxs, :], - s.charge_efficiency[idxs, :], s.discharge_efficiency[idxs, :], - s.carryover_efficiency[idxs, :],s.λ[idxs, :], s.μ[idxs, :]) - -function Base.vcat(stors::Storages{N,L,T,P,E}...) where {N, L, T, P, E} - +Base.getindex(s::S, idxs::AbstractVector{Int}) where {S <: Storages} = S( + s.names[idxs], + s.categories[idxs], + s.charge_capacity[idxs, :], + s.discharge_capacity[idxs, :], + s.energy_capacity[idxs, :], + s.charge_efficiency[idxs, :], + s.discharge_efficiency[idxs, :], + s.carryover_efficiency[idxs, :], + s.λ[idxs, :], + s.μ[idxs, :], +) + +function Base.vcat(stors::Storages{N, L, T, P, E}...) where {N, L, T, P, E} n_stors = sum(length(s) for s in stors) names = Vector{String}(undef, n_stors) @@ -161,7 +171,7 @@ function Base.vcat(stors::Storages{N,L,T,P,E}...) where {N, L, T, P, E} charge_capacity = Matrix{Int}(undef, n_stors, N) discharge_capacity = Matrix{Int}(undef, n_stors, N) - energy_capacity = Matrix{Int}(undef, n_stors, N) + energy_capacity = Matrix{Int}(undef, n_stors, N) charge_efficiency = Matrix{Float64}(undef, n_stors, N) discharge_efficiency = Matrix{Float64}(undef, n_stors, N) @@ -173,7 +183,6 @@ function Base.vcat(stors::Storages{N,L,T,P,E}...) where {N, L, T, P, E} last_idx = 0 for s in stors - n = length(s) rows = last_idx .+ (1:n) @@ -192,16 +201,24 @@ function Base.vcat(stors::Storages{N,L,T,P,E}...) where {N, L, T, P, E} μ[rows, :] = s.μ last_idx += n - end - return Storages{N,L,T,P,E}(names, categories, charge_capacity, discharge_capacity, energy_capacity, charge_efficiency, discharge_efficiency, - carryover_efficiency, λ, μ) - + return Storages{N, L, T, P, E}( + names, + categories, + charge_capacity, + discharge_capacity, + energy_capacity, + charge_efficiency, + discharge_efficiency, + carryover_efficiency, + λ, + μ, + ) end -struct GeneratorStorages{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractAssets{N,L,T,P} - +struct GeneratorStorages{N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} <: + AbstractAssets{N, L, T, P} names::Vector{String} categories::Vector{String} @@ -220,17 +237,21 @@ struct GeneratorStorages{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractAs λ::Matrix{Float64} μ::Matrix{Float64} - function GeneratorStorages{N,L,T,P,E}( - names::Vector{<:AbstractString}, categories::Vector{<:AbstractString}, - charge_capacity::Matrix{Int}, discharge_capacity::Matrix{Int}, + function GeneratorStorages{N, L, T, P, E}( + names::Vector{<:AbstractString}, + categories::Vector{<:AbstractString}, + charge_capacity::Matrix{Int}, + discharge_capacity::Matrix{Int}, energy_capacity::Matrix{Int}, - charge_efficiency::Matrix{Float64}, discharge_efficiency::Matrix{Float64}, + charge_efficiency::Matrix{Float64}, + discharge_efficiency::Matrix{Float64}, carryover_efficiency::Matrix{Float64}, inflow::Matrix{Int}, - gridwithdrawal_capacity::Matrix{Int}, gridinjection_capacity::Matrix{Int}, - λ::Matrix{Float64}, μ::Matrix{Float64} - ) where {N,L,T,P,E} - + gridwithdrawal_capacity::Matrix{Int}, + gridinjection_capacity::Matrix{Int}, + λ::Matrix{Float64}, + μ::Matrix{Float64}, + ) where {N, L, T, P, E} n_stors = length(names) @assert length(categories) == n_stors @assert allunique(names) @@ -264,15 +285,22 @@ struct GeneratorStorages{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractAs @assert all(0 .<= λ .<= 1) @assert all(0 .<= μ .<= 1) - new{N,L,T,P,E}( - string.(names), string.(categories), - charge_capacity, discharge_capacity, energy_capacity, - charge_efficiency, discharge_efficiency, carryover_efficiency, - inflow, gridwithdrawal_capacity, gridinjection_capacity, - λ, μ) - + new{N, L, T, P, E}( + string.(names), + string.(categories), + charge_capacity, + discharge_capacity, + energy_capacity, + charge_efficiency, + discharge_efficiency, + carryover_efficiency, + inflow, + gridwithdrawal_capacity, + gridinjection_capacity, + λ, + μ, + ) end - end Base.:(==)(x::T, y::T) where {T <: GeneratorStorages} = @@ -290,16 +318,23 @@ Base.:(==)(x::T, y::T) where {T <: GeneratorStorages} = x.λ == y.λ && x.μ == y.μ -Base.getindex(g_s::G, idxs::AbstractVector{Int}) where {G <: GeneratorStorages} = - G(g_s.names[idxs], g_s.categories[idxs], g_s.charge_capacity[idxs,:], - g_s.discharge_capacity[idxs, :], g_s.energy_capacity[idxs, :], - g_s.charge_efficiency[idxs, :], g_s.discharge_efficiency[idxs, :], - g_s.carryover_efficiency[idxs, :],g_s.inflow[idxs, :], - g_s.gridwithdrawal_capacity[idxs, :],g_s.gridinjection_capacity[idxs, :], - g_s.λ[idxs, :], g_s.μ[idxs, :]) - -function Base.vcat(gen_stors::GeneratorStorages{N,L,T,P,E}...) where {N, L, T, P, E} - +Base.getindex(g_s::G, idxs::AbstractVector{Int}) where {G <: GeneratorStorages} = G( + g_s.names[idxs], + g_s.categories[idxs], + g_s.charge_capacity[idxs, :], + g_s.discharge_capacity[idxs, :], + g_s.energy_capacity[idxs, :], + g_s.charge_efficiency[idxs, :], + g_s.discharge_efficiency[idxs, :], + g_s.carryover_efficiency[idxs, :], + g_s.inflow[idxs, :], + g_s.gridwithdrawal_capacity[idxs, :], + g_s.gridinjection_capacity[idxs, :], + g_s.λ[idxs, :], + g_s.μ[idxs, :], +) + +function Base.vcat(gen_stors::GeneratorStorages{N, L, T, P, E}...) where {N, L, T, P, E} n_gen_stors = sum(length(g_s) for g_s in gen_stors) names = Vector{String}(undef, n_gen_stors) @@ -307,7 +342,7 @@ function Base.vcat(gen_stors::GeneratorStorages{N,L,T,P,E}...) where {N, L, T, P charge_capacity = Matrix{Int}(undef, n_gen_stors, N) discharge_capacity = Matrix{Int}(undef, n_gen_stors, N) - energy_capacity = Matrix{Int}(undef, n_gen_stors, N) + energy_capacity = Matrix{Int}(undef, n_gen_stors, N) charge_efficiency = Matrix{Float64}(undef, n_gen_stors, N) discharge_efficiency = Matrix{Float64}(undef, n_gen_stors, N) @@ -323,7 +358,6 @@ function Base.vcat(gen_stors::GeneratorStorages{N,L,T,P,E}...) where {N, L, T, P last_idx = 0 for g_s in gen_stors - n = length(g_s) rows = last_idx .+ (1:n) @@ -346,16 +380,26 @@ function Base.vcat(gen_stors::GeneratorStorages{N,L,T,P,E}...) where {N, L, T, P μ[rows, :] = g_s.μ last_idx += n - end - return GeneratorStorages{N,L,T,P,E}(names, categories, charge_capacity, discharge_capacity, energy_capacity, charge_efficiency, discharge_efficiency, - carryover_efficiency,inflow, gridwithdrawal_capacity, gridinjection_capacity, λ, μ) - + return GeneratorStorages{N, L, T, P, E}( + names, + categories, + charge_capacity, + discharge_capacity, + energy_capacity, + charge_efficiency, + discharge_efficiency, + carryover_efficiency, + inflow, + gridwithdrawal_capacity, + gridinjection_capacity, + λ, + μ, + ) end -struct Lines{N,L,T<:Period,P<:PowerUnit} <: AbstractAssets{N,L,T,P} - +struct Lines{N, L, T <: Period, P <: PowerUnit} <: AbstractAssets{N, L, T, P} names::Vector{String} categories::Vector{String} @@ -365,12 +409,14 @@ struct Lines{N,L,T<:Period,P<:PowerUnit} <: AbstractAssets{N,L,T,P} λ::Matrix{Float64} μ::Matrix{Float64} - function Lines{N,L,T,P}( - names::Vector{<:AbstractString}, categories::Vector{<:AbstractString}, - forward_capacity::Matrix{Int}, backward_capacity::Matrix{Int}, - λ::Matrix{Float64}, μ::Matrix{Float64} - ) where {N,L,T,P} - + function Lines{N, L, T, P}( + names::Vector{<:AbstractString}, + categories::Vector{<:AbstractString}, + forward_capacity::Matrix{Int}, + backward_capacity::Matrix{Int}, + λ::Matrix{Float64}, + μ::Matrix{Float64}, + ) where {N, L, T, P} n_lines = length(names) @assert length(categories) == n_lines @assert allunique(names) @@ -385,10 +431,15 @@ struct Lines{N,L,T<:Period,P<:PowerUnit} <: AbstractAssets{N,L,T,P} @assert all(0 .<= λ .<= 1) @assert all(0 .<= μ .<= 1) - new{N,L,T,P}(string.(names), string.(categories), forward_capacity, backward_capacity, λ, μ) - + new{N, L, T, P}( + string.(names), + string.(categories), + forward_capacity, + backward_capacity, + λ, + μ, + ) end - end Base.:(==)(x::T, y::T) where {T <: Lines} = @@ -399,12 +450,16 @@ Base.:(==)(x::T, y::T) where {T <: Lines} = x.λ == y.λ && x.μ == y.μ -Base.getindex(lines::L, idxs::AbstractVector{Int}) where {L <: Lines} = - L(lines.names[idxs], lines.categories[idxs],lines.forward_capacity[idxs,:], - lines.backward_capacity[idxs, :],lines.λ[idxs, :], lines.μ[idxs, :]) - -function Base.vcat(lines::Lines{N,L,T,P}...) where {N, L, T, P} +Base.getindex(lines::L, idxs::AbstractVector{Int}) where {L <: Lines} = L( + lines.names[idxs], + lines.categories[idxs], + lines.forward_capacity[idxs, :], + lines.backward_capacity[idxs, :], + lines.λ[idxs, :], + lines.μ[idxs, :], +) +function Base.vcat(lines::Lines{N, L, T, P}...) where {N, L, T, P} n_lines = sum(length(line) for line in lines) names = Vector{String}(undef, n_lines) @@ -412,14 +467,13 @@ function Base.vcat(lines::Lines{N,L,T,P}...) where {N, L, T, P} forward_capacity = Matrix{Int}(undef, n_lines, N) backward_capacity = Matrix{Int}(undef, n_lines, N) - - λ = Matrix{Float64}(undef,n_lines, N) - μ = Matrix{Float64}(undef,n_lines, N) + + λ = Matrix{Float64}(undef, n_lines, N) + μ = Matrix{Float64}(undef, n_lines, N) last_idx = 0 for line in lines - n = length(line) rows = last_idx .+ (1:n) @@ -433,9 +487,7 @@ function Base.vcat(lines::Lines{N,L,T,P}...) where {N, L, T, P} μ[rows, :] = line.μ last_idx += n - end - return Lines{N,L,T,P}(names, categories, forward_capacity, backward_capacity, λ, μ) - -end \ No newline at end of file + return Lines{N, L, T, P}(names, categories, forward_capacity, backward_capacity, λ, μ) +end diff --git a/src/PRASBase/collections.jl b/src/PRASBase/collections.jl index e1b003ab..6549b060 100644 --- a/src/PRASBase/collections.jl +++ b/src/PRASBase/collections.jl @@ -1,41 +1,36 @@ -struct Regions{N,P<:PowerUnit} - +struct Regions{N, P <: PowerUnit} names::Vector{String} load::Matrix{Int} - function Regions{N,P}( - names::Vector{<:AbstractString}, load::Matrix{Int} - ) where {N,P<:PowerUnit} - + function Regions{N, P}( + names::Vector{<:AbstractString}, + load::Matrix{Int}, + ) where {N, P <: PowerUnit} n_regions = length(names) @assert size(load) == (n_regions, N) @assert all(load .>= 0) - new{N,P}(string.(names), load) - + new{N, P}(string.(names), load) end - end -Base.:(==)(x::T, y::T) where {T <: Regions} = - x.names == y.names && - x.load == y.load +Base.:(==)(x::T, y::T) where {T <: Regions} = x.names == y.names && x.load == y.load Base.length(r::Regions) = length(r.names) -struct Interfaces{N,P<:PowerUnit} - +struct Interfaces{N, P <: PowerUnit} regions_from::Vector{Int} regions_to::Vector{Int} limit_forward::Matrix{Int} limit_backward::Matrix{Int} - function Interfaces{N,P}( - regions_from::Vector{Int}, regions_to::Vector{Int}, - forwardcapacity::Matrix{Int}, backwardcapacity::Matrix{Int} - ) where {N,P<:PowerUnit} - + function Interfaces{N, P}( + regions_from::Vector{Int}, + regions_to::Vector{Int}, + forwardcapacity::Matrix{Int}, + backwardcapacity::Matrix{Int}, + ) where {N, P <: PowerUnit} n_interfaces = length(regions_from) @assert length(regions_to) == n_interfaces @@ -44,10 +39,8 @@ struct Interfaces{N,P<:PowerUnit} @assert all(forwardcapacity .>= 0) @assert all(backwardcapacity .>= 0) - new{N,P}(regions_from, regions_to, forwardcapacity, backwardcapacity) - + new{N, P}(regions_from, regions_to, forwardcapacity, backwardcapacity) end - end Base.:(==)(x::T, y::T) where {T <: Interfaces} = diff --git a/src/PRASBase/read.jl b/src/PRASBase/read.jl index a600c384..85019877 100644 --- a/src/PRASBase/read.jl +++ b/src/PRASBase/read.jl @@ -1,35 +1,28 @@ """ - SystemModel(filename::String) Load a `SystemModel` from an appropriately-formatted HDF5 file on disk. """ function SystemModel(inputfile::String) - system = h5open(inputfile, "r") do f::File - version, versionstring = readversion(f) # Determine the appropriate version of the importer to use - return if (0,5,0) <= version < (0,7,0) + return if (0, 5, 0) <= version < (0, 7, 0) systemmodel_0_5(f) else error("PRAS file format $versionstring not supported by this version of PRASBase.") end - end return system - end - function systemmodel_0_5(f::File) - metadata = attributes(f) - start_timestamp = ZonedDateTime(read(metadata["start_timestamp"]), - dateformat"yyyy-mm-ddTHH:MM:SSz") + start_timestamp = + ZonedDateTime(read(metadata["start_timestamp"]), dateformat"yyyy-mm-ddTHH:MM:SSz") N = Int(read(metadata["timestep_count"])) L = Int(read(metadata["timestep_length"])) @@ -38,7 +31,7 @@ function systemmodel_0_5(f::File) E = energyunits[read(metadata["energy_unit"])] timestep = T(L) - end_timestamp = start_timestamp + (N-1)*timestep + end_timestamp = start_timestamp + (N - 1) * timestep timestamps = StepRange(start_timestamp, timestep, end_timestamp) has_regions = haskey(f, "regions") @@ -48,62 +41,61 @@ function systemmodel_0_5(f::File) has_interfaces = haskey(f, "interfaces") has_lines = haskey(f, "lines") - has_regions || - error("Region data must be provided") + has_regions || error("Region data must be provided") - has_generators || has_generatorstorages || + has_generators || + has_generatorstorages || error("Generator or generator storage data (or both) must be provided") xor(has_interfaces, has_lines) && error("Both (or neither) interface and line data must be provided") regionnames = readvector(f["regions/_core"], :name) - regions = Regions{N,P}( - regionnames, - Int.(read(f["regions/load"])) - ) - regionlookup = Dict(n=>i for (i, n) in enumerate(regionnames)) + regions = Regions{N, P}(regionnames, Int.(read(f["regions/load"]))) + regionlookup = Dict(n => i for (i, n) in enumerate(regionnames)) n_regions = length(regions) if has_generators - gen_core = read(f["generators/_core"]) - gen_names, gen_categories, gen_regionnames = readvector.( - Ref(gen_core), [:name, :category, :region]) + gen_names, gen_categories, gen_regionnames = + readvector.(Ref(gen_core), [:name, :category, :region]) gen_regions = getindex.(Ref(regionlookup), gen_regionnames) region_order = sortperm(gen_regions) - generators = Generators{N,L,T,P}( - gen_names[region_order], gen_categories[region_order], + generators = Generators{N, L, T, P}( + gen_names[region_order], + gen_categories[region_order], Int.(read(f["generators/capacity"]))[region_order, :], read(f["generators/failureprobability"])[region_order, :], - read(f["generators/repairprobability"])[region_order, :] + read(f["generators/repairprobability"])[region_order, :], ) region_gen_idxs = makeidxlist(gen_regions[region_order], n_regions) else - - generators = Generators{N,L,T,P}( - String[], String[], zeros(Int, 0, N), - zeros(Float64, 0, N), zeros(Float64, 0, N)) + generators = Generators{N, L, T, P}( + String[], + String[], + zeros(Int, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + ) region_gen_idxs = fill(1:0, n_regions) - end if has_storages - stor_core = read(f["storages/_core"]) - stor_names, stor_categories, stor_regionnames = readvector.( - Ref(stor_core), [:name, :category, :region]) + stor_names, stor_categories, stor_regionnames = + readvector.(Ref(stor_core), [:name, :category, :region]) stor_regions = getindex.(Ref(regionlookup), stor_regionnames) region_order = sortperm(stor_regions) - storages = Storages{N,L,T,P,E}( - stor_names[region_order], stor_categories[region_order], + storages = Storages{N, L, T, P, E}( + stor_names[region_order], + stor_categories[region_order], Int.(read(f["storages/chargecapacity"]))[region_order, :], Int.(read(f["storages/dischargecapacity"]))[region_order, :], Int.(read(f["storages/energycapacity"]))[region_order, :], @@ -111,35 +103,39 @@ function systemmodel_0_5(f::File) read(f["storages/dischargeefficiency"])[region_order, :], read(f["storages/carryoverefficiency"])[region_order, :], read(f["storages/failureprobability"])[region_order, :], - read(f["storages/repairprobability"])[region_order, :] + read(f["storages/repairprobability"])[region_order, :], ) region_stor_idxs = makeidxlist(stor_regions[region_order], n_regions) else - - storages = Storages{N,L,T,P,E}( - String[], String[], - zeros(Int, 0, N), zeros(Int, 0, N), zeros(Int, 0, N), - zeros(Float64, 0, N), zeros(Float64, 0, N), zeros(Float64, 0, N), - zeros(Float64, 0, N), zeros(Float64, 0, N)) + storages = Storages{N, L, T, P, E}( + String[], + String[], + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + ) region_stor_idxs = fill(1:0, n_regions) - end - if has_generatorstorages - genstor_core = read(f["generatorstorages/_core"]) - genstor_names, genstor_categories, genstor_regionnames = readvector.( - Ref(genstor_core), [:name, :category, :region]) + genstor_names, genstor_categories, genstor_regionnames = + readvector.(Ref(genstor_core), [:name, :category, :region]) genstor_regions = getindex.(Ref(regionlookup), genstor_regionnames) region_order = sortperm(genstor_regions) - generatorstorages = GeneratorStorages{N,L,T,P,E}( - genstor_names[region_order], genstor_categories[region_order], + generatorstorages = GeneratorStorages{N, L, T, P, E}( + genstor_names[region_order], + genstor_categories[region_order], Int.(read(f["generatorstorages/chargecapacity"]))[region_order, :], Int.(read(f["generatorstorages/dischargecapacity"]))[region_order, :], Int.(read(f["generatorstorages/energycapacity"]))[region_order, :], @@ -150,25 +146,32 @@ function systemmodel_0_5(f::File) Int.(read(f["generatorstorages/gridwithdrawalcapacity"]))[region_order, :], Int.(read(f["generatorstorages/gridinjectioncapacity"]))[region_order, :], read(f["generatorstorages/failureprobability"])[region_order, :], - read(f["generatorstorages/repairprobability"])[region_order, :]) + read(f["generatorstorages/repairprobability"])[region_order, :], + ) region_genstor_idxs = makeidxlist(genstor_regions[region_order], n_regions) else - - generatorstorages = GeneratorStorages{N,L,T,P,E}( - String[], String[], - zeros(Int, 0, N), zeros(Int, 0, N), zeros(Int, 0, N), - zeros(Float64, 0, N), zeros(Float64, 0, N), zeros(Float64, 0, N), - zeros(Int, 0, N), zeros(Int, 0, N), zeros(Int, 0, N), - zeros(Float64, 0, N), zeros(Float64, 0, N)) + generatorstorages = GeneratorStorages{N, L, T, P, E}( + String[], + String[], + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + ) region_genstor_idxs = fill(1:0, n_regions) - end if has_interfaces - interfaces_core = read(f["interfaces/_core"]) from_regionnames, to_regionnames = readvector.(Ref(interfaces_core), [:region_from, :region_to]) @@ -190,16 +193,19 @@ function systemmodel_0_5(f::File) backwardcapacity[i, :] .= forwardcapacity[i, :] forwardcapacity[i, :] .= new_forwardcapacity elseif from_region == to_region - error("Cannot have an interface to and from " * - "the same region ($(from_region))") + error( + "Cannot have an interface to and from " * + "the same region ($(from_region))", + ) end end - interfaces = Interfaces{N,P}( - from_regions, to_regions, forwardcapacity, backwardcapacity) + interfaces = + Interfaces{N, P}(from_regions, to_regions, forwardcapacity, backwardcapacity) - interface_lookup = Dict((r1, r2) => i for (i, (r1, r2)) - in enumerate(tuple.(from_regions, to_regions))) + interface_lookup = Dict( + (r1, r2) => i for (i, (r1, r2)) in enumerate(tuple.(from_regions, to_regions)) + ) lines_core = read(f["lines/_core"]) line_names, line_categories, line_fromregionnames, line_toregionnames = @@ -209,7 +215,7 @@ function systemmodel_0_5(f::File) n_lines = length(line_names) line_fromregions = getindex.(Ref(regionlookup), line_fromregionnames) - line_toregions = getindex.(Ref(regionlookup), line_toregionnames) + line_toregions = getindex.(Ref(regionlookup), line_toregionnames) # Force line definitions as smaller => larger region numbers for i in 1:n_lines @@ -222,45 +228,56 @@ function systemmodel_0_5(f::File) line_backwardcapacity[i, :] .= line_forwardcapacity[i, :] line_forwardcapacity[i, :] = new_forwardcapacity elseif from_region == to_region - error("Cannot have a line ($(line_names[i])) to and from " * - "the same region ($(from_region))") + error( + "Cannot have a line ($(line_names[i])) to and from " * + "the same region ($(from_region))", + ) end end - line_interfaces = getindex.(Ref(interface_lookup), - tuple.(line_fromregions, line_toregions)) + line_interfaces = + getindex.(Ref(interface_lookup), tuple.(line_fromregions, line_toregions)) interface_order = sortperm(line_interfaces) - lines = Lines{N,L,T,P}( - line_names[interface_order], line_categories[interface_order], + lines = Lines{N, L, T, P}( + line_names[interface_order], + line_categories[interface_order], line_forwardcapacity[interface_order, :], line_backwardcapacity[interface_order, :], read(f["lines/failureprobability"])[interface_order, :], - read(f["lines/repairprobability"])[interface_order, :]) + read(f["lines/repairprobability"])[interface_order, :], + ) interface_line_idxs = makeidxlist(line_interfaces[interface_order], n_interfaces) else - - interfaces = Interfaces{N,P}( - Int[], Int[], zeros(Int, 0, N), zeros(Int, 0, N)) - - lines = Lines{N,L,T,P}( - String[], String[], zeros(Int, 0, N), zeros(Int, 0, N), - zeros(Float64, 0, N), zeros(Float64, 0, N)) + interfaces = Interfaces{N, P}(Int[], Int[], zeros(Int, 0, N), zeros(Int, 0, N)) + + lines = Lines{N, L, T, P}( + String[], + String[], + zeros(Int, 0, N), + zeros(Int, 0, N), + zeros(Float64, 0, N), + zeros(Float64, 0, N), + ) interface_line_idxs = UnitRange{Int}[] - end return SystemModel( - regions, interfaces, - generators, region_gen_idxs, - storages, region_stor_idxs, - generatorstorages, region_genstor_idxs, - lines, interface_line_idxs, - timestamps) - + regions, + interfaces, + generators, + region_gen_idxs, + storages, + region_stor_idxs, + generatorstorages, + region_genstor_idxs, + lines, + interface_line_idxs, + timestamps, + ) end """ @@ -268,10 +285,10 @@ Attempts to parse the file's "vX.Y.Z" version label into (x::Int, y::Int, z::Int Errors if the label cannot be found or parsed as expected. """ function readversion(f::File) - haskey(attributes(f), "pras_dataversion") || error( - "File format version indicator could not be found - the file may " * - "not be a PRAS SystemModel representation.") + "File format version indicator could not be found - the file may " * + "not be a PRAS SystemModel representation.", + ) versionstring = read(attributes(f)["pras_dataversion"]) @@ -281,12 +298,11 @@ function readversion(f::File) major, minor, patch = parse.(Int, version.captures) return (major, minor, patch), versionstring - end """ Attempts to extract a vector of elements from an HDF5 compound datatype, corresponding to `field`. """ -readvector(d::Dataset, field::Union{Symbol,Int}) = readvector(read(d), field) -readvector(d::Vector{<:NamedTuple}, field::Union{Symbol,Int}) = getindex.(d, field) +readvector(d::Dataset, field::Union{Symbol, Int}) = readvector(read(d), field) +readvector(d::Vector{<:NamedTuple}, field::Union{Symbol, Int}) = getindex.(d, field) diff --git a/src/PRASBase/units.jl b/src/PRASBase/units.jl index 1b7bd8f5..08c517ab 100644 --- a/src/PRASBase/units.jl +++ b/src/PRASBase/units.jl @@ -17,9 +17,7 @@ conversionfactor(::Type{Hour}, ::Type{Hour}) = 1 conversionfactor(::Type{Hour}, ::Type{Day}) = 1 / 24 conversionfactor(::Type{Day}, ::Type{Hour}) = 24 -timeunits = Dict( - unitsymbol(T) => T - for T in [Minute, Hour, Day, Year]) +timeunits = Dict(unitsymbol(T) => T for T in [Minute, Hour, Day, Year]) # Define power units @@ -49,9 +47,7 @@ conversionfactor(::Type{GW}, ::Type{MW}) = 1000 conversionfactor(::Type{MW}, ::Type{TW}) = 1 / 1_000_000 conversionfactor(::Type{TW}, ::Type{MW}) = 1_000_000 -powerunits = Dict( - unitsymbol(T) => T - for T in [kW, MW, GW, TW]) +powerunits = Dict(unitsymbol(T) => T for T in [kW, MW, GW, TW]) # Define energy units @@ -72,12 +68,9 @@ subunits(::Type{MWh}) = (MW, Hour) subunits(::Type{GWh}) = (GW, Hour) subunits(::Type{TWh}) = (TW, Hour) -energyunits = Dict( - unitsymbol(T) => T - for T in [kWh, MWh, GWh, TWh]) +energyunits = Dict(unitsymbol(T) => T for T in [kWh, MWh, GWh, TWh]) function conversionfactor(F::Type{<:EnergyUnit}, T::Type{<:EnergyUnit}) - from_power, from_time = subunits(F) to_power, to_time = subunits(T) @@ -85,11 +78,14 @@ function conversionfactor(F::Type{<:EnergyUnit}, T::Type{<:EnergyUnit}) timeconversion = conversionfactor(from_time, to_time) return powerconversion * timeconversion - end function conversionfactor( - L::Int, T::Type{<:Period}, P::Type{<:PowerUnit}, E::Type{<:EnergyUnit}) + L::Int, + T::Type{<:Period}, + P::Type{<:PowerUnit}, + E::Type{<:EnergyUnit}, +) to_power, to_time = subunits(E) powerconversion = conversionfactor(P, to_power) timeconversion = conversionfactor(T, to_time) @@ -97,7 +93,11 @@ function conversionfactor( end function conversionfactor( - L::Int, T::Type{<:Period}, E::Type{<:EnergyUnit}, P::Type{<:PowerUnit}) + L::Int, + T::Type{<:Period}, + E::Type{<:EnergyUnit}, + P::Type{<:PowerUnit}, +) from_power, from_time = subunits(E) powerconversion = conversionfactor(from_power, P) timeconversion = conversionfactor(from_time, T) @@ -105,12 +105,17 @@ function conversionfactor( end powertoenergy( - p::Real, P::Type{<:PowerUnit}, - L::Real, T::Type{<:Period}, - E::Type{<:EnergyUnit}) = p*conversionfactor(L, T, P, E) + p::Real, + P::Type{<:PowerUnit}, + L::Real, + T::Type{<:Period}, + E::Type{<:EnergyUnit}, +) = p * conversionfactor(L, T, P, E) energytopower( - e::Real, E::Type{<:EnergyUnit}, - L::Real, T::Type{<:Period}, - P::Type{<:PowerUnit}) = e*conversionfactor(L, T, E, P) - + e::Real, + E::Type{<:EnergyUnit}, + L::Real, + T::Type{<:Period}, + P::Type{<:PowerUnit}, +) = e * conversionfactor(L, T, E, P) diff --git a/src/PRASBase/utils.jl b/src/PRASBase/utils.jl index a4f324b5..c61079e0 100644 --- a/src/PRASBase/utils.jl +++ b/src/PRASBase/utils.jl @@ -1,5 +1,4 @@ function makeidxlist(collectionidxs::Vector{Int}, n_collections::Int) - n_assets = length(collectionidxs) idxlist = Vector{UnitRange{Int}}(undef, n_collections) @@ -8,23 +7,22 @@ function makeidxlist(collectionidxs::Vector{Int}, n_collections::Int) a = 1 while a <= n_assets - if collectionidxs[a] > active_collection - idxlist[active_collection] = start_idx:(a-1) + if collectionidxs[a] > active_collection + idxlist[active_collection] = start_idx:(a - 1) active_collection += 1 start_idx = a - else - a += 1 - end + else + a += 1 + end end - idxlist[active_collection] = start_idx:n_assets + idxlist[active_collection] = start_idx:n_assets active_collection += 1 while active_collection <= n_collections - idxlist[active_collection] = (n_assets+1):n_assets + idxlist[active_collection] = (n_assets + 1):n_assets active_collection += 1 end return idxlist - end diff --git a/src/PRASBase/write.jl b/src/PRASBase/write.jl index c1ec757c..08e31156 100644 --- a/src/PRASBase/write.jl +++ b/src/PRASBase/write.jl @@ -4,14 +4,15 @@ savemodel(sys::SystemModel, outfile::String) -> nothing Export a PRAS SystemModel `sys` as a .pras file, saved to `outfile` """ function savemodel( - sys::SystemModel, outfile::String; - string_length::Int=64, compression_level::Int=1, verbose::Bool=false) - - verbose && - @info "The PRAS system being exported is of type $(typeof(sys))" + sys::SystemModel, + outfile::String; + string_length::Int=64, + compression_level::Int=1, + verbose::Bool=false, +) + verbose && @info "The PRAS system being exported is of type $(typeof(sys))" h5open(outfile, "w") do f::File - verbose && @info "Processing metadata for .pras file ..." process_metadata!(f, sys) @@ -43,37 +44,30 @@ function savemodel( verbose && @info "Processing Lines and Interfaces for .pras file ..." process_lines_interfaces!(f, sys, string_length, compression_level) end - end - verbose && @info "Successfully exported the PRAS SystemModel to " * - ".pras file " * outfile + verbose && + @info "Successfully exported the PRAS SystemModel to " * ".pras file " * outfile return - end -function process_metadata!( - f::File, sys::SystemModel{N,L,T,P,E}) where {N,L,T,P,E} - +function process_metadata!(f::File, sys::SystemModel{N, L, T, P, E}) where {N, L, T, P, E} attrs = attributes(f) - + attrs["timestep_count"] = N attrs["timestep_length"] = L attrs["timestep_unit"] = unitsymbol(T) attrs["power_unit"] = unitsymbol(P) attrs["energy_unit"] = unitsymbol(E) - attrs["start_timestamp"] = string(sys.timestamps.start); + attrs["start_timestamp"] = string(sys.timestamps.start) attrs["pras_dataversion"] = PRAS_VERSION return - end -function process_regions!( - f::File, sys::SystemModel, strlen::Int, compression::Int) - +function process_regions!(f::File, sys::SystemModel, strlen::Int, compression::Int) n_regions = length(sys.regions.names) regions = create_group(f, "regions") @@ -82,15 +76,12 @@ function process_regions!( string_table!(regions, "_core", regions_core_colnames, regions_core, strlen) - regions["load", deflate = compression] = sys.regions.load + regions["load", deflate=compression] = sys.regions.load return - end -function process_generators!( - f::File, sys::SystemModel, strlen::Int, compression::Int) - +function process_generators!(f::File, sys::SystemModel, strlen::Int, compression::Int) generators = create_group(f, "generators") gens_core = Matrix{String}(undef, length(sys.generators), 3) @@ -98,24 +89,21 @@ function process_generators!( gens_core[:, 1] = sys.generators.names gens_core[:, 2] = sys.generators.categories - gens_core[:, 3] = regionnames( - length(sys.generators), sys.regions.names, sys.region_gen_idxs) + gens_core[:, 3] = + regionnames(length(sys.generators), sys.regions.names, sys.region_gen_idxs) string_table!(generators, "_core", gens_core_colnames, gens_core, strlen) - generators["capacity", deflate = compression] = sys.generators.capacity + generators["capacity", deflate=compression] = sys.generators.capacity - generators["failureprobability", deflate = compression] = sys.generators.λ + generators["failureprobability", deflate=compression] = sys.generators.λ - generators["repairprobability", deflate = compression] = sys.generators.μ + generators["repairprobability", deflate=compression] = sys.generators.μ return - end -function process_storages!( - f::File, sys::SystemModel, strlen::Int, compression::Int) - +function process_storages!(f::File, sys::SystemModel, strlen::Int, compression::Int) storages = create_group(f, "storages") stors_core = Matrix{String}(undef, length(sys.storages), 3) @@ -123,40 +111,36 @@ function process_storages!( stors_core[:, 1] = sys.storages.names stors_core[:, 2] = sys.storages.categories - stors_core[:, 3] = regionnames( - length(sys.storages), sys.regions.names, sys.region_stor_idxs) + stors_core[:, 3] = + regionnames(length(sys.storages), sys.regions.names, sys.region_stor_idxs) string_table!(storages, "_core", stors_core_colnames, stors_core, strlen) - storages["chargecapacity", deflate = compression] = - sys.storages.charge_capacity + storages["chargecapacity", deflate=compression] = sys.storages.charge_capacity - storages["dischargecapacity", deflate = compression] = - sys.storages.discharge_capacity + storages["dischargecapacity", deflate=compression] = sys.storages.discharge_capacity - storages["energycapacity", deflate = compression] = - sys.storages.energy_capacity + storages["energycapacity", deflate=compression] = sys.storages.energy_capacity - storages["chargeefficiency", deflate = compression] = - sys.storages.charge_efficiency + storages["chargeefficiency", deflate=compression] = sys.storages.charge_efficiency - storages["dischargeefficiency", deflate = compression] = - sys.storages.discharge_efficiency + storages["dischargeefficiency", deflate=compression] = sys.storages.discharge_efficiency - storages["carryoverefficiency", deflate = compression] = - sys.storages.carryover_efficiency + storages["carryoverefficiency", deflate=compression] = sys.storages.carryover_efficiency - storages["failureprobability", deflate = compression] = sys.storages.λ + storages["failureprobability", deflate=compression] = sys.storages.λ - storages["repairprobability", deflate = compression] = sys.storages.μ + storages["repairprobability", deflate=compression] = sys.storages.μ return - end function process_generatorstorages!( - f::File, sys::SystemModel, strlen::Int, compression::Int) - + f::File, + sys::SystemModel, + strlen::Int, + compression::Int, +) generatorstorages = create_group(f, "generatorstorages") genstors_core = Matrix{String}(undef, length(sys.generatorstorages), 3) @@ -165,51 +149,47 @@ function process_generatorstorages!( genstors_core[:, 1] = sys.generatorstorages.names genstors_core[:, 2] = sys.generatorstorages.categories genstors_core[:, 3] = regionnames( - length(sys.generatorstorages), sys.regions.names, sys.region_genstor_idxs) + length(sys.generatorstorages), + sys.regions.names, + sys.region_genstor_idxs, + ) - string_table!(generatorstorages, "_core", - genstors_core_colnames, genstors_core, strlen) + string_table!(generatorstorages, "_core", genstors_core_colnames, genstors_core, strlen) - generatorstorages["inflow", deflate = compression] = - sys.generatorstorages.inflow + generatorstorages["inflow", deflate=compression] = sys.generatorstorages.inflow - generatorstorages["gridwithdrawalcapacity", deflate = compression] = + generatorstorages["gridwithdrawalcapacity", deflate=compression] = sys.generatorstorages.gridwithdrawal_capacity - generatorstorages["gridinjectioncapacity", deflate = compression] = + generatorstorages["gridinjectioncapacity", deflate=compression] = sys.generatorstorages.gridinjection_capacity - generatorstorages["chargecapacity", deflate = compression] = + generatorstorages["chargecapacity", deflate=compression] = sys.generatorstorages.charge_capacity - generatorstorages["dischargecapacity", deflate = compression] = + generatorstorages["dischargecapacity", deflate=compression] = sys.generatorstorages.discharge_capacity - generatorstorages["energycapacity", deflate = compression] = + generatorstorages["energycapacity", deflate=compression] = sys.generatorstorages.energy_capacity - generatorstorages["chargeefficiency", deflate = compression] = + generatorstorages["chargeefficiency", deflate=compression] = sys.generatorstorages.charge_efficiency - generatorstorages["dischargeefficiency", deflate = compression] = + generatorstorages["dischargeefficiency", deflate=compression] = sys.generatorstorages.discharge_efficiency - generatorstorages["carryoverefficiency", deflate = compression] = + generatorstorages["carryoverefficiency", deflate=compression] = sys.generatorstorages.carryover_efficiency - generatorstorages["failureprobability", deflate = compression] = - sys.generatorstorages.λ + generatorstorages["failureprobability", deflate=compression] = sys.generatorstorages.λ - generatorstorages["repairprobability", deflate = compression] = - sys.generatorstorages.μ + generatorstorages["repairprobability", deflate=compression] = sys.generatorstorages.μ return - end -function process_lines_interfaces!( - f::File, sys::SystemModel, strlen::Int, compression::Int) - +function process_lines_interfaces!(f::File, sys::SystemModel, strlen::Int, compression::Int) lines = create_group(f, "lines") lines_core = Matrix{String}(undef, length(sys.lines), 4) @@ -217,71 +197,64 @@ function process_lines_interfaces!( lines_core[:, 1] = sys.lines.names lines_core[:, 2] = sys.lines.categories - for (lines, r_from, r_to) in zip(sys.interface_line_idxs, - sys.interfaces.regions_from, - sys.interfaces.regions_to) + for (lines, r_from, r_to) in + zip(sys.interface_line_idxs, sys.interfaces.regions_from, sys.interfaces.regions_to) lines_core[lines, 3] .= sys.regions.names[r_from] lines_core[lines, 4] .= sys.regions.names[r_to] end string_table!(lines, "_core", lines_core_colnames, lines_core, strlen) - lines["forwardcapacity", deflate = compression] = - sys.lines.forward_capacity + lines["forwardcapacity", deflate=compression] = sys.lines.forward_capacity - lines["backwardcapacity", deflate = compression] = - sys.lines.backward_capacity + lines["backwardcapacity", deflate=compression] = sys.lines.backward_capacity - lines["failureprobability", deflate = compression] = sys.lines.λ - - lines["repairprobability", deflate = compression] = sys.lines.μ + lines["failureprobability", deflate=compression] = sys.lines.λ + lines["repairprobability", deflate=compression] = sys.lines.μ interfaces = create_group(f, "interfaces") ints_core = Matrix{String}(undef, length(sys.interfaces), 2) ints_core_colnames = ["region_from", "region_to"] - ints_core[:, 1] = - getindex.(Ref(sys.regions.names), sys.interfaces.regions_from) - ints_core[:, 2] = - getindex.(Ref(sys.regions.names), sys.interfaces.regions_to) + ints_core[:, 1] = getindex.(Ref(sys.regions.names), sys.interfaces.regions_from) + ints_core[:, 2] = getindex.(Ref(sys.regions.names), sys.interfaces.regions_to) string_table!(interfaces, "_core", ints_core_colnames, ints_core, strlen) - interfaces["forwardcapacity", deflate = compression] = - sys.interfaces.limit_forward + interfaces["forwardcapacity", deflate=compression] = sys.interfaces.limit_forward - interfaces["backwardcapacity", deflate = compression] = - sys.interfaces.limit_backward + interfaces["backwardcapacity", deflate=compression] = sys.interfaces.limit_backward return - end function regionnames( - n_units::Int, regions::Vector{String}, unit_idxs::Vector{UnitRange{Int}}) - + n_units::Int, + regions::Vector{String}, + unit_idxs::Vector{UnitRange{Int}}, +) result = Vector{String}(undef, n_units) for (r, units) in enumerate(unit_idxs) result[units] .= regions[r] end return result - end function string_table!( - f::Group, tablename::String, colnames::Vector{String}, - data::Matrix{String}, strlen::Int + f::Group, + tablename::String, + colnames::Vector{String}, + data::Matrix{String}, + strlen::Int, ) - nrows, ncols = size(data) length(colnames) == ncols || error("Number of column names does not match provided data") - allunique(colnames) || - error("All column names must be unique") + allunique(colnames) || error("All column names must be unique") all(x -> x <= strlen, length.(data)) || error("Input data exceeds the specified HDF5 string length") @@ -292,20 +265,16 @@ function string_table!( dt_id = h5t_create(H5T_COMPOUND, ncols * strlen) for (i, colname) in enumerate(colnames) - h5t_insert(dt_id, colname, (i-1)*strlen, stringtype) + h5t_insert(dt_id, colname, (i - 1) * strlen, stringtype) end rawdata = UInt8.(vcat(vec(convertstring.(permutedims(data), strlen))...)) - dset = create_dataset(f, tablename, Datatype(dt_id), - dataspace((nrows,))) - h5d_write( - dset, dt_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, rawdata) - + dset = create_dataset(f, tablename, Datatype(dt_id), dataspace((nrows,))) + h5d_write(dset, dt_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, rawdata) end function convertstring(s::AbstractString, strlen::Int) - oldstring = ascii(s) newstring = fill('\0', strlen) @@ -314,5 +283,4 @@ function convertstring(s::AbstractString, strlen::Int) end return newstring - end diff --git a/src/ResourceAdequacy/ResourceAdequacy.jl b/src/ResourceAdequacy/ResourceAdequacy.jl index 05a78735..19f9df3a 100644 --- a/src/ResourceAdequacy/ResourceAdequacy.jl +++ b/src/ResourceAdequacy/ResourceAdequacy.jl @@ -16,32 +16,45 @@ import Random123: Philox4x import StatsBase: mean, std, stderror import TimeZones: ZonedDateTime, @tz_str -export - - assess, +export assess, # Metrics - ReliabilityMetric, LOLE, EUE, - val, stderror, + ReliabilityMetric, + LOLE, + EUE, + val, + stderror, # Simulation specifications - Convolution, SequentialMonteCarlo, + Convolution, + SequentialMonteCarlo, # Result specifications - Shortfall, ShortfallSamples, Surplus, SurplusSamples, - Flow, FlowSamples, Utilization, UtilizationSamples, - StorageEnergy, StorageEnergySamples, - GeneratorStorageEnergy, GeneratorStorageEnergySamples, - GeneratorAvailability, StorageAvailability, - GeneratorStorageAvailability, LineAvailability, + Shortfall, + ShortfallSamples, + Surplus, + SurplusSamples, + Flow, + FlowSamples, + Utilization, + UtilizationSamples, + StorageEnergy, + StorageEnergySamples, + GeneratorStorageEnergy, + GeneratorStorageEnergySamples, + GeneratorAvailability, + StorageAvailability, + GeneratorStorageAvailability, + LineAvailability, # Convenience re-exports - ZonedDateTime, @tz_str + ZonedDateTime, + @tz_str abstract type ReliabilityMetric end abstract type SimulationSpec end abstract type ResultSpec end -abstract type ResultAccumulator{S<:SimulationSpec,R<:ResultSpec} end +abstract type ResultAccumulator{S <: SimulationSpec, R <: ResultSpec} end abstract type Result{ N, # Number of timesteps simulated L, # Length of each simulation timestep @@ -49,8 +62,9 @@ abstract type Result{ } end MeanVariance = Series{ - Number, Tuple{Mean{Float64, EqualWeight}, - Variance{Float64, Float64, EqualWeight}}} + Number, + Tuple{Mean{Float64, EqualWeight}, Variance{Float64, Float64, EqualWeight}}, +} include("metrics.jl") include("results/results.jl") diff --git a/src/ResourceAdequacy/metrics.jl b/src/ResourceAdequacy/metrics.jl index fc8aa6b1..159b6d5b 100644 --- a/src/ResourceAdequacy/metrics.jl +++ b/src/ResourceAdequacy/metrics.jl @@ -1,17 +1,14 @@ struct MeanEstimate - estimate::Float64 standarderror::Float64 function MeanEstimate(est::Real, stderr::Real) - - stderr >= 0 || throw(DomainError(stderr, - "Standard error of the estimate should be non-negative")) + stderr >= 0 || throw( + DomainError(stderr, "Standard error of the estimate should be non-negative"), + ) new(convert(Float64, est), convert(Float64, stderr)) - end - end MeanEstimate(x::Real) = MeanEstimate(x, 0) @@ -27,88 +24,89 @@ val(est::MeanEstimate) = est.estimate stderror(est::MeanEstimate) = est.standarderror Base.isapprox(x::MeanEstimate, y::MeanEstimate) = - isapprox(x.estimate, y.estimate) && - isapprox(x.standarderror, y.standarderror) + isapprox(x.estimate, y.estimate) && isapprox(x.standarderror, y.standarderror) function Base.show(io::IO, x::MeanEstimate) v, s = stringprecision(x) - print(io, v, x.standarderror > 0 ? "±"*s : "") + print(io, v, x.standarderror > 0 ? "±" * s : "") end function stringprecision(x::MeanEstimate) - if iszero(x.standarderror) - v_rounded = @sprintf "%0.5f" x.estimate s_rounded = "0" else - stderr_round = round(x.standarderror, sigdigits=1) digits = floor(Int, log(10, stderr_round)) rounded = round(x.estimate, digits=-digits) - reduced = round(Int, rounded / 10. ^ digits) + reduced = round(Int, rounded / 10.0^digits) v_rounded = string(Decimal(Int(x.estimate < 0), abs(reduced), digits)) s_rounded = string(decimal(stderr_round)) - end return v_rounded, s_rounded - end Base.isapprox(x::ReliabilityMetric, y::ReliabilityMetric) = - isapprox(val(x), val(y)) && isapprox(stderror(x), stderror(y)) + isapprox(val(x), val(y)) && isapprox(stderror(x), stderror(y)) # Loss-of-Load Expectation -struct LOLE{N,L,T<:Period} <: ReliabilityMetric - +struct LOLE{N, L, T <: Period} <: ReliabilityMetric lole::MeanEstimate - function LOLE{N,L,T}(lole::MeanEstimate) where {N,L,T<:Period} - val(lole) >= 0 || throw(DomainError(val, - "$val is not a valid expected count of event-periods")) - new{N,L,T}(lole) + function LOLE{N, L, T}(lole::MeanEstimate) where {N, L, T <: Period} + val(lole) >= 0 || + throw(DomainError(val, "$val is not a valid expected count of event-periods")) + new{N, L, T}(lole) end - end val(x::LOLE) = val(x.lole) stderror(x::LOLE) = stderror(x.lole) -function Base.show(io::IO, x::LOLE{N,L,T}) where {N,L,T} - +function Base.show(io::IO, x::LOLE{N, L, T}) where {N, L, T} t_symbol = unitsymbol(T) - print(io, "LOLE = ", x.lole, " event-", - L == 1 ? t_symbol : "(" * string(L) * t_symbol * ")", "/", - N*L == 1 ? "" : N*L, t_symbol) - + print( + io, + "LOLE = ", + x.lole, + " event-", + L == 1 ? t_symbol : "(" * string(L) * t_symbol * ")", + "/", + N * L == 1 ? "" : N * L, + t_symbol, + ) end # Expected Unserved Energy -struct EUE{N,L,T<:Period,E<:EnergyUnit} <: ReliabilityMetric - +struct EUE{N, L, T <: Period, E <: EnergyUnit} <: ReliabilityMetric eue::MeanEstimate - function EUE{N,L,T,E}(eue::MeanEstimate) where {N,L,T<:Period,E<:EnergyUnit} - val(eue) >= 0 || throw(DomainError( - "$val is not a valid unserved energy expectation")) - new{N,L,T,E}(eue) + function EUE{N, L, T, E}(eue::MeanEstimate) where {N, L, T <: Period, E <: EnergyUnit} + val(eue) >= 0 || + throw(DomainError("$val is not a valid unserved energy expectation")) + new{N, L, T, E}(eue) end - end val(x::EUE) = val(x.eue) stderror(x::EUE) = stderror(x.eue) -function Base.show(io::IO, x::EUE{N,L,T,E}) where {N,L,T,E} - - print(io, "EUE = ", x.eue, " ", - unitsymbol(E), "/", N*L == 1 ? "" : N*L, unitsymbol(T)) - +function Base.show(io::IO, x::EUE{N, L, T, E}) where {N, L, T, E} + print( + io, + "EUE = ", + x.eue, + " ", + unitsymbol(E), + "/", + N * L == 1 ? "" : N * L, + unitsymbol(T), + ) end diff --git a/src/ResourceAdequacy/results/availability.jl b/src/ResourceAdequacy/results/availability.jl index 774513be..699372b2 100644 --- a/src/ResourceAdequacy/results/availability.jl +++ b/src/ResourceAdequacy/results/availability.jl @@ -1,4 +1,4 @@ -abstract type AbstractAvailabilityResult{N,L,T} <: Result{N,L,T} end +abstract type AbstractAvailabilityResult{N, L, T} <: Result{N, L, T} end # Colon indexing @@ -15,13 +15,11 @@ getindex(x::AbstractAvailabilityResult, ::Colon, ::Colon) = struct GeneratorAvailability <: ResultSpec end -struct GeneratorAvailabilityResult{N,L,T<:Period} <: AbstractAvailabilityResult{N,L,T} - +struct GeneratorAvailabilityResult{N, L, T <: Period} <: AbstractAvailabilityResult{N, L, T} generators::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - available::Array{Bool,3} + timestamps::StepRange{ZonedDateTime, T} + available::Array{Bool, 3} end names(x::GeneratorAvailabilityResult) = x.generators @@ -36,13 +34,11 @@ end struct StorageAvailability <: ResultSpec end -struct StorageAvailabilityResult{N,L,T<:Period} <: AbstractAvailabilityResult{N,L,T} - +struct StorageAvailabilityResult{N, L, T <: Period} <: AbstractAvailabilityResult{N, L, T} storages::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - available::Array{Bool,3} + timestamps::StepRange{ZonedDateTime, T} + available::Array{Bool, 3} end names(x::StorageAvailabilityResult) = x.storages @@ -57,18 +53,21 @@ end struct GeneratorStorageAvailability <: ResultSpec end -struct GeneratorStorageAvailabilityResult{N,L,T<:Period} <: AbstractAvailabilityResult{N,L,T} - +struct GeneratorStorageAvailabilityResult{N, L, T <: Period} <: + AbstractAvailabilityResult{N, L, T} generatorstorages::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - available::Array{Bool,3} + timestamps::StepRange{ZonedDateTime, T} + available::Array{Bool, 3} end names(x::GeneratorStorageAvailabilityResult) = x.generatorstorages -function getindex(x::GeneratorStorageAvailabilityResult, gs::AbstractString, t::ZonedDateTime) +function getindex( + x::GeneratorStorageAvailabilityResult, + gs::AbstractString, + t::ZonedDateTime, +) i_gs = findfirstunique(x.generatorstorages, gs) i_t = findfirstunique(x.timestamps, t) return vec(x.available[i_gs, i_t, :]) @@ -78,13 +77,11 @@ end struct LineAvailability <: ResultSpec end -struct LineAvailabilityResult{N,L,T<:Period} <: AbstractAvailabilityResult{N,L,T} - +struct LineAvailabilityResult{N, L, T <: Period} <: AbstractAvailabilityResult{N, L, T} lines::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - available::Array{Bool,3} + timestamps::StepRange{ZonedDateTime, T} + available::Array{Bool, 3} end names(x::LineAvailabilityResult) = x.lines diff --git a/src/ResourceAdequacy/results/energy.jl b/src/ResourceAdequacy/results/energy.jl index b121c4a8..689dec6d 100644 --- a/src/ResourceAdequacy/results/energy.jl +++ b/src/ResourceAdequacy/results/energy.jl @@ -1,15 +1,12 @@ -abstract type AbstractEnergyResult{N,L,T} <: Result{N,L,T} end +abstract type AbstractEnergyResult{N, L, T} <: Result{N, L, T} end # Colon indexing -getindex(x::AbstractEnergyResult, ::Colon) = - getindex.(x, x.timestamps) +getindex(x::AbstractEnergyResult, ::Colon) = getindex.(x, x.timestamps) -getindex(x::AbstractEnergyResult, ::Colon, t::ZonedDateTime) = - getindex.(x, names(x), t) +getindex(x::AbstractEnergyResult, ::Colon, t::ZonedDateTime) = getindex.(x, names(x), t) -getindex(x::AbstractEnergyResult, name::String, ::Colon) = - getindex.(x, name, x.timestamps) +getindex(x::AbstractEnergyResult, name::String, ::Colon) = getindex.(x, name, x.timestamps) getindex(x::AbstractEnergyResult, ::Colon, ::Colon) = getindex.(x, names(x), permutedims(x.timestamps)) @@ -18,17 +15,16 @@ getindex(x::AbstractEnergyResult, ::Colon, ::Colon) = struct StorageEnergy <: ResultSpec end -struct StorageEnergyResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractEnergyResult{N,L,T} - - nsamples::Union{Int,Nothing} +struct StorageEnergyResult{N, L, T <: Period, E <: EnergyUnit} <: + AbstractEnergyResult{N, L, T} + nsamples::Union{Int, Nothing} storages::Vector{String} - timestamps::StepRange{ZonedDateTime,T} + timestamps::StepRange{ZonedDateTime, T} energy_mean::Matrix{Float64} energy_period_std::Vector{Float64} energy_regionperiod_std::Matrix{Float64} - end names(x::StorageEnergyResult) = x.storages @@ -48,17 +44,16 @@ end struct GeneratorStorageEnergy <: ResultSpec end -struct GeneratorStorageEnergyResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractEnergyResult{N,L,T} - - nsamples::Union{Int,Nothing} +struct GeneratorStorageEnergyResult{N, L, T <: Period, E <: EnergyUnit} <: + AbstractEnergyResult{N, L, T} + nsamples::Union{Int, Nothing} generatorstorages::Vector{String} - timestamps::StepRange{ZonedDateTime,T} + timestamps::StepRange{ZonedDateTime, T} energy_mean::Matrix{Float64} energy_period_std::Vector{Float64} energy_regionperiod_std::Matrix{Float64} - end names(x::GeneratorStorageEnergyResult) = x.generatorstorages @@ -78,13 +73,12 @@ end struct StorageEnergySamples <: ResultSpec end -struct StorageEnergySamplesResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractEnergyResult{N,L,T} - +struct StorageEnergySamplesResult{N, L, T <: Period, E <: EnergyUnit} <: + AbstractEnergyResult{N, L, T} storages::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - energy::Array{Int,3} + timestamps::StepRange{ZonedDateTime, T} + energy::Array{Int, 3} end names(x::StorageEnergySamplesResult) = x.storages @@ -104,13 +98,12 @@ end struct GeneratorStorageEnergySamples <: ResultSpec end -struct GeneratorStorageEnergySamplesResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractEnergyResult{N,L,T} - +struct GeneratorStorageEnergySamplesResult{N, L, T <: Period, E <: EnergyUnit} <: + AbstractEnergyResult{N, L, T} generatorstorages::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - energy::Array{Int,3} + timestamps::StepRange{ZonedDateTime, T} + energy::Array{Int, 3} end names(x::GeneratorStorageEnergySamplesResult) = x.generatorstorages @@ -120,7 +113,11 @@ function getindex(x::GeneratorStorageEnergySamplesResult, t::ZonedDateTime) return vec(sum(view(x.energy, :, i_t, :), dims=1)) end -function getindex(x::GeneratorStorageEnergySamplesResult, gs::AbstractString, t::ZonedDateTime) +function getindex( + x::GeneratorStorageEnergySamplesResult, + gs::AbstractString, + t::ZonedDateTime, +) i_gs = findfirstunique(x.generatorstorages, gs) i_t = findfirstunique(x.timestamps, t) return vec(x.energy[i_gs, i_t, :]) diff --git a/src/ResourceAdequacy/results/flow.jl b/src/ResourceAdequacy/results/flow.jl index d8c11fca..4e337731 100644 --- a/src/ResourceAdequacy/results/flow.jl +++ b/src/ResourceAdequacy/results/flow.jl @@ -1,15 +1,13 @@ struct Flow <: ResultSpec end -abstract type AbstractFlowResult{N,L,T} <: Result{N,L,T} end +abstract type AbstractFlowResult{N, L, T} <: Result{N, L, T} end # Colon indexing -getindex(x::AbstractFlowResult, ::Colon) = - getindex.(x, x.interfaces) +getindex(x::AbstractFlowResult, ::Colon) = getindex.(x, x.interfaces) -getindex(x::AbstractFlowResult, ::Colon, t::ZonedDateTime) = - getindex.(x, x.interfaces, t) +getindex(x::AbstractFlowResult, ::Colon, t::ZonedDateTime) = getindex.(x, x.interfaces, t) -getindex(x::AbstractFlowResult, i::Pair{<:AbstractString,<:AbstractString}, ::Colon) = +getindex(x::AbstractFlowResult, i::Pair{<:AbstractString, <:AbstractString}, ::Colon) = getindex.(x, i, x.timestamps) getindex(x::AbstractFlowResult, ::Colon, ::Colon) = @@ -17,26 +15,28 @@ getindex(x::AbstractFlowResult, ::Colon, ::Colon) = # Sample-averaged flow data -struct FlowResult{N,L,T<:Period,P<:PowerUnit} <: AbstractFlowResult{N,L,T} - - nsamples::Union{Int,Nothing} - interfaces::Vector{Pair{String,String}} - timestamps::StepRange{ZonedDateTime,T} +struct FlowResult{N, L, T <: Period, P <: PowerUnit} <: AbstractFlowResult{N, L, T} + nsamples::Union{Int, Nothing} + interfaces::Vector{Pair{String, String}} + timestamps::StepRange{ZonedDateTime, T} flow_mean::Matrix{Float64} flow_interface_std::Vector{Float64} flow_interfaceperiod_std::Matrix{Float64} - end -function getindex(x::FlowResult, i::Pair{<:AbstractString,<:AbstractString}) +function getindex(x::FlowResult, i::Pair{<:AbstractString, <:AbstractString}) i_i, reverse = findfirstunique_directional(x.interfaces, i) flow = mean(view(x.flow_mean, i_i, :)) return reverse ? -flow : flow, x.flow_interface_std[i_i] end -function getindex(x::FlowResult, i::Pair{<:AbstractString,<:AbstractString}, t::ZonedDateTime) +function getindex( + x::FlowResult, + i::Pair{<:AbstractString, <:AbstractString}, + t::ZonedDateTime, +) i_i, reverse = findfirstunique_directional(x.interfaces, i) i_t = findfirstunique(x.timestamps, t) flow = x.flow_mean[i_i, i_t] @@ -47,23 +47,24 @@ end struct FlowSamples <: ResultSpec end -struct FlowSamplesResult{N,L,T<:Period,P<:PowerUnit} <: AbstractFlowResult{N,L,T} - - interfaces::Vector{Pair{String,String}} - timestamps::StepRange{ZonedDateTime,T} - - flow::Array{Int,3} +struct FlowSamplesResult{N, L, T <: Period, P <: PowerUnit} <: AbstractFlowResult{N, L, T} + interfaces::Vector{Pair{String, String}} + timestamps::StepRange{ZonedDateTime, T} + flow::Array{Int, 3} end -function getindex(x::FlowSamplesResult, i::Pair{<:AbstractString,<:AbstractString}) +function getindex(x::FlowSamplesResult, i::Pair{<:AbstractString, <:AbstractString}) i_i, reverse = findfirstunique_directional(x.interfaces, i) flow = vec(mean(view(x.flow, i_i, :, :), dims=1)) return reverse ? -flow : flow end - -function getindex(x::FlowSamplesResult, i::Pair{<:AbstractString,<:AbstractString}, t::ZonedDateTime) +function getindex( + x::FlowSamplesResult, + i::Pair{<:AbstractString, <:AbstractString}, + t::ZonedDateTime, +) i_i, reverse = findfirstunique_directional(x.interfaces, i) i_t = findfirstunique(x.timestamps, t) flow = vec(x.flow[i_i, i_t, :]) diff --git a/src/ResourceAdequacy/results/results.jl b/src/ResourceAdequacy/results/results.jl index 895da22f..ce3ee6fd 100644 --- a/src/ResourceAdequacy/results/results.jl +++ b/src/ResourceAdequacy/results/results.jl @@ -9,23 +9,21 @@ include("availability.jl") include("energy.jl") function resultchannel( - method::SimulationSpec, results::T, threads::Int -) where T <: Tuple{Vararg{ResultSpec}} - + method::SimulationSpec, + results::T, + threads::Int, +) where {T <: Tuple{Vararg{ResultSpec}}} types = accumulatortype.(method, results) return Channel{Tuple{types...}}(threads) - end -merge!(xs::T, ys::T) where T <: Tuple{Vararg{ResultAccumulator}} = - foreach(merge!, xs, ys) +merge!(xs::T, ys::T) where {T <: Tuple{Vararg{ResultAccumulator}}} = foreach(merge!, xs, ys) function finalize( results::Channel{<:Tuple{Vararg{ResultAccumulator}}}, - system::SystemModel{N,L,T,P,E}, - threads::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + threads::Int, +) where {N, L, T, P, E} total_result = take!(results) for _ in 2:threads @@ -35,5 +33,4 @@ function finalize( close(results) return finalize.(total_result, system) - end diff --git a/src/ResourceAdequacy/results/shortfall.jl b/src/ResourceAdequacy/results/shortfall.jl index 2eb89357..85313274 100644 --- a/src/ResourceAdequacy/results/shortfall.jl +++ b/src/ResourceAdequacy/results/shortfall.jl @@ -1,10 +1,9 @@ struct Shortfall <: ResultSpec end -abstract type AbstractShortfallResult{N,L,T} <: Result{N,L,T} end +abstract type AbstractShortfallResult{N, L, T} <: Result{N, L, T} end # Colon indexing -getindex(x::AbstractShortfallResult, ::Colon, t::ZonedDateTime) = - getindex.(x, x.regions, t) +getindex(x::AbstractShortfallResult, ::Colon, t::ZonedDateTime) = getindex.(x, x.regions, t) getindex(x::AbstractShortfallResult, r::AbstractString, ::Colon) = getindex.(x, r, x.timestamps) @@ -12,33 +11,27 @@ getindex(x::AbstractShortfallResult, r::AbstractString, ::Colon) = getindex(x::AbstractShortfallResult, ::Colon, ::Colon) = getindex.(x, x.regions, permutedims(x.timestamps)) +LOLE(x::AbstractShortfallResult, ::Colon, t::ZonedDateTime) = LOLE.(x, x.regions, t) -LOLE(x::AbstractShortfallResult, ::Colon, t::ZonedDateTime) = - LOLE.(x, x.regions, t) - -LOLE(x::AbstractShortfallResult, r::AbstractString, ::Colon) = - LOLE.(x, r, x.timestamps) +LOLE(x::AbstractShortfallResult, r::AbstractString, ::Colon) = LOLE.(x, r, x.timestamps) LOLE(x::AbstractShortfallResult, ::Colon, ::Colon) = LOLE.(x, x.regions, permutedims(x.timestamps)) +EUE(x::AbstractShortfallResult, ::Colon, t::ZonedDateTime) = EUE.(x, x.regions, t) -EUE(x::AbstractShortfallResult, ::Colon, t::ZonedDateTime) = - EUE.(x, x.regions, t) - -EUE(x::AbstractShortfallResult, r::AbstractString, ::Colon) = - EUE.(x, r, x.timestamps) +EUE(x::AbstractShortfallResult, r::AbstractString, ::Colon) = EUE.(x, r, x.timestamps) EUE(x::AbstractShortfallResult, ::Colon, ::Colon) = EUE.(x, x.regions, permutedims(x.timestamps)) # Sample-averaged shortfall data -struct ShortfallResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractShortfallResult{N,L,T} - - nsamples::Union{Int,Nothing} +struct ShortfallResult{N, L, T <: Period, E <: EnergyUnit} <: + AbstractShortfallResult{N, L, T} + nsamples::Union{Int, Nothing} regions::Vector{String} - timestamps::StepRange{ZonedDateTime,T} + timestamps::StepRange{ZonedDateTime, T} eventperiod_mean::Float64 eventperiod_std::Float64 @@ -52,7 +45,6 @@ struct ShortfallResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractShortfallResult{N eventperiod_regionperiod_mean::Matrix{Float64} eventperiod_regionperiod_std::Matrix{Float64} - shortfall_mean::Matrix{Float64} # r x t shortfall_std::Float64 @@ -60,10 +52,10 @@ struct ShortfallResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractShortfallResult{N shortfall_period_std::Vector{Float64} shortfall_regionperiod_std::Matrix{Float64} - function ShortfallResult{N,L,T,E}( - nsamples::Union{Int,Nothing}, + function ShortfallResult{N, L, T, E}( + nsamples::Union{Int, Nothing}, regions::Vector{String}, - timestamps::StepRange{ZonedDateTime,T}, + timestamps::StepRange{ZonedDateTime, T}, eventperiod_mean::Float64, eventperiod_std::Float64, eventperiod_region_mean::Vector{Float64}, @@ -76,40 +68,47 @@ struct ShortfallResult{N,L,T<:Period,E<:EnergyUnit} <: AbstractShortfallResult{N shortfall_std::Float64, shortfall_region_std::Vector{Float64}, shortfall_period_std::Vector{Float64}, - shortfall_regionperiod_std::Matrix{Float64} - ) where {N,L,T<:Period,E<:EnergyUnit} - - isnothing(nsamples) || nsamples > 0 || + shortfall_regionperiod_std::Matrix{Float64}, + ) where {N, L, T <: Period, E <: EnergyUnit} + isnothing(nsamples) || + nsamples > 0 || throw(DomainError("Sample count must be positive or `nothing`.")) - length(timestamps) == N || error("The provided timestamp range does not match the simulation length") nregions = length(regions) length(eventperiod_region_mean) == nregions && - length(eventperiod_region_std) == nregions && - length(eventperiod_period_mean) == N && - length(eventperiod_period_std) == N && - size(eventperiod_regionperiod_mean) == (nregions, N) && - size(eventperiod_regionperiod_std) == (nregions, N) && - length(shortfall_region_std) == nregions && - length(shortfall_period_std) == N && - size(shortfall_regionperiod_std) == (nregions, N) || + length(eventperiod_region_std) == nregions && + length(eventperiod_period_mean) == N && + length(eventperiod_period_std) == N && + size(eventperiod_regionperiod_mean) == (nregions, N) && + size(eventperiod_regionperiod_std) == (nregions, N) && + length(shortfall_region_std) == nregions && + length(shortfall_period_std) == N && + size(shortfall_regionperiod_std) == (nregions, N) || error("Inconsistent input data sizes") - new{N,L,T,E}(nsamples, regions, timestamps, - eventperiod_mean, eventperiod_std, - eventperiod_region_mean, eventperiod_region_std, - eventperiod_period_mean, eventperiod_period_std, - eventperiod_regionperiod_mean, eventperiod_regionperiod_std, - shortfall_mean, shortfall_std, - shortfall_region_std, shortfall_period_std, - shortfall_regionperiod_std) - + new{N, L, T, E}( + nsamples, + regions, + timestamps, + eventperiod_mean, + eventperiod_std, + eventperiod_region_mean, + eventperiod_region_std, + eventperiod_period_mean, + eventperiod_period_std, + eventperiod_regionperiod_mean, + eventperiod_regionperiod_std, + shortfall_mean, + shortfall_std, + shortfall_region_std, + shortfall_period_std, + shortfall_regionperiod_std, + ) end - end function getindex(x::ShortfallResult) @@ -132,126 +131,147 @@ function getindex(x::ShortfallResult, r::AbstractString, t::ZonedDateTime) return x.shortfall_mean[i_r, i_t], x.shortfall_regionperiod_std[i_r, i_t] end +LOLE(x::ShortfallResult{N, L, T}) where {N, L, T} = + LOLE{N, L, T}(MeanEstimate(x.eventperiod_mean, x.eventperiod_std, x.nsamples)) -LOLE(x::ShortfallResult{N,L,T}) where {N,L,T} = - LOLE{N,L,T}(MeanEstimate(x.eventperiod_mean, - x.eventperiod_std, - x.nsamples)) - -function LOLE(x::ShortfallResult{N,L,T}, r::AbstractString) where {N,L,T} +function LOLE(x::ShortfallResult{N, L, T}, r::AbstractString) where {N, L, T} i_r = findfirstunique(x.regions, r) - return LOLE{N,L,T}(MeanEstimate(x.eventperiod_region_mean[i_r], - x.eventperiod_region_std[i_r], - x.nsamples)) + return LOLE{N, L, T}( + MeanEstimate( + x.eventperiod_region_mean[i_r], + x.eventperiod_region_std[i_r], + x.nsamples, + ), + ) end -function LOLE(x::ShortfallResult{N,L,T}, t::ZonedDateTime) where {N,L,T} +function LOLE(x::ShortfallResult{N, L, T}, t::ZonedDateTime) where {N, L, T} i_t = findfirstunique(x.timestamps, t) - return LOLE{1,L,T}(MeanEstimate(x.eventperiod_period_mean[i_t], - x.eventperiod_period_std[i_t], - x.nsamples)) + return LOLE{1, L, T}( + MeanEstimate( + x.eventperiod_period_mean[i_t], + x.eventperiod_period_std[i_t], + x.nsamples, + ), + ) end -function LOLE(x::ShortfallResult{N,L,T}, r::AbstractString, t::ZonedDateTime) where {N,L,T} +function LOLE( + x::ShortfallResult{N, L, T}, + r::AbstractString, + t::ZonedDateTime, +) where {N, L, T} i_r = findfirstunique(x.regions, r) i_t = findfirstunique(x.timestamps, t) - return LOLE{1,L,T}(MeanEstimate(x.eventperiod_regionperiod_mean[i_r, i_t], - x.eventperiod_regionperiod_std[i_r, i_t], - x.nsamples)) + return LOLE{1, L, T}( + MeanEstimate( + x.eventperiod_regionperiod_mean[i_r, i_t], + x.eventperiod_regionperiod_std[i_r, i_t], + x.nsamples, + ), + ) end +EUE(x::ShortfallResult{N, L, T, E}) where {N, L, T, E} = + EUE{N, L, T, E}(MeanEstimate(x[]..., x.nsamples)) -EUE(x::ShortfallResult{N,L,T,E}) where {N,L,T,E} = - EUE{N,L,T,E}(MeanEstimate(x[]..., x.nsamples)) - -EUE(x::ShortfallResult{N,L,T,E}, r::AbstractString) where {N,L,T,E} = - EUE{N,L,T,E}(MeanEstimate(x[r]..., x.nsamples)) +EUE(x::ShortfallResult{N, L, T, E}, r::AbstractString) where {N, L, T, E} = + EUE{N, L, T, E}(MeanEstimate(x[r]..., x.nsamples)) -EUE(x::ShortfallResult{N,L,T,E}, t::ZonedDateTime) where {N,L,T,E} = - EUE{1,L,T,E}(MeanEstimate(x[t]..., x.nsamples)) +EUE(x::ShortfallResult{N, L, T, E}, t::ZonedDateTime) where {N, L, T, E} = + EUE{1, L, T, E}(MeanEstimate(x[t]..., x.nsamples)) -EUE(x::ShortfallResult{N,L,T,E}, r::AbstractString, t::ZonedDateTime) where {N,L,T,E} = - EUE{1,L,T,E}(MeanEstimate(x[r, t]..., x.nsamples)) +EUE( + x::ShortfallResult{N, L, T, E}, + r::AbstractString, + t::ZonedDateTime, +) where {N, L, T, E} = EUE{1, L, T, E}(MeanEstimate(x[r, t]..., x.nsamples)) # Full shortfall data struct ShortfallSamples <: ResultSpec end -struct ShortfallSamplesResult{N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} <: AbstractShortfallResult{N,L,T} - +struct ShortfallSamplesResult{N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} <: + AbstractShortfallResult{N, L, T} regions::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - shortfall::Array{Int,3} # r x t x s + timestamps::StepRange{ZonedDateTime, T} + shortfall::Array{Int, 3} # r x t x s end -function getindex( - x::ShortfallSamplesResult{N,L,T,P,E} -) where {N,L,T,P,E} +function getindex(x::ShortfallSamplesResult{N, L, T, P, E}) where {N, L, T, P, E} p2e = conversionfactor(L, T, P, E) return vec(p2e * sum(x.shortfall, dims=1:2)) end function getindex( - x::ShortfallSamplesResult{N,L,T,P,E}, r::AbstractString -) where {N,L,T,P,E} + x::ShortfallSamplesResult{N, L, T, P, E}, + r::AbstractString, +) where {N, L, T, P, E} i_r = findfirstunique(x.regions, r) p2e = conversionfactor(L, T, P, E) return vec(p2e * sum(view(x.shortfall, i_r, :, :), dims=1)) end function getindex( - x::ShortfallSamplesResult{N,L,T,P,E}, t::ZonedDateTime -) where {N,L,T,P,E} + x::ShortfallSamplesResult{N, L, T, P, E}, + t::ZonedDateTime, +) where {N, L, T, P, E} i_t = findfirstunique(x.timestamps, t) p2e = conversionfactor(L, T, P, E) return vec(p2e * sum(view(x.shortfall, :, i_t, :), dims=1)) end function getindex( - x::ShortfallSamplesResult{N,L,T,P,E}, r::AbstractString, t::ZonedDateTime -) where {N,L,T,P,E} + x::ShortfallSamplesResult{N, L, T, P, E}, + r::AbstractString, + t::ZonedDateTime, +) where {N, L, T, P, E} i_r = findfirstunique(x.regions, r) i_t = findfirstunique(x.timestamps, t) p2e = conversionfactor(L, T, P, E) return vec(p2e * x.shortfall[i_r, i_t, :]) end - -function LOLE(x::ShortfallSamplesResult{N,L,T}) where {N,L,T} +function LOLE(x::ShortfallSamplesResult{N, L, T}) where {N, L, T} eventperiods = sum(sum(x.shortfall, dims=1) .> 0, dims=2) - return LOLE{N,L,T}(MeanEstimate(eventperiods)) + return LOLE{N, L, T}(MeanEstimate(eventperiods)) end -function LOLE(x::ShortfallSamplesResult{N,L,T}, r::AbstractString) where {N,L,T} +function LOLE(x::ShortfallSamplesResult{N, L, T}, r::AbstractString) where {N, L, T} i_r = findfirstunique(x.regions, r) eventperiods = sum(view(x.shortfall, i_r, :, :) .> 0, dims=1) - return LOLE{N,L,T}(MeanEstimate(eventperiods)) + return LOLE{N, L, T}(MeanEstimate(eventperiods)) end -function LOLE(x::ShortfallSamplesResult{N,L,T}, t::ZonedDateTime) where {N,L,T} +function LOLE(x::ShortfallSamplesResult{N, L, T}, t::ZonedDateTime) where {N, L, T} i_t = findfirstunique(x.timestamps, t) eventperiods = sum(view(x.shortfall, :, i_t, :), dims=1) .> 0 - return LOLE{1,L,T}(MeanEstimate(eventperiods)) + return LOLE{1, L, T}(MeanEstimate(eventperiods)) end -function LOLE(x::ShortfallSamplesResult{N,L,T}, r::AbstractString, t::ZonedDateTime) where {N,L,T} +function LOLE( + x::ShortfallSamplesResult{N, L, T}, + r::AbstractString, + t::ZonedDateTime, +) where {N, L, T} i_r = findfirstunique(x.regions, r) i_t = findfirstunique(x.timestamps, t) eventperiods = view(x.shortfall, i_r, i_t, :) .> 0 - return LOLE{1,L,T}(MeanEstimate(eventperiods)) + return LOLE{1, L, T}(MeanEstimate(eventperiods)) end +EUE(x::ShortfallSamplesResult{N, L, T, P, E}) where {N, L, T, P, E} = + EUE{N, L, T, E}(MeanEstimate(x[])) -EUE(x::ShortfallSamplesResult{N,L,T,P,E}) where {N,L,T,P,E} = - EUE{N,L,T,E}(MeanEstimate(x[])) - -EUE(x::ShortfallSamplesResult{N,L,T,P,E}, r::AbstractString) where {N,L,T,P,E} = - EUE{N,L,T,E}(MeanEstimate(x[r])) +EUE(x::ShortfallSamplesResult{N, L, T, P, E}, r::AbstractString) where {N, L, T, P, E} = + EUE{N, L, T, E}(MeanEstimate(x[r])) -EUE(x::ShortfallSamplesResult{N,L,T,P,E}, t::ZonedDateTime) where {N,L,T,P,E} = - EUE{1,L,T,E}(MeanEstimate(x[t])) +EUE(x::ShortfallSamplesResult{N, L, T, P, E}, t::ZonedDateTime) where {N, L, T, P, E} = + EUE{1, L, T, E}(MeanEstimate(x[t])) -EUE(x::ShortfallSamplesResult{N,L,T,P,E}, r::AbstractString, t::ZonedDateTime) where {N,L,T,P,E} = - EUE{1,L,T,E}(MeanEstimate(x[r, t])) +EUE( + x::ShortfallSamplesResult{N, L, T, P, E}, + r::AbstractString, + t::ZonedDateTime, +) where {N, L, T, P, E} = EUE{1, L, T, E}(MeanEstimate(x[r, t])) diff --git a/src/ResourceAdequacy/results/surplus.jl b/src/ResourceAdequacy/results/surplus.jl index a3ab03ca..5c7014e2 100644 --- a/src/ResourceAdequacy/results/surplus.jl +++ b/src/ResourceAdequacy/results/surplus.jl @@ -1,13 +1,11 @@ struct Surplus <: ResultSpec end -abstract type AbstractSurplusResult{N,L,T} <: Result{N,L,T} end +abstract type AbstractSurplusResult{N, L, T} <: Result{N, L, T} end # Colon indexing -getindex(x::AbstractSurplusResult, ::Colon) = - getindex.(x, x.timestamps) +getindex(x::AbstractSurplusResult, ::Colon) = getindex.(x, x.timestamps) -getindex(x::AbstractSurplusResult, ::Colon, t::ZonedDateTime) = - getindex.(x, x.regions, t) +getindex(x::AbstractSurplusResult, ::Colon, t::ZonedDateTime) = getindex.(x, x.regions, t) getindex(x::AbstractSurplusResult, r::AbstractString, ::Colon) = getindex.(x, r, x.timestamps) @@ -17,17 +15,15 @@ getindex(x::AbstractSurplusResult, ::Colon, ::Colon) = # Sample-averaged surplus data -struct SurplusResult{N,L,T<:Period,P<:PowerUnit} <: AbstractSurplusResult{N,L,T} - - nsamples::Union{Int,Nothing} +struct SurplusResult{N, L, T <: Period, P <: PowerUnit} <: AbstractSurplusResult{N, L, T} + nsamples::Union{Int, Nothing} regions::Vector{String} - timestamps::StepRange{ZonedDateTime,T} + timestamps::StepRange{ZonedDateTime, T} surplus_mean::Matrix{Float64} surplus_period_std::Vector{Float64} surplus_regionperiod_std::Matrix{Float64} - end function getindex(x::SurplusResult, t::ZonedDateTime) @@ -45,13 +41,12 @@ end struct SurplusSamples <: ResultSpec end -struct SurplusSamplesResult{N,L,T<:Period,P<:PowerUnit} <: AbstractSurplusResult{N,L,T} - +struct SurplusSamplesResult{N, L, T <: Period, P <: PowerUnit} <: + AbstractSurplusResult{N, L, T} regions::Vector{String} - timestamps::StepRange{ZonedDateTime,T} - - surplus::Array{Int,3} + timestamps::StepRange{ZonedDateTime, T} + surplus::Array{Int, 3} end function getindex(x::SurplusSamplesResult, t::ZonedDateTime) diff --git a/src/ResourceAdequacy/results/utilization.jl b/src/ResourceAdequacy/results/utilization.jl index e17e007a..21b8a8fe 100644 --- a/src/ResourceAdequacy/results/utilization.jl +++ b/src/ResourceAdequacy/results/utilization.jl @@ -1,41 +1,45 @@ struct Utilization <: ResultSpec end -abstract type AbstractUtilizationResult{N,L,T} <: Result{N,L,T} end +abstract type AbstractUtilizationResult{N, L, T} <: Result{N, L, T} end # Colon indexing -getindex(x::AbstractUtilizationResult, ::Colon) = - getindex.(x, x.interfaces) +getindex(x::AbstractUtilizationResult, ::Colon) = getindex.(x, x.interfaces) getindex(x::AbstractUtilizationResult, ::Colon, t::ZonedDateTime) = getindex.(x, x.interfaces, t) -getindex(x::AbstractUtilizationResult, i::Pair{<:AbstractString,<:AbstractString}, ::Colon) = - getindex.(x, i, x.timestamps) +getindex( + x::AbstractUtilizationResult, + i::Pair{<:AbstractString, <:AbstractString}, + ::Colon, +) = getindex.(x, i, x.timestamps) getindex(x::AbstractUtilizationResult, ::Colon, ::Colon) = getindex.(x, x.interfaces, permutedims(x.timestamps)) # Sample-averaged utilization data -struct UtilizationResult{N,L,T<:Period} <: AbstractUtilizationResult{N,L,T} - - nsamples::Union{Int,Nothing} - interfaces::Vector{Pair{String,String}} - timestamps::StepRange{ZonedDateTime,T} +struct UtilizationResult{N, L, T <: Period} <: AbstractUtilizationResult{N, L, T} + nsamples::Union{Int, Nothing} + interfaces::Vector{Pair{String, String}} + timestamps::StepRange{ZonedDateTime, T} utilization_mean::Matrix{Float64} utilization_interface_std::Vector{Float64} utilization_interfaceperiod_std::Matrix{Float64} - end -function getindex(x::UtilizationResult, i::Pair{<:AbstractString,<:AbstractString}) +function getindex(x::UtilizationResult, i::Pair{<:AbstractString, <:AbstractString}) i_i, _ = findfirstunique_directional(x.interfaces, i) return mean(view(x.utilization_mean, i_i, :)), x.utilization_interface_std[i_i] end -function getindex(x::UtilizationResult, i::Pair{<:AbstractString,<:AbstractString}, t::ZonedDateTime) +function getindex( + x::UtilizationResult, + i::Pair{<:AbstractString, <:AbstractString}, + t::ZonedDateTime, +) i_i, _ = findfirstunique_directional(x.interfaces, i) i_t = findfirstunique(x.timestamps, t) return x.utilization_mean[i_i, i_t], x.utilization_interfaceperiod_std[i_i, i_t] @@ -45,24 +49,23 @@ end struct UtilizationSamples <: ResultSpec end -struct UtilizationSamplesResult{N,L,T<:Period} <: AbstractUtilizationResult{N,L,T} - - interfaces::Vector{Pair{String,String}} - timestamps::StepRange{ZonedDateTime,T} - - utilization::Array{Float64,3} +struct UtilizationSamplesResult{N, L, T <: Period} <: AbstractUtilizationResult{N, L, T} + interfaces::Vector{Pair{String, String}} + timestamps::StepRange{ZonedDateTime, T} + utilization::Array{Float64, 3} end -function getindex(x::UtilizationSamplesResult, - i::Pair{<:AbstractString,<:AbstractString}) +function getindex(x::UtilizationSamplesResult, i::Pair{<:AbstractString, <:AbstractString}) i_i, _ = findfirstunique_directional(x.interfaces, i) return vec(mean(view(x.utilization, i_i, :, :), dims=1)) end - -function getindex(x::UtilizationSamplesResult, - i::Pair{<:AbstractString,<:AbstractString}, t::ZonedDateTime) +function getindex( + x::UtilizationSamplesResult, + i::Pair{<:AbstractString, <:AbstractString}, + t::ZonedDateTime, +) i_i, _ = findfirstunique_directional(x.interfaces, i) i_t = findfirstunique(x.timestamps, t) return vec(x.utilization[i_i, i_t, :]) diff --git a/src/ResourceAdequacy/simulations/convolution/Convolution.jl b/src/ResourceAdequacy/simulations/convolution/Convolution.jl index 4b0224b0..6944880c 100644 --- a/src/ResourceAdequacy/simulations/convolution/Convolution.jl +++ b/src/ResourceAdequacy/simulations/convolution/Convolution.jl @@ -1,21 +1,17 @@ include("conv.jl") struct Convolution <: SimulationSpec - verbose::Bool threaded::Bool - Convolution(;verbose::Bool=false, threaded::Bool=true) = - new(verbose, threaded) - + Convolution(; verbose::Bool=false, threaded::Bool=true) = new(verbose, threaded) end function assess( system::SystemModel{N}, method::Convolution, - resultspecs::ResultSpec... + resultspecs::ResultSpec..., ) where {N} - nregions = length(system.regions) nstors = length(system.storages) ngenstors = length(system.generatorstorages) @@ -31,18 +27,19 @@ function assess( nstors > 0 && push!(resources, "$nstors Storage") ngenstors > 0 && push!(resources, "$ngenstors GeneratorStorage") @warn "$method is a non-sequential simulation method. " * - "The system's " * join(resources, " and ") * " resources " * + "The system's " * + join(resources, " and ") * + " resources " * "will be ignored in this assessment." end threads = nthreads() - periods = Channel{Int}(2*threads) + periods = Channel{Int}(2 * threads) results = resultchannel(method, resultspecs, threads) @spawn makeperiods(periods, N) if method.threaded - if (threads == 1) @warn "It looks like you haven't configured JULIA_NUM_THREADS before you started the julia repl. \n If you want to use multi-threading, stop the execution and start your julia repl using : \n julia --project --threads auto" end @@ -55,7 +52,6 @@ function assess( end return finalize(results, system, method.threaded ? threads : 1) - end function makeperiods(periods::Channel{Int}, N::Int) @@ -66,23 +62,20 @@ function makeperiods(periods::Channel{Int}, N::Int) end function assess( - system::SystemModel{N,L,T,P,E}, method::Convolution, + system::SystemModel{N, L, T, P, E}, + method::Convolution, periods::Channel{Int}, results::Channel{<:Tuple{Vararg{ResultAccumulator{Convolution}}}}, - resultspecs::ResultSpec... -) where {N,L,T<:Period,P<:PowerUnit,E<:EnergyUnit} - + resultspecs::ResultSpec..., +) where {N, L, T <: Period, P <: PowerUnit, E <: EnergyUnit} accs = accumulator.(system, method, resultspecs) for t in periods - distr = CapacityDistribution(system, t) foreach(acc -> record!(acc, t, distr), accs) - end put!(results, accs) - end include("result_shortfall.jl") diff --git a/src/ResourceAdequacy/simulations/convolution/conv.jl b/src/ResourceAdequacy/simulations/convolution/conv.jl index b2892f10..033d0d21 100644 --- a/src/ResourceAdequacy/simulations/convolution/conv.jl +++ b/src/ResourceAdequacy/simulations/convolution/conv.jl @@ -1,8 +1,6 @@ -CapacityDistribution = - DiscreteNonParametric{Int,Float64,Vector{Int},Vector{Float64}} +CapacityDistribution = DiscreteNonParametric{Int, Float64, Vector{Int}, Vector{Float64}} function (::Type{CapacityDistribution})(system::SystemModel, t::Int) - capacities = system.generators.capacity[:, t] μ = system.generators.μ[:, t] @@ -13,88 +11,90 @@ function (::Type{CapacityDistribution})(system::SystemModel, t::Int) support(result) .-= colsum(system.regions.load, t) return result - end function assess(distr::CapacityDistribution) - xs = support(distr) ps = probs(distr) i = 1 - lolp = 0. - eul = 0. + lolp = 0.0 + eul = 0.0 while i <= length(xs) - - xs[i] >= 0 && break - lolp += ps[i] - eul -= ps[i] * xs[i] - i += 1 - + xs[i] >= 0 && break + lolp += ps[i] + eul -= ps[i] * xs[i] + i += 1 end return lolp, eul - end function surplus(distr::CapacityDistribution) - xs = support(distr) ps = probs(distr) i = 1 - es = 0. + es = 0.0 for i in 1:length(xs) - xs[i] <= 0 && continue - es += ps[i] * xs[i] + xs[i] <= 0 && continue + es += ps[i] * xs[i] end return es - end function spconv(hvsraw::AbstractVector{Int}, hpsraw::AbstractVector{Float64}) - zeroidxs = hvsraw .!= 0 hvs = hvsraw[zeroidxs] hps = hpsraw[zeroidxs] - length(hvs) == 0 && - return DiscreteNonParametric([0], [1.], check_args=false) + length(hvs) == 0 && return DiscreteNonParametric([0], [1.0], check_args=false) max_n = sum(hvs) + 1 - current_probs = Vector{Float64}(undef, max_n) - prev_probs = Vector{Float64}(undef, max_n) + current_probs = Vector{Float64}(undef, max_n) + prev_probs = Vector{Float64}(undef, max_n) current_values = Vector{Int}(undef, max_n) - prev_values = Vector{Int}(undef, max_n) + prev_values = Vector{Int}(undef, max_n) current_n = 2 current_values[1:current_n] = [0, hvs[1]] - current_probs[1:current_n] = [1 - hps[1], hps[1]] + current_probs[1:current_n] = [1 - hps[1], hps[1]] for (hv, hp) in zip(hvs[2:end], hps[2:end]) - current_values, current_probs, current_n, prev_values, prev_probs = - spconv!(prev_values, prev_probs, hv, hp, - current_values, current_probs, current_n) + current_values, current_probs, current_n, prev_values, prev_probs = spconv!( + prev_values, + prev_probs, + hv, + hp, + current_values, + current_probs, + current_n, + ) end resize!(current_values, current_n) resize!(current_probs, current_n) - nonzeroprob_idxs = findall(x -> x>0, current_probs) + nonzeroprob_idxs = findall(x -> x > 0, current_probs) return DiscreteNonParametric( current_values[nonzeroprob_idxs], current_probs[nonzeroprob_idxs], - check_args=false) - + check_args=false, + ) end -function spconv!(y_values::Vector{Int}, y_probs::Vector{Float64}, - h_value::Int, h_prob::Float64, - x_values::Vector{Int}, x_probs::Vector{Float64}, nx::Int) - +function spconv!( + y_values::Vector{Int}, + y_probs::Vector{Float64}, + h_value::Int, + h_prob::Float64, + x_values::Vector{Int}, + x_probs::Vector{Float64}, + nx::Int, +) h_q = 1 - h_prob ix = ixsh = 1 @@ -102,7 +102,6 @@ function spconv!(y_values::Vector{Int}, y_probs::Vector{Float64}, lastval = -1 @inbounds while ix <= nx - x = x_values[ix] xsh = x_values[ixsh] + h_value @@ -135,9 +134,7 @@ function spconv!(y_values::Vector{Int}, y_probs::Vector{Float64}, @fastmath y_probs[iy] = h_prob * x_probs[ixsh] lastval = xsh ixsh += 1 - end - end @inbounds while ixsh <= nx @@ -148,5 +145,4 @@ function spconv!(y_values::Vector{Int}, y_probs::Vector{Float64}, end return y_values, y_probs, iy, x_values, x_probs - end diff --git a/src/ResourceAdequacy/simulations/convolution/result_shortfall.jl b/src/ResourceAdequacy/simulations/convolution/result_shortfall.jl index 9821b654..538d6c38 100644 --- a/src/ResourceAdequacy/simulations/convolution/result_shortfall.jl +++ b/src/ResourceAdequacy/simulations/convolution/result_shortfall.jl @@ -1,18 +1,12 @@ -struct ConvolutionShortfallAccumulator <: ResultAccumulator{Convolution,Shortfall} - +struct ConvolutionShortfallAccumulator <: ResultAccumulator{Convolution, Shortfall} lolps::Vector{Float64} euls::Vector{Float64} - end -function merge!( - x::ConvolutionShortfallAccumulator, y::ConvolutionShortfallAccumulator -) - +function merge!(x::ConvolutionShortfallAccumulator, y::ConvolutionShortfallAccumulator) x.lolps .+= y.lolps x.euls .+= y.euls return - end accumulatortype(::Convolution, ::Shortfall) = ConvolutionShortfallAccumulator @@ -20,36 +14,41 @@ accumulatortype(::Convolution, ::Shortfall) = ConvolutionShortfallAccumulator accumulator(::SystemModel{N}, ::Convolution, ::Shortfall) where {N} = ConvolutionShortfallAccumulator(zeros(N), zeros(N)) -function record!( - acc::ConvolutionShortfallAccumulator, - t::Int, distr::CapacityDistribution -) - +function record!(acc::ConvolutionShortfallAccumulator, t::Int, distr::CapacityDistribution) lolp, eul = assess(distr) acc.lolps[t] = lolp acc.euls[t] = eul return - end function finalize( acc::ConvolutionShortfallAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} lole = sum(acc.lolps) - p2e = conversionfactor(L,T,P,E) + p2e = conversionfactor(L, T, P, E) eues = acc.euls .* p2e eue = sum(eues) allzeros = zeros(length(acc.lolps)) - return ShortfallResult{N,L,T,E}( - nothing, ["[PRAS] Entire System"], system.timestamps, - lole, 0., [lole], [0.], acc.lolps, allzeros, - reshape(acc.lolps, 1, :), reshape(allzeros, 1, :), - reshape(eues, 1, :), 0., [0.], allzeros, reshape(allzeros, 1, :) + return ShortfallResult{N, L, T, E}( + nothing, + ["[PRAS] Entire System"], + system.timestamps, + lole, + 0.0, + [lole], + [0.0], + acc.lolps, + allzeros, + reshape(acc.lolps, 1, :), + reshape(allzeros, 1, :), + reshape(eues, 1, :), + 0.0, + [0.0], + allzeros, + reshape(allzeros, 1, :), ) - end diff --git a/src/ResourceAdequacy/simulations/convolution/result_surplus.jl b/src/ResourceAdequacy/simulations/convolution/result_surplus.jl index 2ff4763c..a3861b4f 100644 --- a/src/ResourceAdequacy/simulations/convolution/result_surplus.jl +++ b/src/ResourceAdequacy/simulations/convolution/result_surplus.jl @@ -1,16 +1,10 @@ -struct ConvolutionSurplusAccumulator <: ResultAccumulator{Convolution,Surplus} - +struct ConvolutionSurplusAccumulator <: ResultAccumulator{Convolution, Surplus} surplus::Vector{Float64} - end -function merge!( - x::ConvolutionSurplusAccumulator, y::ConvolutionSurplusAccumulator -) - +function merge!(x::ConvolutionSurplusAccumulator, y::ConvolutionSurplusAccumulator) x.surplus .+= y.surplus return - end accumulatortype(::Convolution, ::Surplus) = ConvolutionSurplusAccumulator @@ -18,26 +12,23 @@ accumulatortype(::Convolution, ::Surplus) = ConvolutionSurplusAccumulator accumulator(::SystemModel{N}, ::Convolution, ::Surplus) where {N} = ConvolutionSurplusAccumulator(zeros(N)) -function record!( - acc::ConvolutionSurplusAccumulator, - t::Int, distr::CapacityDistribution -) - +function record!(acc::ConvolutionSurplusAccumulator, t::Int, distr::CapacityDistribution) acc.surplus[t] = surplus(distr) return - end function finalize( acc::ConvolutionSurplusAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} allzeros = zeros(length(acc.surplus)) - return SurplusResult{N,L,T,P}( - nothing, ["__EntireSystem"], system.timestamps, - reshape(acc.surplus, 1, :), allzeros, reshape(allzeros, 1, :) + return SurplusResult{N, L, T, P}( + nothing, + ["__EntireSystem"], + system.timestamps, + reshape(acc.surplus, 1, :), + allzeros, + reshape(allzeros, 1, :), ) - end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/DispatchProblem.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/DispatchProblem.jl index a261a84f..ae3f0f92 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/DispatchProblem.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/DispatchProblem.jl @@ -1,5 +1,4 @@ """ - DispatchProblem(sys::SystemModel) Create a min-cost flow problem for the multi-region max power delivery problem @@ -61,17 +60,15 @@ Edges are ordered as: 7. Storage charge from grid (Storage order) 8. Storage charge unused (Storage order) 9. GenerationStorage discharge to grid (GeneratorStorage order) - 10. GenerationStorage discharge unused (GeneratorStorage order) - 11. GenerationStorage inflow to grid (GenerationStorage order) - 12. GenerationStorage total to grid (GeneratorStorage order) - 13. GenerationStorage charge from grid (GeneratorStorage order) - 14. GenerationStorage charge from inflow (GeneratorStorage order) - 15. GenerationStorage charge unused (GeneratorStorage order) - 16. GenerationStorage inflow unused (GeneratorStorage order) - +10. GenerationStorage discharge unused (GeneratorStorage order) +11. GenerationStorage inflow to grid (GenerationStorage order) +12. GenerationStorage total to grid (GeneratorStorage order) +13. GenerationStorage charge from grid (GeneratorStorage order) +14. GenerationStorage charge from inflow (GeneratorStorage order) +15. GenerationStorage charge unused (GeneratorStorage order) +16. GenerationStorage inflow unused (GeneratorStorage order) """ struct DispatchProblem - fp::FlowProblem # Node labels @@ -105,17 +102,15 @@ struct DispatchProblem min_chargecost::Int max_dischargecost::Int - function DispatchProblem( - sys::SystemModel; unlimited::Int=999_999_999) - + function DispatchProblem(sys::SystemModel; unlimited::Int=999_999_999) nregions = length(sys.regions) nifaces = length(sys.interfaces) nstors = length(sys.storages) ngenstors = length(sys.generatorstorages) maxchargetime, maxdischargetime = maxtimetocharge_discharge(sys) - min_chargecost = - maxchargetime - 1 - max_dischargecost = - min_chargecost + maxdischargetime + 1 + min_chargecost = -maxchargetime - 1 + max_dischargecost = -min_chargecost + maxdischargetime + 1 shortagepenalty = 10 * (nifaces + max_dischargecost) stor_regions = assetgrouplist(sys.region_stor_idxs) @@ -154,7 +149,11 @@ struct DispatchProblem limits = fill(unlimited, nedges) injections = zeros(Int, nnodes) - function initedges(idxs::UnitRange{Int}, from::AbstractVector{Int}, to::AbstractVector{Int}) + function initedges( + idxs::UnitRange{Int}, + from::AbstractVector{Int}, + to::AbstractVector{Int}, + ) nodesfrom[idxs] = from nodesto[idxs] = to end @@ -202,76 +201,85 @@ struct DispatchProblem initedges(genstor_inflowunused, genstor_inflow_nodes, slack_node) return new( - FlowProblem(nodesfrom, nodesto, limits, costs, injections), - - region_nodes, stor_discharge_nodes, stor_charge_nodes, - genstor_inflow_nodes, genstor_discharge_nodes, - genstor_togrid_nodes, genstor_charge_nodes, slack_node, - - region_unservedenergy, region_unusedcapacity, - iface_forward, iface_reverse, - stor_dischargeused, stor_dischargeunused, - stor_chargeused, stor_chargeunused, - genstor_dischargegrid, genstor_dischargeunused, genstor_inflowgrid, + region_nodes, + stor_discharge_nodes, + stor_charge_nodes, + genstor_inflow_nodes, + genstor_discharge_nodes, + genstor_togrid_nodes, + genstor_charge_nodes, + slack_node, + region_unservedenergy, + region_unusedcapacity, + iface_forward, + iface_reverse, + stor_dischargeused, + stor_dischargeunused, + stor_chargeused, + stor_chargeunused, + genstor_dischargegrid, + genstor_dischargeunused, + genstor_inflowgrid, genstor_totalgrid, - genstor_gridcharge, genstor_inflowcharge, genstor_chargeunused, - genstor_inflowunused, min_chargecost, max_dischargecost + genstor_gridcharge, + genstor_inflowcharge, + genstor_chargeunused, + genstor_inflowunused, + min_chargecost, + max_dischargecost, ) - end - end -indices_after(lastset::UnitRange{Int}, setsize::Int) = - last(lastset) .+ (1:setsize) +indices_after(lastset::UnitRange{Int}, setsize::Int) = last(lastset) .+ (1:setsize) function update_problem!( - problem::DispatchProblem, state::SystemState, - system::SystemModel{N,L,T,P,E}, t::Int -) where {N,L,T,P,E} - + problem::DispatchProblem, + state::SystemState, + system::SystemModel{N, L, T, P, E}, + t::Int, +) where {N, L, T, P, E} fp = problem.fp slack_node = fp.nodes[problem.slack_node] # Update regional net available injection / withdrawal (from generators) for (r, gen_idxs) in zip(problem.region_nodes, system.region_gen_idxs) - region_node = fp.nodes[r] - region_netgenavailable = available_capacity( - state.gens_available, system.generators, gen_idxs, t - ) - system.regions.load[r, t] + region_netgenavailable = + available_capacity(state.gens_available, system.generators, gen_idxs, t) - + system.regions.load[r, t] updateinjection!(region_node, slack_node, region_netgenavailable) - end # Update bidirectional interface limits (from lines) for (i, line_idxs) in enumerate(system.interface_line_idxs) - interface_forwardedge = fp.edges[problem.interface_forward_edges[i]] interface_backwardedge = fp.edges[problem.interface_reverse_edges[i]] lines_capacity_forward, lines_capacity_backward = available_capacity(state.lines_available, system.lines, line_idxs, t) - interface_capacity_forward = min( - lines_capacity_forward, system.interfaces.limit_forward[i,t]) + interface_capacity_forward = + min(lines_capacity_forward, system.interfaces.limit_forward[i, t]) updateflowlimit!(interface_forwardedge, interface_capacity_forward) - interface_capacity_backward = min( - lines_capacity_backward, system.interfaces.limit_backward[i,t]) + interface_capacity_backward = + min(lines_capacity_backward, system.interfaces.limit_backward[i, t]) updateflowlimit!(interface_backwardedge, interface_capacity_backward) - end # Update Storage charge/discharge limits and priorities - for (i, (charge_node, charge_edge, discharge_node, discharge_edge)) in - enumerate(zip( - problem.storage_charge_nodes, problem.storage_charge_edges, - problem.storage_discharge_nodes, problem.storage_discharge_edges)) - + for (i, (charge_node, charge_edge, discharge_node, discharge_edge)) in enumerate( + zip( + problem.storage_charge_nodes, + problem.storage_charge_edges, + problem.storage_discharge_nodes, + problem.storage_discharge_edges, + ), + ) stor_online = state.stors_available[i] stor_energy = state.stors_energy[i] maxenergy = system.storages.energy_capacity[i, t] @@ -289,10 +297,8 @@ function update_problem!( end discharge_capacity = - min(maxdischarge, floor(Int, energytopower( - energydischargeable, E, L, T, P))) - updateinjection!( - fp.nodes[discharge_node], slack_node, discharge_capacity) + min(maxdischarge, floor(Int, energytopower(energydischargeable, E, L, T, P))) + updateinjection!(fp.nodes[discharge_node], slack_node, discharge_capacity) # Largest time-to-discharge = highest priority (discharge first) dischargecost = problem.max_dischargecost - timetodischarge # Positive cost @@ -305,26 +311,37 @@ function update_problem!( energychargeable = (maxenergy - stor_energy) / chargeefficiency charge_capacity = - min(maxcharge, floor(Int, energytopower( - energychargeable, E, L, T, P))) - updateinjection!( - fp.nodes[charge_node], slack_node, -charge_capacity) + min(maxcharge, floor(Int, energytopower(energychargeable, E, L, T, P))) + updateinjection!(fp.nodes[charge_node], slack_node, -charge_capacity) # Smallest time-to-discharge = highest priority (charge first) chargecost = problem.min_chargecost + timetodischarge # Negative cost updateflowcost!(fp.edges[charge_edge], chargecost) - end # Update GeneratorStorage inflow/charge/discharge limits and priorities - for (i, (charge_node, gridcharge_edge, inflowcharge_edge, - discharge_node, dischargegrid_edge, totalgrid_edge, - inflow_node)) in enumerate(zip( - problem.genstorage_charge_nodes, problem.genstorage_gridcharge_edges, - problem.genstorage_inflowcharge_edges, problem.genstorage_discharge_nodes, - problem.genstorage_dischargegrid_edges, problem.genstorage_totalgrid_edges, - problem.genstorage_inflow_nodes)) - + for ( + i, + ( + charge_node, + gridcharge_edge, + inflowcharge_edge, + discharge_node, + dischargegrid_edge, + totalgrid_edge, + inflow_node, + ), + ) in enumerate( + zip( + problem.genstorage_charge_nodes, + problem.genstorage_gridcharge_edges, + problem.genstorage_inflowcharge_edges, + problem.genstorage_discharge_nodes, + problem.genstorage_dischargegrid_edges, + problem.genstorage_totalgrid_edges, + problem.genstorage_inflow_nodes, + ), + ) stor_online = state.genstors_available[i] stor_energy = state.genstors_energy[i] maxenergy = system.generatorstorages.energy_capacity[i, t] @@ -332,8 +349,7 @@ function update_problem!( # Update inflow and grid injection / withdrawal limits inflow_capacity = stor_online * system.generatorstorages.inflow[i, t] - updateinjection!( - fp.nodes[inflow_node], slack_node, inflow_capacity) + updateinjection!(fp.nodes[inflow_node], slack_node, inflow_capacity) gridinjection_capacity = system.generatorstorages.gridinjection_capacity[i, t] updateflowlimit!(fp.edges[totalgrid_edge], gridinjection_capacity) @@ -354,10 +370,8 @@ function update_problem!( end discharge_capacity = - min(maxdischarge, floor(Int, energytopower( - energydischargeable, E, L, T, P))) - updateinjection!( - fp.nodes[discharge_node], slack_node, discharge_capacity) + min(maxdischarge, floor(Int, energytopower(energydischargeable, E, L, T, P))) + updateinjection!(fp.nodes[discharge_node], slack_node, discharge_capacity) # Largest time-to-discharge = highest priority (discharge first) dischargecost = problem.max_dischargecost - timetodischarge # Positive cost @@ -370,34 +384,30 @@ function update_problem!( energychargeable = (maxenergy - stor_energy) / chargeefficiency charge_capacity = - min(maxcharge, floor(Int, energytopower( - energychargeable, E, L, T, P))) - updateinjection!( - fp.nodes[charge_node], slack_node, -charge_capacity) + min(maxcharge, floor(Int, energytopower(energychargeable, E, L, T, P))) + updateinjection!(fp.nodes[charge_node], slack_node, -charge_capacity) # Smallest time-to-discharge = highest priority (charge first) chargecost = problem.min_chargecost + timetodischarge # Negative cost updateflowcost!(fp.edges[gridcharge_edge], chargecost) updateflowcost!(fp.edges[inflowcharge_edge], chargecost) - end - end function update_state!( - state::SystemState, problem::DispatchProblem, - system::SystemModel{N,L,T,P,E}, t::Int -) where {N,L,T,P,E} - + state::SystemState, + problem::DispatchProblem, + system::SystemModel{N, L, T, P, E}, + t::Int, +) where {N, L, T, P, E} edges = problem.fp.edges p2e = conversionfactor(L, T, P, E) for (i, e) in enumerate(problem.storage_discharge_edges) energy = state.stors_energy[i] - energy_drop = ceil(Int, edges[e].flow * p2e / - system.storages.discharge_efficiency[i, t]) + energy_drop = + ceil(Int, edges[e].flow * p2e / system.storages.discharge_efficiency[i, t]) state.stors_energy[i] = max(0, energy - energy_drop) - end for (i, e) in enumerate(problem.storage_charge_edges) @@ -407,16 +417,18 @@ function update_state!( for (i, e) in enumerate(problem.genstorage_dischargegrid_edges) energy = state.genstors_energy[i] - energy_drop = ceil(Int, edges[e].flow * p2e / - system.generatorstorages.discharge_efficiency[i, t]) + energy_drop = ceil( + Int, + edges[e].flow * p2e / system.generatorstorages.discharge_efficiency[i, t], + ) state.genstors_energy[i] = max(0, energy - energy_drop) end - for (i, (e1, e2)) in enumerate(zip(problem.genstorage_gridcharge_edges, - problem.genstorage_inflowcharge_edges)) + for (i, (e1, e2)) in enumerate( + zip(problem.genstorage_gridcharge_edges, problem.genstorage_inflowcharge_edges), + ) totalcharge = (edges[e1].flow + edges[e2].flow) * p2e state.genstors_energy[i] += ceil(Int, totalcharge * system.generatorstorages.charge_efficiency[i, t]) end - end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/SequentialMonteCarlo.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/SequentialMonteCarlo.jl index 95d5853e..75ef785d 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/SequentialMonteCarlo.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/SequentialMonteCarlo.jl @@ -3,41 +3,39 @@ include("DispatchProblem.jl") include("utils.jl") struct SequentialMonteCarlo <: SimulationSpec - nsamples::Int seed::UInt64 verbose::Bool threaded::Bool function SequentialMonteCarlo(; - samples::Int=10_000, seed::Integer=rand(UInt64), - verbose::Bool=false, threaded::Bool=true + samples::Int=10_000, + seed::Integer=rand(UInt64), + verbose::Bool=false, + threaded::Bool=true, ) samples <= 0 && throw(DomainError("Sample count must be positive")) seed < 0 && throw(DomainError("Random seed must be non-negative")) new(samples, UInt64(seed), verbose, threaded) end - end function assess( system::SystemModel, method::SequentialMonteCarlo, - resultspecs::ResultSpec... + resultspecs::ResultSpec..., ) - threads = nthreads() - sampleseeds = Channel{Int}(2*threads) + sampleseeds = Channel{Int}(2 * threads) results = resultchannel(method, resultspecs, threads) @spawn makeseeds(sampleseeds, method.nsamples) if method.threaded - if (threads == 1) @warn "It looks like you haven't configured JULIA_NUM_THREADS before you started the julia repl. \n If you want to use multi-threading, stop the execution and start your julia repl using : \n julia --project --threads auto" end - + for _ in 1:threads @spawn assess(system, method, sampleseeds, results, resultspecs...) end @@ -46,26 +44,23 @@ function assess( end return finalize(results, system, method.threaded ? threads : 1) - end function makeseeds(sampleseeds::Channel{Int}, nsamples::Int) - for s in 1:nsamples put!(sampleseeds, s) end close(sampleseeds) - end function assess( - system::SystemModel{N}, method::SequentialMonteCarlo, + system::SystemModel{N}, + method::SequentialMonteCarlo, sampleseeds::Channel{Int}, results::Channel{<:Tuple{Vararg{ResultAccumulator{SequentialMonteCarlo}}}}, - resultspecs::ResultSpec... -) where N - + resultspecs::ResultSpec..., +) where {N} dispatchproblem = DispatchProblem(system) systemstate = SystemState(system) recorders = accumulator.(system, method, resultspecs) @@ -76,87 +71,117 @@ function assess( rng = Philox4x((0, 0), 10) for s in sampleseeds - seed!(rng, (method.seed, s)) initialize!(rng, systemstate, system) for t in 1:N - advance!(rng, systemstate, dispatchproblem, system, t) solve!(dispatchproblem, systemstate, system, t) - foreach(recorder -> record!( - recorder, system, systemstate, dispatchproblem, s, t - ), recorders) - + foreach( + recorder -> record!(recorder, system, systemstate, dispatchproblem, s, t), + recorders, + ) end foreach(recorder -> reset!(recorder, s), recorders) - end put!(results, recorders) - end -function initialize!( - rng::AbstractRNG, state::SystemState, system::SystemModel{N} -) where N - - initialize_availability!( - rng, state.gens_available, state.gens_nexttransition, - system.generators, N) - - initialize_availability!( - rng, state.stors_available, state.stors_nexttransition, - system.storages, N) +function initialize!(rng::AbstractRNG, state::SystemState, system::SystemModel{N}) where {N} + initialize_availability!( + rng, + state.gens_available, + state.gens_nexttransition, + system.generators, + N, + ) - initialize_availability!( - rng, state.genstors_available, state.genstors_nexttransition, - system.generatorstorages, N) + initialize_availability!( + rng, + state.stors_available, + state.stors_nexttransition, + system.storages, + N, + ) - initialize_availability!( - rng, state.lines_available, state.lines_nexttransition, - system.lines, N) + initialize_availability!( + rng, + state.genstors_available, + state.genstors_nexttransition, + system.generatorstorages, + N, + ) - fill!(state.stors_energy, 0) - fill!(state.genstors_energy, 0) + initialize_availability!( + rng, + state.lines_available, + state.lines_nexttransition, + system.lines, + N, + ) - return + fill!(state.stors_energy, 0) + fill!(state.genstors_energy, 0) + return end function advance!( rng::AbstractRNG, state::SystemState, dispatchproblem::DispatchProblem, - system::SystemModel{N}, t::Int) where N - + system::SystemModel{N}, + t::Int, +) where {N} update_availability!( - rng, state.gens_available, state.gens_nexttransition, - system.generators, t, N) + rng, + state.gens_available, + state.gens_nexttransition, + system.generators, + t, + N, + ) update_availability!( - rng, state.stors_available, state.stors_nexttransition, - system.storages, t, N) + rng, + state.stors_available, + state.stors_nexttransition, + system.storages, + t, + N, + ) update_availability!( - rng, state.genstors_available, state.genstors_nexttransition, - system.generatorstorages, t, N) + rng, + state.genstors_available, + state.genstors_nexttransition, + system.generatorstorages, + t, + N, + ) update_availability!( - rng, state.lines_available, state.lines_nexttransition, - system.lines, t, N) + rng, + state.lines_available, + state.lines_nexttransition, + system.lines, + t, + N, + ) update_energy!(state.stors_energy, system.storages, t) update_energy!(state.genstors_energy, system.generatorstorages, t) update_problem!(dispatchproblem, state, system, t) - end function solve!( - dispatchproblem::DispatchProblem, state::SystemState, - system::SystemModel, t::Int + dispatchproblem::DispatchProblem, + state::SystemState, + system::SystemModel, + t::Int, ) solveflows!(dispatchproblem.fp) update_state!(state, dispatchproblem, system, t) diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/SystemState.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/SystemState.jl index a88296cb..b427f6db 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/SystemState.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/SystemState.jl @@ -1,5 +1,4 @@ struct SystemState - gens_available::Vector{Bool} gens_nexttransition::Vector{Int} @@ -15,10 +14,9 @@ struct SystemState lines_nexttransition::Vector{Int} function SystemState(system::SystemModel) - ngens = length(system.generators) gens_available = Vector{Bool}(undef, ngens) - gens_nexttransition= Vector{Int}(undef, ngens) + gens_nexttransition = Vector{Int}(undef, ngens) nstors = length(system.storages) stors_available = Vector{Bool}(undef, nstors) @@ -35,11 +33,16 @@ struct SystemState lines_nexttransition = Vector{Int}(undef, nlines) return new( - gens_available, gens_nexttransition, - stors_available, stors_nexttransition, stors_energy, - genstors_available, genstors_nexttransition, genstors_energy, - lines_available, lines_nexttransition) - + gens_available, + gens_nexttransition, + stors_available, + stors_nexttransition, + stors_energy, + genstors_available, + genstors_nexttransition, + genstors_energy, + lines_available, + lines_nexttransition, + ) end - end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_availability.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_availability.jl index 8c8a6ecb..039474d1 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_availability.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_availability.jl @@ -1,219 +1,202 @@ # GeneratorAvailability struct SMCGenAvailabilityAccumulator <: - ResultAccumulator{SequentialMonteCarlo,GeneratorAvailability} - - available::Array{Bool,3} - + ResultAccumulator{SequentialMonteCarlo, GeneratorAvailability} + available::Array{Bool, 3} end -function merge!( - x::SMCGenAvailabilityAccumulator, y::SMCGenAvailabilityAccumulator -) - +function merge!(x::SMCGenAvailabilityAccumulator, y::SMCGenAvailabilityAccumulator) x.available .|= y.available return - end -accumulatortype(::SequentialMonteCarlo, ::GeneratorAvailability) = SMCGenAvailabilityAccumulator +accumulatortype(::SequentialMonteCarlo, ::GeneratorAvailability) = + SMCGenAvailabilityAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::GeneratorAvailability + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::GeneratorAvailability, ) where {N} - ngens = length(sys.generators) available = zeros(Bool, ngens, N, simspec.nsamples) return SMCGenAvailabilityAccumulator(available) - end function record!( acc::SMCGenAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} acc.available[:, t, sampleid] .= state.gens_available return - end reset!(acc::SMCGenAvailabilityAccumulator, sampleid::Int) = nothing function finalize( acc::SMCGenAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return GeneratorAvailabilityResult{N,L,T}( - system.generators.names, system.timestamps, acc.available) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return GeneratorAvailabilityResult{N, L, T}( + system.generators.names, + system.timestamps, + acc.available, + ) end # StorageAvailability struct SMCStorAvailabilityAccumulator <: - ResultAccumulator{SequentialMonteCarlo,StorageAvailability} - - available::Array{Bool,3} - + ResultAccumulator{SequentialMonteCarlo, StorageAvailability} + available::Array{Bool, 3} end -function merge!( - x::SMCStorAvailabilityAccumulator, y::SMCStorAvailabilityAccumulator -) - +function merge!(x::SMCStorAvailabilityAccumulator, y::SMCStorAvailabilityAccumulator) x.available .|= y.available return - end -accumulatortype(::SequentialMonteCarlo, ::StorageAvailability) = SMCStorAvailabilityAccumulator +accumulatortype(::SequentialMonteCarlo, ::StorageAvailability) = + SMCStorAvailabilityAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::StorageAvailability + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::StorageAvailability, ) where {N} - nstors = length(sys.storages) available = zeros(Bool, nstors, N, simspec.nsamples) return SMCStorAvailabilityAccumulator(available) - end function record!( acc::SMCStorAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} acc.available[:, t, sampleid] .= state.stors_available return - end reset!(acc::SMCStorAvailabilityAccumulator, sampleid::Int) = nothing function finalize( acc::SMCStorAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return StorageAvailabilityResult{N,L,T}( - system.storages.names, system.timestamps, acc.available) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return StorageAvailabilityResult{N, L, T}( + system.storages.names, + system.timestamps, + acc.available, + ) end # GeneratorStorageAvailability struct SMCGenStorAvailabilityAccumulator <: - ResultAccumulator{SequentialMonteCarlo,GeneratorStorageAvailability} - - available::Array{Bool,3} - + ResultAccumulator{SequentialMonteCarlo, GeneratorStorageAvailability} + available::Array{Bool, 3} end -function merge!( - x::SMCGenStorAvailabilityAccumulator, y::SMCGenStorAvailabilityAccumulator -) - +function merge!(x::SMCGenStorAvailabilityAccumulator, y::SMCGenStorAvailabilityAccumulator) x.available .|= y.available return - end -accumulatortype(::SequentialMonteCarlo, ::GeneratorStorageAvailability) = SMCGenStorAvailabilityAccumulator +accumulatortype(::SequentialMonteCarlo, ::GeneratorStorageAvailability) = + SMCGenStorAvailabilityAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::GeneratorStorageAvailability + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::GeneratorStorageAvailability, ) where {N} - ngenstors = length(sys.generatorstorages) available = zeros(Bool, ngenstors, N, simspec.nsamples) return SMCGenStorAvailabilityAccumulator(available) - end function record!( acc::SMCGenStorAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} acc.available[:, t, sampleid] .= state.genstors_available return - end reset!(acc::SMCGenStorAvailabilityAccumulator, sampleid::Int) = nothing function finalize( acc::SMCGenStorAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return GeneratorStorageAvailabilityResult{N,L,T}( - system.generatorstorages.names, system.timestamps, acc.available) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return GeneratorStorageAvailabilityResult{N, L, T}( + system.generatorstorages.names, + system.timestamps, + acc.available, + ) end # LineAvailability struct SMCLineAvailabilityAccumulator <: - ResultAccumulator{SequentialMonteCarlo,LineAvailability} - - available::Array{Bool,3} - + ResultAccumulator{SequentialMonteCarlo, LineAvailability} + available::Array{Bool, 3} end -function merge!( - x::SMCLineAvailabilityAccumulator, y::SMCLineAvailabilityAccumulator -) - +function merge!(x::SMCLineAvailabilityAccumulator, y::SMCLineAvailabilityAccumulator) x.available .|= y.available return - end accumulatortype(::SequentialMonteCarlo, ::LineAvailability) = SMCLineAvailabilityAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::LineAvailability + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::LineAvailability, ) where {N} - nlines = length(sys.lines) available = zeros(Bool, nlines, N, simspec.nsamples) return SMCLineAvailabilityAccumulator(available) - end function record!( acc::SMCLineAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} acc.available[:, t, sampleid] .= state.lines_available return - end reset!(acc::SMCLineAvailabilityAccumulator, sampleid::Int) = nothing function finalize( acc::SMCLineAvailabilityAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return LineAvailabilityResult{N,L,T}( - system.lines.names, system.timestamps, acc.available) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return LineAvailabilityResult{N, L, T}( + system.lines.names, + system.timestamps, + acc.available, + ) end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_energy.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_energy.jl index 5fc3e9b3..b152c746 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_energy.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_energy.jl @@ -1,273 +1,254 @@ # StorageEnergy mutable struct SMCStorageEnergyAccumulator <: - ResultAccumulator{SequentialMonteCarlo,StorageEnergy} + ResultAccumulator{SequentialMonteCarlo, StorageEnergy} # Cross-simulation energy mean/variances energy_period::Vector{MeanVariance} energy_storageperiod::Matrix{MeanVariance} - end -function merge!( - x::SMCStorageEnergyAccumulator, y::SMCStorageEnergyAccumulator -) - +function merge!(x::SMCStorageEnergyAccumulator, y::SMCStorageEnergyAccumulator) foreach(merge!, x.energy_period, y.energy_period) foreach(merge!, x.energy_storageperiod, y.energy_storageperiod) return - end accumulatortype(::SequentialMonteCarlo, ::StorageEnergy) = SMCStorageEnergyAccumulator -function accumulator( - sys::SystemModel{N}, ::SequentialMonteCarlo, ::StorageEnergy -) where {N} - +function accumulator(sys::SystemModel{N}, ::SequentialMonteCarlo, ::StorageEnergy) where {N} nstorages = length(sys.storages) energy_period = [meanvariance() for _ in 1:N] energy_storageperiod = [meanvariance() for _ in 1:nstorages, _ in 1:N] - return SMCStorageEnergyAccumulator( - energy_period, energy_storageperiod) - + return SMCStorageEnergyAccumulator(energy_period, energy_storageperiod) end function record!( acc::SMCStorageEnergyAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} totalenergy = 0 nstorages = length(system.storages) for s in 1:nstorages - storageenergy = state.stors_energy[s] - fit!(acc.energy_storageperiod[s,t], storageenergy) + fit!(acc.energy_storageperiod[s, t], storageenergy) totalenergy += storageenergy - end fit!(acc.energy_period[t], totalenergy) return - end reset!(acc::SMCStorageEnergyAccumulator, sampleid::Int) = nothing function finalize( acc::SMCStorageEnergyAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} _, period_std = mean_std(acc.energy_period) storageperiod_mean, storageperiod_std = mean_std(acc.energy_storageperiod) nsamples = first(first(acc.energy_period).stats).n - return StorageEnergyResult{N,L,T,E}( - nsamples, system.storages.names, system.timestamps, - storageperiod_mean, period_std, storageperiod_std) - + return StorageEnergyResult{N, L, T, E}( + nsamples, + system.storages.names, + system.timestamps, + storageperiod_mean, + period_std, + storageperiod_std, + ) end # GeneratorStorageEnergy mutable struct SMCGenStorageEnergyAccumulator <: - ResultAccumulator{SequentialMonteCarlo,GeneratorStorageEnergy} + ResultAccumulator{SequentialMonteCarlo, GeneratorStorageEnergy} # Cross-simulation energy mean/variances energy_period::Vector{MeanVariance} energy_genstorperiod::Matrix{MeanVariance} - end -function merge!( - x::SMCGenStorageEnergyAccumulator, y::SMCGenStorageEnergyAccumulator -) - +function merge!(x::SMCGenStorageEnergyAccumulator, y::SMCGenStorageEnergyAccumulator) foreach(merge!, x.energy_period, y.energy_period) foreach(merge!, x.energy_genstorperiod, y.energy_genstorperiod) return - end accumulatortype(::SequentialMonteCarlo, ::GeneratorStorageEnergy) = SMCGenStorageEnergyAccumulator function accumulator( - sys::SystemModel{N}, ::SequentialMonteCarlo, ::GeneratorStorageEnergy + sys::SystemModel{N}, + ::SequentialMonteCarlo, + ::GeneratorStorageEnergy, ) where {N} - ngenstors = length(sys.generatorstorages) energy_period = [meanvariance() for _ in 1:N] energy_genstorperiod = [meanvariance() for _ in 1:ngenstors, _ in 1:N] - return SMCGenStorageEnergyAccumulator( - energy_period, energy_genstorperiod) - + return SMCGenStorageEnergyAccumulator(energy_period, energy_genstorperiod) end function record!( acc::SMCGenStorageEnergyAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} totalenergy = 0 ngenstors = length(system.generatorstorages) for s in 1:ngenstors - genstorenergy = state.genstors_energy[s] - fit!(acc.energy_genstorperiod[s,t], genstorenergy) + fit!(acc.energy_genstorperiod[s, t], genstorenergy) totalenergy += genstorenergy - end fit!(acc.energy_period[t], totalenergy) return - end reset!(acc::SMCGenStorageEnergyAccumulator, sampleid::Int) = nothing function finalize( acc::SMCGenStorageEnergyAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} _, period_std = mean_std(acc.energy_period) genstorperiod_mean, genstorperiod_std = mean_std(acc.energy_genstorperiod) nsamples = first(first(acc.energy_period).stats).n - return GeneratorStorageEnergyResult{N,L,T,E}( - nsamples, system.generatorstorages.names, system.timestamps, - genstorperiod_mean, period_std, genstorperiod_std) - + return GeneratorStorageEnergyResult{N, L, T, E}( + nsamples, + system.generatorstorages.names, + system.timestamps, + genstorperiod_mean, + period_std, + genstorperiod_std, + ) end # StorageEnergySamples struct SMCStorageEnergySamplesAccumulator <: - ResultAccumulator{SequentialMonteCarlo,StorageEnergySamples} - - energy::Array{Float64,3} - + ResultAccumulator{SequentialMonteCarlo, StorageEnergySamples} + energy::Array{Float64, 3} end function merge!( - x::SMCStorageEnergySamplesAccumulator, y::SMCStorageEnergySamplesAccumulator + x::SMCStorageEnergySamplesAccumulator, + y::SMCStorageEnergySamplesAccumulator, ) - x.energy .+= y.energy return - end accumulatortype(::SequentialMonteCarlo, ::StorageEnergySamples) = SMCStorageEnergySamplesAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::StorageEnergySamples + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::StorageEnergySamples, ) where {N} - nstors = length(sys.storages) energy = zeros(Int, nstors, N, simspec.nsamples) return SMCStorageEnergySamplesAccumulator(energy) - end function record!( acc::SMCStorageEnergySamplesAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} acc.energy[:, t, sampleid] .= state.stors_energy return - end reset!(acc::SMCStorageEnergySamplesAccumulator, sampleid::Int) = nothing function finalize( acc::SMCStorageEnergySamplesAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return StorageEnergySamplesResult{N,L,T,E}( - system.storages.names, system.timestamps, acc.energy) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return StorageEnergySamplesResult{N, L, T, E}( + system.storages.names, + system.timestamps, + acc.energy, + ) end # GeneratorStorageEnergySamples struct SMCGenStorageEnergySamplesAccumulator <: - ResultAccumulator{SequentialMonteCarlo,GeneratorStorageEnergySamples} - - energy::Array{Float64,3} - + ResultAccumulator{SequentialMonteCarlo, GeneratorStorageEnergySamples} + energy::Array{Float64, 3} end function merge!( x::SMCGenStorageEnergySamplesAccumulator, - y::SMCGenStorageEnergySamplesAccumulator + y::SMCGenStorageEnergySamplesAccumulator, ) - x.energy .+= y.energy return - end accumulatortype(::SequentialMonteCarlo, ::GeneratorStorageEnergySamples) = SMCGenStorageEnergySamplesAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::GeneratorStorageEnergySamples + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::GeneratorStorageEnergySamples, ) where {N} - ngenstors = length(sys.generatorstorages) energy = zeros(Int, ngenstors, N, simspec.nsamples) return SMCGenStorageEnergySamplesAccumulator(energy) - end function record!( acc::SMCGenStorageEnergySamplesAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} acc.energy[:, t, sampleid] .= state.genstors_energy return - end reset!(acc::SMCGenStorageEnergySamplesAccumulator, sampleid::Int) = nothing function finalize( acc::SMCGenStorageEnergySamplesAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return GeneratorStorageEnergySamplesResult{N,L,T,E}( - system.generatorstorages.names, system.timestamps, acc.energy) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return GeneratorStorageEnergySamplesResult{N, L, T, E}( + system.generatorstorages.names, + system.timestamps, + acc.energy, + ) end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_flow.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_flow.jl index 7e6a7eaa..02b2abab 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_flow.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_flow.jl @@ -1,29 +1,20 @@ # Flow -struct SMCFlowAccumulator <: ResultAccumulator{SequentialMonteCarlo,Flow} - +struct SMCFlowAccumulator <: ResultAccumulator{SequentialMonteCarlo, Flow} flow_interface::Vector{MeanVariance} flow_interfaceperiod::Matrix{MeanVariance} flow_interface_currentsim::Vector{Int} - end -function merge!( - x::SMCFlowAccumulator, y::SMCFlowAccumulator -) - +function merge!(x::SMCFlowAccumulator, y::SMCFlowAccumulator) foreach(merge!, x.flow_interface, y.flow_interface) foreach(merge!, x.flow_interfaceperiod, y.flow_interfaceperiod) - end accumulatortype(::SequentialMonteCarlo, ::Flow) = SMCFlowAccumulator -function accumulator( - sys::SystemModel{N}, ::SequentialMonteCarlo, ::Flow -) where {N} - +function accumulator(sys::SystemModel{N}, ::SequentialMonteCarlo, ::Flow) where {N} n_interfaces = length(sys.interfaces) flow_interface = [meanvariance() for _ in 1:n_interfaces] flow_interfaceperiod = [meanvariance() for _ in 1:n_interfaces, _ in 1:N] @@ -31,46 +22,43 @@ function accumulator( flow_interface_currentsim = zeros(Int, n_interfaces) return SMCFlowAccumulator( - flow_interface, flow_interfaceperiod, flow_interface_currentsim) - + flow_interface, + flow_interfaceperiod, + flow_interface_currentsim, + ) end function record!( acc::SMCFlowAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} edges = problem.fp.edges - for (i, (f, b)) in enumerate(zip(problem.interface_forward_edges, - problem.interface_reverse_edges)) - + for (i, (f, b)) in + enumerate(zip(problem.interface_forward_edges, problem.interface_reverse_edges)) flow = edges[f].flow - edges[b].flow acc.flow_interface_currentsim[i] += flow - fit!(acc.flow_interfaceperiod[i,t], flow) - + fit!(acc.flow_interfaceperiod[i, t], flow) end - end function reset!(acc::SMCFlowAccumulator, sampleid::Int) - for i in eachindex(acc.flow_interface_currentsim) fit!(acc.flow_interface[i], acc.flow_interface_currentsim[i]) acc.flow_interface_currentsim[i] = 0 end - end function finalize( acc::SMCFlowAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - nsamples = length(system.interfaces) > 0 ? - first(acc.flow_interface[1].stats).n : nothing + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + nsamples = + length(system.interfaces) > 0 ? first(acc.flow_interface[1].stats).n : nothing flow_mean, flow_interfaceperiod_std = mean_std(acc.flow_interfaceperiod) flow_interface_std = last(mean_std(acc.flow_interface)) / N @@ -78,71 +66,68 @@ function finalize( fromregions = getindex.(Ref(system.regions.names), system.interfaces.regions_from) toregions = getindex.(Ref(system.regions.names), system.interfaces.regions_to) - return FlowResult{N,L,T,P}( - nsamples, Pair.(fromregions, toregions), system.timestamps, - flow_mean, flow_interface_std, flow_interfaceperiod_std) - + return FlowResult{N, L, T, P}( + nsamples, + Pair.(fromregions, toregions), + system.timestamps, + flow_mean, + flow_interface_std, + flow_interfaceperiod_std, + ) end # FlowSamples -struct SMCFlowSamplesAccumulator <: - ResultAccumulator{SequentialMonteCarlo,FlowSamples} - - flow::Array{Int,3} - +struct SMCFlowSamplesAccumulator <: ResultAccumulator{SequentialMonteCarlo, FlowSamples} + flow::Array{Int, 3} end -function merge!( - x::SMCFlowSamplesAccumulator, y::SMCFlowSamplesAccumulator -) - +function merge!(x::SMCFlowSamplesAccumulator, y::SMCFlowSamplesAccumulator) x.flow .+= y.flow return - end accumulatortype(::SequentialMonteCarlo, ::FlowSamples) = SMCFlowSamplesAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::FlowSamples + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::FlowSamples, ) where {N} - ninterfaces = length(sys.interfaces) flow = zeros(Int, ninterfaces, N, simspec.nsamples) return SMCFlowSamplesAccumulator(flow) - end function record!( acc::SMCFlowSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - - for (i, (e_f, e_r)) in enumerate(zip(problem.interface_forward_edges, - problem.interface_reverse_edges)) - acc.flow[i, t, sampleid] = problem.fp.edges[e_f].flow - - problem.fp.edges[e_r].flow + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} + for (i, (e_f, e_r)) in + enumerate(zip(problem.interface_forward_edges, problem.interface_reverse_edges)) + acc.flow[i, t, sampleid] = problem.fp.edges[e_f].flow - problem.fp.edges[e_r].flow end return - end reset!(acc::SMCFlowSamplesAccumulator, sampleid::Int) = nothing function finalize( acc::SMCFlowSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} fromregions = getindex.(Ref(system.regions.names), system.interfaces.regions_from) toregions = getindex.(Ref(system.regions.names), system.interfaces.regions_to) - return FlowSamplesResult{N,L,T,P}( - Pair.(fromregions, toregions), system.timestamps, acc.flow) - + return FlowSamplesResult{N, L, T, P}( + Pair.(fromregions, toregions), + system.timestamps, + acc.flow, + ) end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_shortfall.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_shortfall.jl index 0972cc64..95f9b4a9 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_shortfall.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_shortfall.jl @@ -1,6 +1,6 @@ # Shortfall -mutable struct SMCShortfallAccumulator <: ResultAccumulator{SequentialMonteCarlo,Shortfall} +mutable struct SMCShortfallAccumulator <: ResultAccumulator{SequentialMonteCarlo, Shortfall} # Cross-simulation LOL period count mean/variances periodsdropped_total::MeanVariance @@ -21,13 +21,9 @@ mutable struct SMCShortfallAccumulator <: ResultAccumulator{SequentialMonteCarlo # Running UE totals for current simulation unservedload_total_currentsim::Int unservedload_region_currentsim::Vector{Int} - end -function merge!( - x::SMCShortfallAccumulator, y::SMCShortfallAccumulator -) - +function merge!(x::SMCShortfallAccumulator, y::SMCShortfallAccumulator) merge!(x.periodsdropped_total, y.periodsdropped_total) foreach(merge!, x.periodsdropped_region, y.periodsdropped_region) foreach(merge!, x.periodsdropped_period, y.periodsdropped_period) @@ -39,15 +35,11 @@ function merge!( foreach(merge!, x.unservedload_regionperiod, y.unservedload_regionperiod) return - end accumulatortype(::SequentialMonteCarlo, ::Shortfall) = SMCShortfallAccumulator -function accumulator( - sys::SystemModel{N}, ::SequentialMonteCarlo, ::Shortfall -) where {N} - +function accumulator(sys::SystemModel{N}, ::SequentialMonteCarlo, ::Shortfall) where {N} nregions = length(sys.regions) periodsdropped_total = meanvariance() @@ -67,45 +59,48 @@ function accumulator( unservedload_region_currentsim = zeros(Int, nregions) return SMCShortfallAccumulator( - periodsdropped_total, periodsdropped_region, - periodsdropped_period, periodsdropped_regionperiod, - periodsdropped_total_currentsim, periodsdropped_region_currentsim, - unservedload_total, unservedload_region, - unservedload_period, unservedload_regionperiod, - unservedload_total_currentsim, unservedload_region_currentsim) - + periodsdropped_total, + periodsdropped_region, + periodsdropped_period, + periodsdropped_regionperiod, + periodsdropped_total_currentsim, + periodsdropped_region_currentsim, + unservedload_total, + unservedload_region, + unservedload_period, + unservedload_regionperiod, + unservedload_total_currentsim, + unservedload_region_currentsim, + ) end function record!( acc::SMCShortfallAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} totalshortfall = 0 isshortfall = false edges = problem.fp.edges for r in problem.region_unserved_edges - regionshortfall = edges[r].flow isregionshortfall = regionshortfall > 0 - fit!(acc.periodsdropped_regionperiod[r,t], isregionshortfall) - fit!(acc.unservedload_regionperiod[r,t], regionshortfall) + fit!(acc.periodsdropped_regionperiod[r, t], isregionshortfall) + fit!(acc.unservedload_regionperiod[r, t], regionshortfall) if isregionshortfall - isshortfall = true totalshortfall += regionshortfall acc.periodsdropped_region_currentsim[r] += 1 acc.unservedload_region_currentsim[r] += regionshortfall - end - end if isshortfall @@ -117,7 +112,6 @@ function record!( fit!(acc.unservedload_period[t], totalshortfall) return - end function reset!(acc::SMCShortfallAccumulator, sampleid::Int) @@ -138,93 +132,94 @@ function reset!(acc::SMCShortfallAccumulator, sampleid::Int) fill!(acc.unservedload_region_currentsim, 0) return - end function finalize( acc::SMCShortfallAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} ep_total_mean, ep_total_std = mean_std(acc.periodsdropped_total) ep_region_mean, ep_region_std = mean_std(acc.periodsdropped_region) ep_period_mean, ep_period_std = mean_std(acc.periodsdropped_period) - ep_regionperiod_mean, ep_regionperiod_std = - mean_std(acc.periodsdropped_regionperiod) + ep_regionperiod_mean, ep_regionperiod_std = mean_std(acc.periodsdropped_regionperiod) _, ue_total_std = mean_std(acc.unservedload_total) _, ue_region_std = mean_std(acc.unservedload_region) _, ue_period_std = mean_std(acc.unservedload_period) - ue_regionperiod_mean, ue_regionperiod_std = - mean_std(acc.unservedload_regionperiod) + ue_regionperiod_mean, ue_regionperiod_std = mean_std(acc.unservedload_regionperiod) nsamples = first(acc.unservedload_total.stats).n - p2e = conversionfactor(L,T,P,E) - - return ShortfallResult{N,L,T,E}( - nsamples, system.regions.names, system.timestamps, - ep_total_mean, ep_total_std, ep_region_mean, ep_region_std, - ep_period_mean, ep_period_std, - ep_regionperiod_mean, ep_regionperiod_std, - p2e*ue_regionperiod_mean, p2e*ue_total_std, - p2e*ue_region_std, p2e*ue_period_std, p2e*ue_regionperiod_std) - + p2e = conversionfactor(L, T, P, E) + + return ShortfallResult{N, L, T, E}( + nsamples, + system.regions.names, + system.timestamps, + ep_total_mean, + ep_total_std, + ep_region_mean, + ep_region_std, + ep_period_mean, + ep_period_std, + ep_regionperiod_mean, + ep_regionperiod_std, + p2e * ue_regionperiod_mean, + p2e * ue_total_std, + p2e * ue_region_std, + p2e * ue_period_std, + p2e * ue_regionperiod_std, + ) end # ShortfallSamples struct SMCShortfallSamplesAccumulator <: - ResultAccumulator{SequentialMonteCarlo,ShortfallSamples} - - shortfall::Array{Int,3} - + ResultAccumulator{SequentialMonteCarlo, ShortfallSamples} + shortfall::Array{Int, 3} end -function merge!( - x::SMCShortfallSamplesAccumulator, y::SMCShortfallSamplesAccumulator -) - +function merge!(x::SMCShortfallSamplesAccumulator, y::SMCShortfallSamplesAccumulator) x.shortfall .+= y.shortfall return - end accumulatortype(::SequentialMonteCarlo, ::ShortfallSamples) = SMCShortfallSamplesAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::ShortfallSamples + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::ShortfallSamples, ) where {N} - nregions = length(sys.regions) shortfall = zeros(Int, nregions, N, simspec.nsamples) return SMCShortfallSamplesAccumulator(shortfall) - end function record!( acc::SMCShortfallSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} for (r, e) in enumerate(problem.region_unserved_edges) acc.shortfall[r, t, sampleid] = problem.fp.edges[e].flow end return - end reset!(acc::SMCShortfallSamplesAccumulator, sampleid::Int) = nothing function finalize( acc::SMCShortfallSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return ShortfallSamplesResult{N,L,T,P,E}( - system.regions.names, system.timestamps, acc.shortfall) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return ShortfallSamplesResult{N, L, T, P, E}( + system.regions.names, + system.timestamps, + acc.shortfall, + ) end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_surplus.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_surplus.jl index 4386e872..5384de2f 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_surplus.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_surplus.jl @@ -1,52 +1,42 @@ # Surplus -mutable struct SMCSurplusAccumulator <: ResultAccumulator{SequentialMonteCarlo,Surplus} +mutable struct SMCSurplusAccumulator <: ResultAccumulator{SequentialMonteCarlo, Surplus} # Cross-simulation surplus mean/variances surplus_period::Vector{MeanVariance} surplus_regionperiod::Matrix{MeanVariance} - end -function merge!( - x::SMCSurplusAccumulator, y::SMCSurplusAccumulator -) - +function merge!(x::SMCSurplusAccumulator, y::SMCSurplusAccumulator) foreach(merge!, x.surplus_period, y.surplus_period) foreach(merge!, x.surplus_regionperiod, y.surplus_regionperiod) return - end accumulatortype(::SequentialMonteCarlo, ::Surplus) = SMCSurplusAccumulator -function accumulator( - sys::SystemModel{N}, ::SequentialMonteCarlo, ::Surplus -) where {N} - +function accumulator(sys::SystemModel{N}, ::SequentialMonteCarlo, ::Surplus) where {N} nregions = length(sys.regions) surplus_period = [meanvariance() for _ in 1:N] surplus_regionperiod = [meanvariance() for _ in 1:nregions, _ in 1:N] - return SMCSurplusAccumulator( - surplus_period, surplus_regionperiod) - + return SMCSurplusAccumulator(surplus_period, surplus_regionperiod) end function record!( acc::SMCSurplusAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} totalsurplus = 0 edges = problem.fp.edges for (r, e_idx) in enumerate(problem.region_unused_edges) - regionsurplus = edges[e_idx].flow for s in system.region_stor_idxs[r] @@ -55,7 +45,6 @@ function record!( end for gs in system.region_genstor_idxs[r] - gse_discharge_idx = problem.genstorage_dischargeunused_edges[gs] gse_inflow_idx = problem.genstorage_inflowunused_edges[gs] @@ -63,80 +52,74 @@ function record!( total_unused = edges[gse_discharge_idx].flow + edges[gse_inflow_idx].flow regionsurplus += min(grid_limit, total_unused) - end - fit!(acc.surplus_regionperiod[r,t], regionsurplus) + fit!(acc.surplus_regionperiod[r, t], regionsurplus) totalsurplus += regionsurplus - end fit!(acc.surplus_period[t], totalsurplus) return - end reset!(acc::SMCSurplusAccumulator, sampleid::Int) = nothing function finalize( acc::SMCSurplusAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} _, period_std = mean_std(acc.surplus_period) regionperiod_mean, regionperiod_std = mean_std(acc.surplus_regionperiod) nsamples = first(first(acc.surplus_period).stats).n - return SurplusResult{N,L,T,P}( - nsamples, system.regions.names, system.timestamps, - regionperiod_mean, period_std, regionperiod_std) - + return SurplusResult{N, L, T, P}( + nsamples, + system.regions.names, + system.timestamps, + regionperiod_mean, + period_std, + regionperiod_std, + ) end # SurplusSamples struct SMCSurplusSamplesAccumulator <: - ResultAccumulator{SequentialMonteCarlo,SurplusSamples} - - surplus::Array{Int,3} - + ResultAccumulator{SequentialMonteCarlo, SurplusSamples} + surplus::Array{Int, 3} end -function merge!( - x::SMCSurplusSamplesAccumulator, y::SMCSurplusSamplesAccumulator -) - +function merge!(x::SMCSurplusSamplesAccumulator, y::SMCSurplusSamplesAccumulator) x.surplus .+= y.surplus return - end accumulatortype(::SequentialMonteCarlo, ::SurplusSamples) = SMCSurplusSamplesAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::SurplusSamples + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::SurplusSamples, ) where {N} - nregions = length(sys.regions) surplus = zeros(Int, nregions, N, simspec.nsamples) return SMCSurplusSamplesAccumulator(surplus) - end function record!( acc::SMCSurplusSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} edges = problem.fp.edges for (r, e) in enumerate(problem.region_unused_edges) - regionsurplus = edges[e].flow for s in system.region_stor_idxs[r] @@ -145,7 +128,6 @@ function record!( end for gs in system.region_genstor_idxs[r] - gse_discharge_idx = problem.genstorage_dischargeunused_edges[gs] gse_inflow_idx = problem.genstorage_inflowunused_edges[gs] @@ -153,25 +135,23 @@ function record!( total_unused = edges[gse_discharge_idx].flow + edges[gse_inflow_idx].flow regionsurplus += min(grid_limit, total_unused) - end acc.surplus[r, t, sampleid] = regionsurplus - end return - end reset!(acc::SMCSurplusSamplesAccumulator, sampleid::Int) = nothing function finalize( acc::SMCSurplusSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - return SurplusSamplesResult{N,L,T,P}( - system.regions.names, system.timestamps, acc.surplus) - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + return SurplusSamplesResult{N, L, T, P}( + system.regions.names, + system.timestamps, + acc.surplus, + ) end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_utilization.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_utilization.jl index 778cd8cf..f3de85cb 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_utilization.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/result_utilization.jl @@ -1,29 +1,20 @@ # Utilization -struct SMCUtilizationAccumulator <: ResultAccumulator{SequentialMonteCarlo,Utilization} - +struct SMCUtilizationAccumulator <: ResultAccumulator{SequentialMonteCarlo, Utilization} util_interface::Vector{MeanVariance} util_interfaceperiod::Matrix{MeanVariance} util_interface_currentsim::Vector{Float64} - end -function merge!( - x::SMCUtilizationAccumulator, y::SMCUtilizationAccumulator -) - +function merge!(x::SMCUtilizationAccumulator, y::SMCUtilizationAccumulator) foreach(merge!, x.util_interface, y.util_interface) foreach(merge!, x.util_interfaceperiod, y.util_interfaceperiod) - end accumulatortype(::SequentialMonteCarlo, ::Utilization) = SMCUtilizationAccumulator -function accumulator( - sys::SystemModel{N}, ::SequentialMonteCarlo, ::Utilization -) where {N} - +function accumulator(sys::SystemModel{N}, ::SequentialMonteCarlo, ::Utilization) where {N} n_interfaces = length(sys.interfaces) util_interface = [meanvariance() for _ in 1:n_interfaces] util_interfaceperiod = [meanvariance() for _ in 1:n_interfaces, _ in 1:N] @@ -31,46 +22,43 @@ function accumulator( util_interface_currentsim = zeros(Int, n_interfaces) return SMCUtilizationAccumulator( - util_interface, util_interfaceperiod, util_interface_currentsim) - + util_interface, + util_interfaceperiod, + util_interface_currentsim, + ) end function record!( acc::SMCUtilizationAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} edges = problem.fp.edges - for (i, (f, b)) in enumerate(zip(problem.interface_forward_edges, - problem.interface_reverse_edges)) - + for (i, (f, b)) in + enumerate(zip(problem.interface_forward_edges, problem.interface_reverse_edges)) util = utilization(problem.fp.edges[f], problem.fp.edges[b]) acc.util_interface_currentsim[i] += util - fit!(acc.util_interfaceperiod[i,t], util) - + fit!(acc.util_interfaceperiod[i, t], util) end - end function reset!(acc::SMCUtilizationAccumulator, sampleid::Int) - for i in eachindex(acc.util_interface_currentsim) fit!(acc.util_interface[i], acc.util_interface_currentsim[i]) acc.util_interface_currentsim[i] = 0 end - end function finalize( acc::SMCUtilizationAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - - nsamples = length(system.interfaces) > 0 ? - first(acc.util_interface[1].stats).n : nothing + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} + nsamples = + length(system.interfaces) > 0 ? first(acc.util_interface[1].stats).n : nothing util_mean, util_interfaceperiod_std = mean_std(acc.util_interfaceperiod) util_interface_std = last(mean_std(acc.util_interface)) / N @@ -78,80 +66,76 @@ function finalize( fromregions = getindex.(Ref(system.regions.names), system.interfaces.regions_from) toregions = getindex.(Ref(system.regions.names), system.interfaces.regions_to) - return UtilizationResult{N,L,T}( - nsamples, Pair.(fromregions, toregions), system.timestamps, - util_mean, util_interface_std, util_interfaceperiod_std) - + return UtilizationResult{N, L, T}( + nsamples, + Pair.(fromregions, toregions), + system.timestamps, + util_mean, + util_interface_std, + util_interfaceperiod_std, + ) end # UtilizationSamples struct SMCUtilizationSamplesAccumulator <: - ResultAccumulator{SequentialMonteCarlo,UtilizationSamples} - - utilization::Array{Float64,3} - + ResultAccumulator{SequentialMonteCarlo, UtilizationSamples} + utilization::Array{Float64, 3} end -function merge!( - x::SMCUtilizationSamplesAccumulator, y::SMCUtilizationSamplesAccumulator -) - +function merge!(x::SMCUtilizationSamplesAccumulator, y::SMCUtilizationSamplesAccumulator) x.utilization .+= y.utilization return - end -accumulatortype(::SequentialMonteCarlo, ::UtilizationSamples) = SMCUtilizationSamplesAccumulator +accumulatortype(::SequentialMonteCarlo, ::UtilizationSamples) = + SMCUtilizationSamplesAccumulator function accumulator( - sys::SystemModel{N}, simspec::SequentialMonteCarlo, ::UtilizationSamples + sys::SystemModel{N}, + simspec::SequentialMonteCarlo, + ::UtilizationSamples, ) where {N} - ninterfaces = length(sys.interfaces) utilization = zeros(Float64, ninterfaces, N, simspec.nsamples) return SMCUtilizationSamplesAccumulator(utilization) - end function record!( acc::SMCUtilizationSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, - state::SystemState, problem::DispatchProblem, - sampleid::Int, t::Int -) where {N,L,T,P,E} - - for (i, (e_f, e_r)) in enumerate(zip(problem.interface_forward_edges, - problem.interface_reverse_edges)) - + system::SystemModel{N, L, T, P, E}, + state::SystemState, + problem::DispatchProblem, + sampleid::Int, + t::Int, +) where {N, L, T, P, E} + for (i, (e_f, e_r)) in + enumerate(zip(problem.interface_forward_edges, problem.interface_reverse_edges)) acc.utilization[i, t, sampleid] = utilization(problem.fp.edges[e_f], problem.fp.edges[e_r]) - end return - end reset!(acc::SMCUtilizationSamplesAccumulator, sampleid::Int) = nothing function finalize( acc::SMCUtilizationSamplesAccumulator, - system::SystemModel{N,L,T,P,E}, -) where {N,L,T,P,E} - + system::SystemModel{N, L, T, P, E}, +) where {N, L, T, P, E} fromregions = getindex.(Ref(system.regions.names), system.interfaces.regions_from) toregions = getindex.(Ref(system.regions.names), system.interfaces.regions_to) - return UtilizationSamplesResult{N,L,T}( - Pair.(fromregions, toregions), system.timestamps, acc.utilization) - + return UtilizationSamplesResult{N, L, T}( + Pair.(fromregions, toregions), + system.timestamps, + acc.utilization, + ) end - function utilization(f::MinCostFlows.Edge, b::MinCostFlows.Edge) - flow_forward = f.flow max_forward = f.limit @@ -159,9 +143,9 @@ function utilization(f::MinCostFlows.Edge, b::MinCostFlows.Edge) max_back = b.limit util = if flow_forward > 0 - flow_forward/max_forward + flow_forward / max_forward elseif flow_back > 0 - flow_back/max_back + flow_back / max_back elseif iszero(max_forward) && iszero(max_back) 1.0 else @@ -169,5 +153,4 @@ function utilization(f::MinCostFlows.Edge, b::MinCostFlows.Edge) end return util - end diff --git a/src/ResourceAdequacy/simulations/sequentialmontecarlo/utils.jl b/src/ResourceAdequacy/simulations/sequentialmontecarlo/utils.jl index 8bf81bd7..fd43e010 100644 --- a/src/ResourceAdequacy/simulations/sequentialmontecarlo/utils.jl +++ b/src/ResourceAdequacy/simulations/sequentialmontecarlo/utils.jl @@ -1,10 +1,11 @@ function initialize_availability!( rng::AbstractRNG, - availability::Vector{Bool}, nexttransition::Vector{Int}, - devices::AbstractAssets, t_last::Int) - + availability::Vector{Bool}, + nexttransition::Vector{Int}, + devices::AbstractAssets, + t_last::Int, +) for i in 1:length(devices) - λ = devices.λ[i, 1] μ = devices.μ[i, 1] online = rand(rng) < μ / (λ + μ) @@ -12,61 +13,58 @@ function initialize_availability!( availability[i] = online transitionprobs = online ? devices.λ : devices.μ - nexttransition[i] = randtransitiontime( - rng, transitionprobs, i, 1, t_last) - + nexttransition[i] = randtransitiontime(rng, transitionprobs, i, 1, t_last) end return availability - end function update_availability!( rng::AbstractRNG, - availability::Vector{Bool}, nexttransition::Vector{Int}, - devices::AbstractAssets, t_now::Int, t_last::Int) - + availability::Vector{Bool}, + nexttransition::Vector{Int}, + devices::AbstractAssets, + t_now::Int, + t_last::Int, +) for i in 1:length(devices) - if nexttransition[i] == t_now # Unit switches states transitionprobs = (availability[i] ⊻= true) ? devices.λ : devices.μ - nexttransition[i] = randtransitiontime( - rng, transitionprobs, i, t_now, t_last) + nexttransition[i] = randtransitiontime(rng, transitionprobs, i, t_now, t_last) end - end - end function randtransitiontime( - rng::AbstractRNG, p::Matrix{Float64}, - i::Int, t_now::Int, t_last::Int + rng::AbstractRNG, + p::Matrix{Float64}, + i::Int, + t_now::Int, + t_last::Int, ) - - cdf = 0. - p_noprevtransition = 1. + cdf = 0.0 + p_noprevtransition = 1.0 x = rand(rng) t = t_now + 1 while t <= t_last - p_it = p[i,t] + p_it = p[i, t] cdf += p_noprevtransition * p_it x < cdf && return t - p_noprevtransition *= (1. - p_it) + p_noprevtransition *= (1.0 - p_it) t += 1 end return t_last + 1 - end function available_capacity( availability::Vector{Bool}, lines::Lines, - idxs::UnitRange{Int}, t::Int + idxs::UnitRange{Int}, + t::Int, ) - avcap_forward = 0 avcap_backward = 0 @@ -78,15 +76,14 @@ function available_capacity( end return avcap_forward, avcap_backward - end function available_capacity( availability::Vector{Bool}, gens::Generators, - idxs::UnitRange{Int}, t::Int + idxs::UnitRange{Int}, + t::Int, ) - caps = gens.capacity avcap = 0 @@ -95,35 +92,24 @@ function available_capacity( end return avcap - end -function update_energy!( - stors_energy::Vector{Int}, - stors::AbstractAssets, - t::Int -) - +function update_energy!(stors_energy::Vector{Int}, stors::AbstractAssets, t::Int) for i in 1:length(stors_energy) - soc = stors_energy[i] - efficiency = stors.carryover_efficiency[i,t] - maxenergy = stors.energy_capacity[i,t] + efficiency = stors.carryover_efficiency[i, t] + maxenergy = stors.energy_capacity[i, t] # Decay SoC soc = round(Int, soc * efficiency) # Shed SoC above current energy limit stors_energy[i] = min(soc, maxenergy) - end - end function maxtimetocharge_discharge(system::SystemModel) - if length(system.storages) > 0 - if any(iszero, system.storages.charge_capacity) stor_charge_max = length(system.timestamps) + 1 else @@ -141,19 +127,17 @@ function maxtimetocharge_discharge(system::SystemModel) end else - stor_charge_max = 0 stor_discharge_max = 0 - end if length(system.generatorstorages) > 0 - if any(iszero, system.generatorstorages.charge_capacity) genstor_charge_max = length(system.timestamps) + 1 else genstor_charge_durations = - system.generatorstorages.energy_capacity ./ system.generatorstorages.charge_capacity + system.generatorstorages.energy_capacity ./ + system.generatorstorages.charge_capacity genstor_charge_max = ceil(Int, maximum(genstor_charge_durations)) end @@ -161,18 +145,18 @@ function maxtimetocharge_discharge(system::SystemModel) genstor_discharge_max = length(system.timestamps) + 1 else genstor_discharge_durations = - system.generatorstorages.energy_capacity ./ system.generatorstorages.discharge_capacity + system.generatorstorages.energy_capacity ./ + system.generatorstorages.discharge_capacity genstor_discharge_max = ceil(Int, maximum(genstor_discharge_durations)) end else - genstor_charge_max = 0 genstor_discharge_max = 0 - end - return (max(stor_charge_max, genstor_charge_max), - max(stor_discharge_max, genstor_discharge_max)) - + return ( + max(stor_charge_max, genstor_charge_max), + max(stor_discharge_max, genstor_discharge_max), + ) end diff --git a/src/ResourceAdequacy/utils.jl b/src/ResourceAdequacy/utils.jl index 4fc10a07..c6703bf0 100644 --- a/src/ResourceAdequacy/utils.jl +++ b/src/ResourceAdequacy/utils.jl @@ -6,7 +6,6 @@ function mean_std(x::MeanVariance) end function mean_std(x::AbstractArray{<:MeanVariance}) - means = similar(x, Float64) vars = similar(means) @@ -17,7 +16,6 @@ function mean_std(x::AbstractArray{<:MeanVariance}) end return means, vars - end function findfirstunique_directional(a::AbstractVector{<:Pair}, i::Pair) @@ -31,7 +29,7 @@ function findfirstunique_directional(a::AbstractVector{<:Pair}, i::Pair) return i_idx, reverse end -function findfirstunique(a::AbstractVector{T}, i::T) where T +function findfirstunique(a::AbstractVector{T}, i::T) where {T} i_idx = findfirst(isequal(i), a) i_idx === nothing && throw(BoundsError(a)) return i_idx diff --git a/test/CapacityCredit/runtests.jl b/test/CapacityCredit/runtests.jl index d3084d00..11706f07 100644 --- a/test/CapacityCredit/runtests.jl +++ b/test/CapacityCredit/runtests.jl @@ -1,44 +1,46 @@ @testset "CapacityCredit" begin - empty_str = String[] empty_int(x) = Matrix{Int}(undef, 0, x) empty_float(x) = Matrix{Float64}(undef, 0, x) - gens = Generators{4,1,Hour,MW}( + gens = Generators{4, 1, Hour, MW}( ["Gen1", "Gen2", "Gen3", "VG"], ["Gens", "Gens", "Gens", "VG"], [fill(10, 3, 4); [5 6 7 8]], [fill(0.1, 3, 4); fill(0.0, 1, 4)], - [fill(0.9, 3, 4); fill(1.0, 1, 4)] + [fill(0.9, 3, 4); fill(1.0, 1, 4)], ) - gens_after = Generators{4,1,Hour,MW}( + gens_after = Generators{4, 1, Hour, MW}( ["Gen1", "Gen2", "Gen3", "Gen4", "VG"], ["Gens", "Gens", "Gens", "Gens", "VG"], [fill(10, 4, 4); [5 6 7 8]], [fill(0.1, 4, 4); fill(0.0, 1, 4)], - [fill(0.9, 4, 4); fill(1.0, 1, 4)] + [fill(0.9, 4, 4); fill(1.0, 1, 4)], ) - emptystors = Storages{4,1,Hour,MW,MWh}((empty_str for _ in 1:2)..., - (empty_int(4) for _ in 1:3)..., - (empty_float(4) for _ in 1:5)...) + emptystors = Storages{4, 1, Hour, MW, MWh}( + (empty_str for _ in 1:2)..., + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:5)..., + ) - emptygenstors = GeneratorStorages{4,1,Hour,MW,MWh}( + emptygenstors = GeneratorStorages{4, 1, Hour, MW, MWh}( (empty_str for _ in 1:2)..., - (empty_int(4) for _ in 1:3)..., (empty_float(4) for _ in 1:3)..., - (empty_int(4) for _ in 1:3)..., (empty_float(4) for _ in 1:2)...) + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:3)..., + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:2)..., + ) load = [25, 28, 27, 24] tz = TimeZone("UTC") - timestamps = ZonedDateTime(2010,1,1,0,tz):Hour(1):ZonedDateTime(2010,1,1,3,tz) + timestamps = ZonedDateTime(2010, 1, 1, 0, tz):Hour(1):ZonedDateTime(2010, 1, 1, 3, tz) - sys_before = SystemModel( - gens, emptystors, emptygenstors, timestamps, load) + sys_before = SystemModel(gens, emptystors, emptygenstors, timestamps, load) - sys_after = SystemModel( - gens_after, emptystors, emptygenstors, timestamps, load) + sys_after = SystemModel(gens_after, emptystors, emptygenstors, timestamps, load) threenode2 = deepcopy(TestSystems.threenode) threenode2.generators.capacity[1, :] .+= 5 @@ -47,7 +49,6 @@ smc = SequentialMonteCarlo(samples=100_000, threaded=false) @testset "EFC" begin - cc = assess(sys_before, sys_before, EFC{EUE}(10, "Region"), conv) @test extrema(cc) == (0, 1) @@ -59,12 +60,9 @@ cc = assess(TestSystems.threenode, threenode2, EFC{EUE}(10, "Region A"), smc) @test extrema(cc) == (3, 4) - end @testset "ELCC" begin - - cc = assess(sys_before, sys_before, ELCC{EUE}(10, "Region"), conv) @test extrema(cc) == (0, 1) @@ -76,7 +74,5 @@ cc = assess(TestSystems.threenode, threenode2, ELCC{EUE}(10, "Region A"), smc) @test extrema(cc) == (3, 4) - end - end diff --git a/test/PRASBase/SystemModel.jl b/test/PRASBase/SystemModel.jl index 1a11697b..5d8fddf8 100644 --- a/test/PRASBase/SystemModel.jl +++ b/test/PRASBase/SystemModel.jl @@ -1,37 +1,58 @@ @testset "SystemModel" begin + generators = Generators{10, 1, Hour, MW}( + ["Gen A", "Gen B"], + ["CC", "CT"], + rand(1:10, 2, 10), + fill(0.1, 2, 10), + fill(0.1, 2, 10), + ) - generators = Generators{10,1,Hour,MW}( - ["Gen A", "Gen B"], ["CC", "CT"], - rand(1:10, 2, 10), fill(0.1, 2, 10), fill(0.1, 2, 10)) + storages = Storages{10, 1, Hour, MW, MWh}( + ["S1", "S2"], + ["Battery", "Pumped Hydro"], + rand(1:10, 2, 10), + rand(1:10, 2, 10), + rand(1:10, 2, 10), + fill(0.9, 2, 10), + fill(1.0, 2, 10), + fill(0.99, 2, 10), + fill(0.1, 2, 10), + fill(0.5, 2, 10), + ) - storages = Storages{10,1,Hour,MW,MWh}( - ["S1", "S2"], ["Battery", "Pumped Hydro"], - rand(1:10, 2, 10), rand(1:10, 2, 10), rand(1:10, 2, 10), - fill(0.9, 2, 10), fill(1.0, 2, 10), fill(0.99, 2, 10), - fill(0.1, 2, 10), fill(0.5, 2, 10)) + generatorstorages = GeneratorStorages{10, 1, Hour, MW, MWh}( + ["GS1"], + ["CSP"], + rand(1:10, 1, 10), + rand(1:10, 1, 10), + rand(1:10, 1, 10), + fill(0.9, 1, 10), + fill(1.0, 1, 10), + fill(0.99, 1, 10), + rand(1:10, 1, 10), + rand(1:10, 1, 10), + rand(1:10, 1, 10), + fill(0.1, 1, 10), + fill(0.5, 1, 10), + ) - generatorstorages = GeneratorStorages{10,1,Hour,MW,MWh}( - ["GS1"], ["CSP"], - rand(1:10, 1, 10), rand(1:10, 1, 10), rand(1:10, 1, 10), - fill(0.9, 1, 10), fill(1.0, 1, 10), fill(0.99, 1, 10), - rand(1:10, 1, 10), rand(1:10, 1, 10), rand(1:10, 1, 10), - fill(0.1, 1, 10), fill(0.5, 1, 10)) - - timestamps = DateTime(2020, 1, 1, 0):Hour(1):DateTime(2020,1,1,9) + timestamps = DateTime(2020, 1, 1, 0):Hour(1):DateTime(2020, 1, 1, 9) # Single-region constructor - SystemModel( - generators, storages, generatorstorages, timestamps, rand(1:20, 10)) + SystemModel(generators, storages, generatorstorages, timestamps, rand(1:20, 10)) - regions = Regions{10,MW}( - ["Region A", "Region B"], rand(1:20, 2, 10)) + regions = Regions{10, MW}(["Region A", "Region B"], rand(1:20, 2, 10)) - interfaces = Interfaces{10,MW}( - [1], [2], fill(100, 1, 10), fill(100, 1, 10)) + interfaces = Interfaces{10, MW}([1], [2], fill(100, 1, 10), fill(100, 1, 10)) - lines = Lines{10,1,Hour,MW}( - ["Line 1", "Line 2"], ["Line", "Line"], - fill(10, 2, 10), fill(10, 2, 10), fill(0., 2, 10), fill(1.0, 2, 10)) + lines = Lines{10, 1, Hour, MW}( + ["Line 1", "Line 2"], + ["Line", "Line"], + fill(10, 2, 10), + fill(10, 2, 10), + fill(0.0, 2, 10), + fill(1.0, 2, 10), + ) gen_regions = [1:1, 2:2] stor_regions = [1:0, 1:2] @@ -40,10 +61,16 @@ # Multi-region constructor SystemModel( - regions, interfaces, - generators, gen_regions, storages, stor_regions, - generatorstorages, genstor_regions, - lines, line_interfaces, - timestamps) - + regions, + interfaces, + generators, + gen_regions, + storages, + stor_regions, + generatorstorages, + genstor_regions, + lines, + line_interfaces, + timestamps, + ) end diff --git a/test/PRASBase/assets.jl b/test/PRASBase/assets.jl index d2b38275..9ea0c7ff 100644 --- a/test/PRASBase/assets.jl +++ b/test/PRASBase/assets.jl @@ -1,101 +1,239 @@ @testset "Assets" begin - names = ["A1", "A2", "B1"] categories = ["A", "A", "B"] - vals_int = rand(1:100, 3, 10) + vals_int = rand(1:100, 3, 10) vals_float = rand(3, 10) @testset "Generators" begin - - Generators{10,1,Hour,MW}( - names, categories, vals_int, vals_float, vals_float) - - @test_throws AssertionError Generators{5,1,Hour,MW}( - names, categories, vals_int, vals_float, vals_float) - - @test_throws AssertionError Generators{10,1,Hour,MW}( - names[1:2], categories, vals_int, vals_float, vals_float) - - @test_throws AssertionError Generators{10,1,Hour,MW}( - names[1:2], categories[1:2], vals_int, vals_float, vals_float) - - @test_throws AssertionError Generators{10,1,Hour,MW}( - names, categories, -vals_int, vals_float, vals_float) - + Generators{10, 1, Hour, MW}(names, categories, vals_int, vals_float, vals_float) + + @test_throws AssertionError Generators{5, 1, Hour, MW}( + names, + categories, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Generators{10, 1, Hour, MW}( + names[1:2], + categories, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Generators{10, 1, Hour, MW}( + names[1:2], + categories[1:2], + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Generators{10, 1, Hour, MW}( + names, + categories, + -vals_int, + vals_float, + vals_float, + ) end @testset "Storages" begin - - Storages{10,1,Hour,MW,MWh}( - names, categories, vals_int, vals_int, vals_int, - vals_float, vals_float, vals_float, vals_float, vals_float) - - @test_throws AssertionError Storages{5,1,Hour,MW,MWh}( - names, categories, vals_int, vals_int, vals_int, - vals_float, vals_float, vals_float, vals_float, vals_float) - - @test_throws AssertionError Storages{10,1,Hour,MW,MWh}( - names, categories[1:2], vals_int, vals_int, vals_int, - vals_float, vals_float, vals_float, vals_float, vals_float) - - @test_throws AssertionError Storages{10,1,Hour,MW,MWh}( - names[1:2], categories[1:2], vals_int, vals_int, vals_int, - vals_float, vals_float, vals_float, vals_float, vals_float) - - @test_throws AssertionError Storages{10,1,Hour,MW,MWh}( - names, categories, vals_int, vals_int, vals_int, - vals_float, vals_float, -vals_float, vals_float, vals_float) - + Storages{10, 1, Hour, MW, MWh}( + names, + categories, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_float, + vals_float, + ) + + @test_throws AssertionError Storages{5, 1, Hour, MW, MWh}( + names, + categories, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_float, + vals_float, + ) + + @test_throws AssertionError Storages{10, 1, Hour, MW, MWh}( + names, + categories[1:2], + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_float, + vals_float, + ) + + @test_throws AssertionError Storages{10, 1, Hour, MW, MWh}( + names[1:2], + categories[1:2], + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_float, + vals_float, + ) + + @test_throws AssertionError Storages{10, 1, Hour, MW, MWh}( + names, + categories, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + -vals_float, + vals_float, + vals_float, + ) end @testset "GeneratorStorages" begin - - GeneratorStorages{10,1,Hour,MW,MWh}( - names, categories, - vals_int, vals_int, vals_int, vals_float, vals_float, vals_float, - vals_int, vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError GeneratorStorages{5,1,Hour,MW,MWh}( - names, categories, - vals_int, vals_int, vals_int, vals_float, vals_float, vals_float, - vals_int, vals_int, vals_int, vals_float, vals_float) - - - @test_throws AssertionError GeneratorStorages{10,1,Hour,MW,MWh}( - names, categories[1:2], - vals_int, vals_int, vals_int, vals_float, vals_float, vals_float, - vals_int, vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError GeneratorStorages{10,1,Hour,MW,MWh}( - names[1:2], categories[1:2], - vals_int, vals_int, vals_int, vals_float, vals_float, vals_float, - vals_int, vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError GeneratorStorages{10,1,Hour,MW,MWh}( - names, categories, - vals_int, vals_int, vals_int, vals_float, vals_float, -vals_float, - vals_int, vals_int, vals_int, vals_float, vals_float) - + GeneratorStorages{10, 1, Hour, MW, MWh}( + names, + categories, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError GeneratorStorages{5, 1, Hour, MW, MWh}( + names, + categories, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError GeneratorStorages{10, 1, Hour, MW, MWh}( + names, + categories[1:2], + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError GeneratorStorages{10, 1, Hour, MW, MWh}( + names[1:2], + categories[1:2], + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + vals_float, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError GeneratorStorages{10, 1, Hour, MW, MWh}( + names, + categories, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + -vals_float, + vals_int, + vals_int, + vals_int, + vals_float, + vals_float, + ) end @testset "Lines" begin - - Lines{10,1,Hour,MW}( - names, categories, vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError Lines{5,1,Hour,MW}( - names, categories, vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError Lines{10,1,Hour,MW}( - names[1:2], categories, vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError Lines{10,1,Hour,MW}( - names[1:2], categories[1:2], vals_int, vals_int, vals_float, vals_float) - - @test_throws AssertionError Lines{10,1,Hour,MW}( - names, categories, -vals_int, vals_int, vals_float, vals_float) - + Lines{10, 1, Hour, MW}( + names, + categories, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Lines{5, 1, Hour, MW}( + names, + categories, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Lines{10, 1, Hour, MW}( + names[1:2], + categories, + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Lines{10, 1, Hour, MW}( + names[1:2], + categories[1:2], + vals_int, + vals_int, + vals_float, + vals_float, + ) + + @test_throws AssertionError Lines{10, 1, Hour, MW}( + names, + categories, + -vals_int, + vals_int, + vals_float, + vals_float, + ) end - end diff --git a/test/PRASBase/collections.jl b/test/PRASBase/collections.jl index e610ef21..330a8357 100644 --- a/test/PRASBase/collections.jl +++ b/test/PRASBase/collections.jl @@ -1,37 +1,52 @@ @testset "Collections" begin - @testset "Regions" begin - - Regions{10,MW}(["Region A", "Region B"], rand(1:10, 2, 10)) - - @test_throws AssertionError Regions{10,MW}( - ["Region A", "Region B"], rand(1:10, 2, 5)) - - @test_throws AssertionError Regions{10,MW}( - ["Region A", "Region B"], rand(1:10, 3, 10)) - - @test_throws AssertionError Regions{10,MW}( - ["Region A", "Region B"], -rand(1:10, 2, 10)) - + Regions{10, MW}(["Region A", "Region B"], rand(1:10, 2, 10)) + + @test_throws AssertionError Regions{10, MW}( + ["Region A", "Region B"], + rand(1:10, 2, 5), + ) + + @test_throws AssertionError Regions{10, MW}( + ["Region A", "Region B"], + rand(1:10, 3, 10), + ) + + @test_throws AssertionError Regions{10, MW}( + ["Region A", "Region B"], + -rand(1:10, 2, 10), + ) end @testset "Interfaces" begin - - Interfaces{10,MW}( - [1,1,2], [2,3,3], rand(1:15, 3, 10), rand(1:15, 3, 10)) - - @test_throws AssertionError Interfaces{10,MW}( - [1,1,2], [2,3], rand(1:15, 3, 10), rand(1:15, 3, 10)) - - @test_throws AssertionError Interfaces{10,MW}( - [1,1,2], [2,3,3], rand(1:15, 2, 10), rand(1:15, 2, 10)) - - @test_throws AssertionError Interfaces{10,MW}( - [1,1,2], [2,3,3], rand(1:15, 3, 11), rand(1:15, 3, 11)) - - @test_throws AssertionError Interfaces{10,MW}( - [1,1,2], [2,3,3], rand(1:15, 3, 10), -rand(1:15, 3, 10)) - + Interfaces{10, MW}([1, 1, 2], [2, 3, 3], rand(1:15, 3, 10), rand(1:15, 3, 10)) + + @test_throws AssertionError Interfaces{10, MW}( + [1, 1, 2], + [2, 3], + rand(1:15, 3, 10), + rand(1:15, 3, 10), + ) + + @test_throws AssertionError Interfaces{10, MW}( + [1, 1, 2], + [2, 3, 3], + rand(1:15, 2, 10), + rand(1:15, 2, 10), + ) + + @test_throws AssertionError Interfaces{10, MW}( + [1, 1, 2], + [2, 3, 3], + rand(1:15, 3, 11), + rand(1:15, 3, 11), + ) + + @test_throws AssertionError Interfaces{10, MW}( + [1, 1, 2], + [2, 3, 3], + rand(1:15, 3, 10), + -rand(1:15, 3, 10), + ) end - end diff --git a/test/PRASBase/io.jl b/test/PRASBase/io.jl index 828f961b..3fa6817b 100644 --- a/test/PRASBase/io.jl +++ b/test/PRASBase/io.jl @@ -12,5 +12,4 @@ savemodel(rts, path * "/rts2.pras") rts2 = SystemModel(path * "/rts2.pras") @test rts == rts2 - end diff --git a/test/PRASBase/runtests.jl b/test/PRASBase/runtests.jl index b37b8ccd..8e830b18 100644 --- a/test/PRASBase/runtests.jl +++ b/test/PRASBase/runtests.jl @@ -1,9 +1,7 @@ @testset "PRASBase" begin - include("units.jl") include("assets.jl") include("collections.jl") include("SystemModel.jl") include("io.jl") - end diff --git a/test/PRASBase/units.jl b/test/PRASBase/units.jl index f4e9439d..f1b235c2 100644 --- a/test/PRASBase/units.jl +++ b/test/PRASBase/units.jl @@ -1,5 +1,4 @@ @testset "Units and Conversions" begin - @test powertoenergy(10, MW, 2, Hour, MWh) == 20 @test powertoenergy(10, MW, 30, Minute, MWh) == 5 @@ -17,5 +16,4 @@ @test unitsymbol(Hour) == "h" @test unitsymbol(Day) == "d" @test unitsymbol(Year) == "y" - end diff --git a/test/ResourceAdequacy/metrics.jl b/test/ResourceAdequacy/metrics.jl index 96a15249..b67f8b9f 100644 --- a/test/ResourceAdequacy/metrics.jl +++ b/test/ResourceAdequacy/metrics.jl @@ -1,63 +1,54 @@ @testset "Metrics" begin - @testset "MeanEstimate" begin - - me1 = MeanEstimate(513.1, 26) - @test val(me1) == 513.1 - @test stderror(me1) == 26. - @test string(me1) == "510±30" - - me2 = MeanEstimate(0.03, 0.0001) - me3 = MeanEstimate(0.03, 0.001, 100) - @test me2 ≈ me2 - @test val(me2) == 0.03 - @test stderror(me2) == 0.0001 - @test string(me2) == "0.0300±0.0001" - - me4 = MeanEstimate(2.4) - @test val(me4) == 2.4 - @test stderror(me4) == 0. - @test string(me4) == "2.40000" - - me5 = MeanEstimate([1,2,3,4,5]) - @test val(me5) == 3. - @test stderror(me5) ≈ sqrt(0.5) - - me6 = MeanEstimate(-503.1, 260) - @test val(me6) == -503.1 - @test stderror(me6) == 260. - @test string(me6) == "-500±300" - - @test_throws DomainError MeanEstimate(1.23, -0.002) - + me1 = MeanEstimate(513.1, 26) + @test val(me1) == 513.1 + @test stderror(me1) == 26.0 + @test string(me1) == "510±30" + + me2 = MeanEstimate(0.03, 0.0001) + me3 = MeanEstimate(0.03, 0.001, 100) + @test me2 ≈ me2 + @test val(me2) == 0.03 + @test stderror(me2) == 0.0001 + @test string(me2) == "0.0300±0.0001" + + me4 = MeanEstimate(2.4) + @test val(me4) == 2.4 + @test stderror(me4) == 0.0 + @test string(me4) == "2.40000" + + me5 = MeanEstimate([1, 2, 3, 4, 5]) + @test val(me5) == 3.0 + @test stderror(me5) ≈ sqrt(0.5) + + me6 = MeanEstimate(-503.1, 260) + @test val(me6) == -503.1 + @test stderror(me6) == 260.0 + @test string(me6) == "-500±300" + + @test_throws DomainError MeanEstimate(1.23, -0.002) end @testset "LOLE" begin - - lole1 = LOLE{4380,2,Hour}(MeanEstimate(1.2)) + lole1 = LOLE{4380, 2, Hour}(MeanEstimate(1.2)) @test string(lole1) == "LOLE = 1.20000 event-(2h)/8760h" - lole2 = LOLE{8760,1,Hour}(MeanEstimate(2.4, 0.1)) + lole2 = LOLE{8760, 1, Hour}(MeanEstimate(2.4, 0.1)) @test string(lole2) == "LOLE = 2.4±0.1 event-h/8760h" - lole3 = LOLE{3650,1,Day}(MeanEstimate(1.0, 0.01)) + lole3 = LOLE{3650, 1, Day}(MeanEstimate(1.0, 0.01)) @test string(lole3) == "LOLE = 1.00±0.01 event-d/3650d" - @test_throws DomainError LOLE{3650,1,Day}(MeanEstimate(-1.2, 0.)) - - + @test_throws DomainError LOLE{3650, 1, Day}(MeanEstimate(-1.2, 0.0)) end @testset "EUE" begin - - eue1 = EUE{2,1,Hour,MWh}(MeanEstimate(1.2)) + eue1 = EUE{2, 1, Hour, MWh}(MeanEstimate(1.2)) @test string(eue1) == "EUE = 1.20000 MWh/2h" - eue2 = EUE{1,2,Year,GWh}(MeanEstimate(17.2, 1.3)) + eue2 = EUE{1, 2, Year, GWh}(MeanEstimate(17.2, 1.3)) @test string(eue2) == "EUE = 17±1 GWh/2y" - @test_throws DomainError EUE{1,1,Hour,MWh}(MeanEstimate(-1.2)) - + @test_throws DomainError EUE{1, 1, Hour, MWh}(MeanEstimate(-1.2)) end - end diff --git a/test/ResourceAdequacy/results/availability.jl b/test/ResourceAdequacy/results/availability.jl index 8d88ed63..6eaef176 100644 --- a/test/ResourceAdequacy/results/availability.jl +++ b/test/ResourceAdequacy/results/availability.jl @@ -1,14 +1,16 @@ @testset "AvailabilityResult" begin - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - available = rand(Bool, DD.nresources, N, DD.nsamples) + available = rand(Bool, DD.nresources, N, DD.nsamples) # Generators - result = ResourceAdequacy.GeneratorAvailabilityResult{N,1,Hour}( - DD.resourcenames, DD.periods, available) + result = ResourceAdequacy.GeneratorAvailabilityResult{N, 1, Hour}( + DD.resourcenames, + DD.periods, + available, + ) @test length(result[r, t]) == DD.nsamples @test result[r, t] ≈ vec(available[r_idx, t_idx, :]) @@ -19,8 +21,11 @@ # Storages - result = ResourceAdequacy.StorageAvailabilityResult{N,1,Hour}( - DD.resourcenames, DD.periods, available) + result = ResourceAdequacy.StorageAvailabilityResult{N, 1, Hour}( + DD.resourcenames, + DD.periods, + available, + ) @test length(result[r, t]) == DD.nsamples @test result[r, t] ≈ vec(available[r_idx, t_idx, :]) @@ -31,8 +36,11 @@ # GeneratorStorages - result = ResourceAdequacy.GeneratorStorageAvailabilityResult{N,1,Hour}( - DD.resourcenames, DD.periods, available) + result = ResourceAdequacy.GeneratorStorageAvailabilityResult{N, 1, Hour}( + DD.resourcenames, + DD.periods, + available, + ) @test length(result[r, t]) == DD.nsamples @test result[r, t] ≈ vec(available[r_idx, t_idx, :]) @@ -43,8 +51,11 @@ # Lines - result = ResourceAdequacy.LineAvailabilityResult{N,1,Hour}( - DD.resourcenames, DD.periods, available) + result = ResourceAdequacy.LineAvailabilityResult{N, 1, Hour}( + DD.resourcenames, + DD.periods, + available, + ) @test length(result[r, t]) == DD.nsamples @test result[r, t] ≈ vec(available[r_idx, t_idx, :]) @@ -52,5 +63,4 @@ @test_throws BoundsError result[r, t_bad] @test_throws BoundsError result[r_bad, t] @test_throws BoundsError result[r_bad, t_bad] - end diff --git a/test/ResourceAdequacy/results/energy.jl b/test/ResourceAdequacy/results/energy.jl index f5e5a28e..da7678b5 100644 --- a/test/ResourceAdequacy/results/energy.jl +++ b/test/ResourceAdequacy/results/energy.jl @@ -1,18 +1,22 @@ @testset "EnergyResult" begin - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod # Storages - result = ResourceAdequacy.StorageEnergyResult{N,1,Hour,MWh}( - DD.nsamples, DD.resourcenames, DD.periods, - DD.d1_resourceperiod, DD.d2_period, DD.d2_resourceperiod) + result = ResourceAdequacy.StorageEnergyResult{N, 1, Hour, MWh}( + DD.nsamples, + DD.resourcenames, + DD.periods, + DD.d1_resourceperiod, + DD.d2_period, + DD.d2_resourceperiod, + ) @test result[t] ≈ (sum(DD.d1_resourceperiod[:, t_idx]), DD.d2_period[t_idx]) @test result[r, t] ≈ - (DD.d1_resourceperiod[r_idx, t_idx], DD.d2_resourceperiod[r_idx, t_idx]) + (DD.d1_resourceperiod[r_idx, t_idx], DD.d2_resourceperiod[r_idx, t_idx]) @test_throws BoundsError result[t_bad] @test_throws BoundsError result[r, t_bad] @@ -21,31 +25,37 @@ # GeneratorStorages - result = ResourceAdequacy.GeneratorStorageEnergyResult{N,1,Hour,MWh}( - DD.nsamples, DD.resourcenames, DD.periods, - DD.d1_resourceperiod, DD.d2_period, DD.d2_resourceperiod) + result = ResourceAdequacy.GeneratorStorageEnergyResult{N, 1, Hour, MWh}( + DD.nsamples, + DD.resourcenames, + DD.periods, + DD.d1_resourceperiod, + DD.d2_period, + DD.d2_resourceperiod, + ) @test result[t] ≈ (sum(DD.d1_resourceperiod[:, t_idx]), DD.d2_period[t_idx]) @test result[r, t] ≈ - (DD.d1_resourceperiod[r_idx, t_idx], DD.d2_resourceperiod[r_idx, t_idx]) + (DD.d1_resourceperiod[r_idx, t_idx], DD.d2_resourceperiod[r_idx, t_idx]) @test_throws BoundsError result[t_bad] @test_throws BoundsError result[r, t_bad] @test_throws BoundsError result[r_bad, t] @test_throws BoundsError result[r_bad, t_bad] - end @testset "EnergySamplesResult" begin - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod # Storages - result = ResourceAdequacy.StorageEnergySamplesResult{N,1,Hour,MWh}( - DD.resourcenames, DD.periods, DD.d) + result = ResourceAdequacy.StorageEnergySamplesResult{N, 1, Hour, MWh}( + DD.resourcenames, + DD.periods, + DD.d, + ) @test length(result[t]) == DD.nsamples @test result[t] ≈ vec(sum(view(DD.d, :, t_idx, :), dims=1)) @@ -60,8 +70,11 @@ end # GeneratorStorages - result = ResourceAdequacy.GeneratorStorageEnergySamplesResult{N,1,Hour,MWh}( - DD.resourcenames, DD.periods, DD.d) + result = ResourceAdequacy.GeneratorStorageEnergySamplesResult{N, 1, Hour, MWh}( + DD.resourcenames, + DD.periods, + DD.d, + ) @test length(result[t]) == DD.nsamples @test result[t] ≈ vec(sum(view(DD.d, :, t_idx, :), dims=1)) @@ -73,5 +86,4 @@ end @test_throws BoundsError result[r, t_bad] @test_throws BoundsError result[r_bad, t] @test_throws BoundsError result[r_bad, t_bad] - end diff --git a/test/ResourceAdequacy/results/flow.jl b/test/ResourceAdequacy/results/flow.jl index fb1b7ab6..7e24adb6 100644 --- a/test/ResourceAdequacy/results/flow.jl +++ b/test/ResourceAdequacy/results/flow.jl @@ -1,12 +1,16 @@ @testset "FlowResult" begin - N = DD.nperiods i, i_idx, i_bad = DD.testinterface, DD.testinterface_idx, DD.notaninterface t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.FlowResult{N,1,Hour,MW}( - DD.nsamples, DD.interfacenames, DD.periods, - DD.d1_resourceperiod, DD.d2_resource, DD.d2_resourceperiod) + result = ResourceAdequacy.FlowResult{N, 1, Hour, MW}( + DD.nsamples, + DD.interfacenames, + DD.periods, + DD.d1_resourceperiod, + DD.d2_resource, + DD.d2_resourceperiod, + ) # Interface-specific @@ -16,22 +20,23 @@ # Interface + period-specific @test result[i, t] ≈ - (DD.d1_resourceperiod[i_idx, t_idx], DD.d2_resourceperiod[i_idx, t_idx]) + (DD.d1_resourceperiod[i_idx, t_idx], DD.d2_resourceperiod[i_idx, t_idx]) @test_throws BoundsError result[i, t_bad] @test_throws BoundsError result[i_bad, t] @test_throws BoundsError result[i_bad, t_bad] - end @testset "FlowSamplesResult" begin - N = DD.nperiods i, i_idx, i_bad = DD.testinterface, DD.testinterface_idx, DD.notaninterface t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.FlowSamplesResult{N,1,Hour,MW}( - DD.interfacenames, DD.periods, DD.d) + result = ResourceAdequacy.FlowSamplesResult{N, 1, Hour, MW}( + DD.interfacenames, + DD.periods, + DD.d, + ) # Interface-specific @@ -46,5 +51,4 @@ end @test_throws BoundsError result[i, t_bad] @test_throws BoundsError result[i_bad, t] @test_throws BoundsError result[i_bad, t_bad] - end diff --git a/test/ResourceAdequacy/results/results.jl b/test/ResourceAdequacy/results/results.jl index 303ddd89..43941403 100644 --- a/test/ResourceAdequacy/results/results.jl +++ b/test/ResourceAdequacy/results/results.jl @@ -1,10 +1,8 @@ @testset "Results" begin - include("shortfall.jl") include("surplus.jl") include("flow.jl") include("utilization.jl") include("energy.jl") include("availability.jl") - end diff --git a/test/ResourceAdequacy/results/shortfall.jl b/test/ResourceAdequacy/results/shortfall.jl index f1bdf1ce..a83f4d34 100644 --- a/test/ResourceAdequacy/results/shortfall.jl +++ b/test/ResourceAdequacy/results/shortfall.jl @@ -1,16 +1,26 @@ @testset "ShortfallResult" begin - - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.ShortfallResult{N,1,Hour,MWh}( - DD.nsamples, DD.resourcenames, DD.periods, - DD.d1, DD.d2, DD.d1_resource, DD.d2_resource, - DD.d1_period, DD.d2_period, DD.d1_resourceperiod, DD.d2_resourceperiod, + result = ResourceAdequacy.ShortfallResult{N, 1, Hour, MWh}( + DD.nsamples, + DD.resourcenames, + DD.periods, + DD.d1, + DD.d2, + DD.d1_resource, + DD.d2_resource, + DD.d1_period, + DD.d2_period, + DD.d1_resourceperiod, + DD.d2_resourceperiod, DD.d3_resourceperiod, - DD.d4, DD.d4_resource, DD.d4_period, DD.d4_resourceperiod) + DD.d4, + DD.d4_resource, + DD.d4_period, + DD.d4_resourceperiod, + ) # Overall @@ -26,7 +36,7 @@ # Region-specific - @test result[r] ≈ (sum(DD.d3_resourceperiod[r_idx,:]), DD.d4_resource[r_idx]) + @test result[r] ≈ (sum(DD.d3_resourceperiod[r_idx, :]), DD.d4_resource[r_idx]) region_lole = LOLE(result, r) @test val(region_lole) ≈ DD.d1_resource[r_idx] @@ -59,12 +69,12 @@ # Region + period-specific @test result[r, t] ≈ - (DD.d3_resourceperiod[r_idx, t_idx], DD.d4_resourceperiod[r_idx, t_idx]) + (DD.d3_resourceperiod[r_idx, t_idx], DD.d4_resourceperiod[r_idx, t_idx]) regionperiod_lole = LOLE(result, r, t) @test val(regionperiod_lole) ≈ DD.d1_resourceperiod[r_idx, t_idx] @test stderror(regionperiod_lole) ≈ - DD.d2_resourceperiod[r_idx, t_idx] / sqrt(DD.nsamples) + DD.d2_resourceperiod[r_idx, t_idx] / sqrt(DD.nsamples) regionperiod_eue = EUE(result, r, t) @test val(regionperiod_eue) ≈ first(result[r, t]) @@ -81,18 +91,18 @@ @test_throws BoundsError EUE(result, r, t_bad) @test_throws BoundsError EUE(result, r_bad, t) @test_throws BoundsError EUE(result, r_bad, t_bad) - end - @testset "ShortfallSamplesResult" begin - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.ShortfallSamplesResult{N,1,Hour,MW,MWh}( - DD.resourcenames, DD.periods, DD.d) + result = ResourceAdequacy.ShortfallSamplesResult{N, 1, Hour, MW, MWh}( + DD.resourcenames, + DD.periods, + DD.d, + ) # Overall @@ -150,10 +160,9 @@ end @test result[r, t] ≈ vec(DD.d[r_idx, t_idx, :]) regionperiod_lole = LOLE(result, r, t) - regionperiod_eventperiods = result[r, t] .> 0 + regionperiod_eventperiods = result[r, t] .> 0 @test val(regionperiod_lole) ≈ mean(regionperiod_eventperiods) - @test stderror(regionperiod_lole) ≈ - std(regionperiod_eventperiods) / sqrt(DD.nsamples) + @test stderror(regionperiod_lole) ≈ std(regionperiod_eventperiods) / sqrt(DD.nsamples) regionperiod_eue = EUE(result, r, t) @test val(regionperiod_eue) ≈ mean(result[r, t]) @@ -170,5 +179,4 @@ end @test_throws BoundsError EUE(result, r, t_bad) @test_throws BoundsError EUE(result, r_bad, t) @test_throws BoundsError EUE(result, r_bad, t_bad) - end diff --git a/test/ResourceAdequacy/results/surplus.jl b/test/ResourceAdequacy/results/surplus.jl index c00bd314..2fb1f712 100644 --- a/test/ResourceAdequacy/results/surplus.jl +++ b/test/ResourceAdequacy/results/surplus.jl @@ -1,12 +1,16 @@ @testset "SurplusResult" begin - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.SurplusResult{N,1,Hour,MW}( - DD.nsamples, DD.resourcenames, DD.periods, - DD.d1_resourceperiod, DD.d2_period, DD.d2_resourceperiod) + result = ResourceAdequacy.SurplusResult{N, 1, Hour, MW}( + DD.nsamples, + DD.resourcenames, + DD.periods, + DD.d1_resourceperiod, + DD.d2_period, + DD.d2_resourceperiod, + ) # Period-specific @@ -16,22 +20,23 @@ # Region + period-specific @test result[r, t] ≈ - (DD.d1_resourceperiod[r_idx, t_idx], DD.d2_resourceperiod[r_idx, t_idx]) + (DD.d1_resourceperiod[r_idx, t_idx], DD.d2_resourceperiod[r_idx, t_idx]) @test_throws BoundsError result[r, t_bad] @test_throws BoundsError result[r_bad, t] @test_throws BoundsError result[r_bad, t_bad] - end @testset "SurplusSamplesResult" begin - N = DD.nperiods r, r_idx, r_bad = DD.testresource, DD.testresource_idx, DD.notaresource t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.SurplusSamplesResult{N,1,Hour,MW}( - DD.resourcenames, DD.periods, DD.d) + result = ResourceAdequacy.SurplusSamplesResult{N, 1, Hour, MW}( + DD.resourcenames, + DD.periods, + DD.d, + ) # Period-specific @@ -46,5 +51,4 @@ end @test_throws BoundsError result[r, t_bad] @test_throws BoundsError result[r_bad, t] @test_throws BoundsError result[r_bad, t_bad] - end diff --git a/test/ResourceAdequacy/results/utilization.jl b/test/ResourceAdequacy/results/utilization.jl index 2a4f6318..64a08352 100644 --- a/test/ResourceAdequacy/results/utilization.jl +++ b/test/ResourceAdequacy/results/utilization.jl @@ -1,12 +1,16 @@ @testset "UtilizationResult" begin - N = DD.nperiods i, i_idx, i_bad = DD.testinterface, DD.testinterface_idx, DD.notaninterface t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.UtilizationResult{N,1,Hour}( - DD.nsamples, DD.interfacenames, DD.periods, - DD.d1_resourceperiod, DD.d2_resource, DD.d2_resourceperiod) + result = ResourceAdequacy.UtilizationResult{N, 1, Hour}( + DD.nsamples, + DD.interfacenames, + DD.periods, + DD.d1_resourceperiod, + DD.d2_resource, + DD.d2_resourceperiod, + ) # Interface-specific @@ -16,22 +20,23 @@ # Interface + period-specific @test result[i, t] ≈ - (DD.d1_resourceperiod[i_idx, t_idx], DD.d2_resourceperiod[i_idx, t_idx]) + (DD.d1_resourceperiod[i_idx, t_idx], DD.d2_resourceperiod[i_idx, t_idx]) @test_throws BoundsError result[i, t_bad] @test_throws BoundsError result[i_bad, t] @test_throws BoundsError result[i_bad, t_bad] - end @testset "UtilizationSamplesResult" begin - N = DD.nperiods i, i_idx, i_bad = DD.testinterface, DD.testinterface_idx, DD.notaninterface t, t_idx, t_bad = DD.testperiod, DD.testperiod_idx, DD.notaperiod - result = ResourceAdequacy.UtilizationSamplesResult{N,1,Hour}( - DD.interfacenames, DD.periods, DD.d) + result = ResourceAdequacy.UtilizationSamplesResult{N, 1, Hour}( + DD.interfacenames, + DD.periods, + DD.d, + ) # Interface-specific @@ -46,5 +51,4 @@ end @test_throws BoundsError result[i, t_bad] @test_throws BoundsError result[i_bad, t] @test_throws BoundsError result[i_bad, t_bad] - end diff --git a/test/ResourceAdequacy/runtests.jl b/test/ResourceAdequacy/runtests.jl index 5eb9ed3b..ce6744d8 100644 --- a/test/ResourceAdequacy/runtests.jl +++ b/test/ResourceAdequacy/runtests.jl @@ -1,8 +1,6 @@ @testset "ResourceAdequacy" begin - include("utils.jl") include("metrics.jl") include("results/results.jl") include("simulation.jl") - end diff --git a/test/ResourceAdequacy/simulation.jl b/test/ResourceAdequacy/simulation.jl index 2785ea69..0b8b929e 100644 --- a/test/ResourceAdequacy/simulation.jl +++ b/test/ResourceAdequacy/simulation.jl @@ -1,6 +1,4 @@ @testset "Simulation" begin - include("simulation/convolution.jl") include("simulation/sequentialmontecarlo.jl") - end diff --git a/test/ResourceAdequacy/simulation/convolution.jl b/test/ResourceAdequacy/simulation/convolution.jl index d41ba2e4..582c3f29 100644 --- a/test/ResourceAdequacy/simulation/convolution.jl +++ b/test/ResourceAdequacy/simulation/convolution.jl @@ -1,53 +1,62 @@ @testset "Convolution" begin - simspec = Convolution(threaded=false) simspec_threaded = Convolution(threaded=true) resultspecs = (Shortfall(), Surplus()) - result_1a, surplus_1a = - assess(TestSystems.singlenode_a, simspec, resultspecs...) - - @test LOLE(result_1a) ≈ LOLE{4,1,Hour}(MeanEstimate(0.355)) - @test all(LOLE.(result_1a, result_1a.timestamps) .≈ - LOLE{1,1,Hour}.(MeanEstimate.([0.028, 0.271, 0.028, 0.028]))) - @test EUE(result_1a) ≈ EUE{4,1,Hour,MWh}(MeanEstimate(1.59)) - @test all(EUE.(result_1a, result_1a.timestamps) .≈ - EUE{1,1,Hour,MWh}.(MeanEstimate.([0.29, 0.832, 0.29, 0.178]))) - - result_1a5, surplus_1a5 = - assess(TestSystems.singlenode_a_5min, simspec, resultspecs...) - - @test LOLE(result_1a5) ≈ - LOLE{4,5,Minute}(MeanEstimate(TestSystems.singlenode_a_lole)) - @test all(LOLE.(result_1a5, result_1a5.timestamps) .≈ - LOLE{1,5,Minute}.(MeanEstimate.(TestSystems.singlenode_a_lolps))) + result_1a, surplus_1a = assess(TestSystems.singlenode_a, simspec, resultspecs...) + + @test LOLE(result_1a) ≈ LOLE{4, 1, Hour}(MeanEstimate(0.355)) + @test all( + LOLE.(result_1a, result_1a.timestamps) .≈ + LOLE{1, 1, Hour}.(MeanEstimate.([0.028, 0.271, 0.028, 0.028])), + ) + @test EUE(result_1a) ≈ EUE{4, 1, Hour, MWh}(MeanEstimate(1.59)) + @test all( + EUE.(result_1a, result_1a.timestamps) .≈ + EUE{1, 1, Hour, MWh}.(MeanEstimate.([0.29, 0.832, 0.29, 0.178])), + ) + + result_1a5, surplus_1a5 = assess(TestSystems.singlenode_a_5min, simspec, resultspecs...) + + @test LOLE(result_1a5) ≈ LOLE{4, 5, Minute}(MeanEstimate(TestSystems.singlenode_a_lole)) + @test all( + LOLE.(result_1a5, result_1a5.timestamps) .≈ + LOLE{1, 5, Minute}.(MeanEstimate.(TestSystems.singlenode_a_lolps)), + ) @test EUE(result_1a5) ≈ - EUE{4,5,Minute,MWh}(MeanEstimate.(TestSystems.singlenode_a_eue/12)) - @test all(EUE.(result_1a5, result_1a5.timestamps) .≈ - EUE{1,5,Minute,MWh}.(MeanEstimate.(TestSystems.singlenode_a_eues ./ 12))) - - result_1b, surplus_1b = - assess(TestSystems.singlenode_b, simspec, resultspecs...) - - @test LOLE(result_1b) ≈ LOLE{6,1,Hour}(MeanEstimate(0.96)) - @test all(LOLE.(result_1b, result_1b.timestamps) .≈ - LOLE{1,1,Hour}.(MeanEstimate.([0.19, 0.19, 0.19, 0.1, 0.1, 0.19]))) - @test EUE(result_1b) ≈ EUE{6,1,Hour,MWh}(MeanEstimate(7.11)) - @test all(EUE.(result_1b, result_1b.timestamps) .≈ - EUE{1,1,Hour,MWh}.(MeanEstimate.([1.29, 1.29, 1.29, 0.85, 1.05, 1.34]))) - - result_3, surplus_3 = - assess(TestSystems.threenode, simspec, resultspecs...) - - @test LOLE(result_3) ≈ LOLE{4,1,Hour}(MeanEstimate(1.17877)) - @test all(LOLE.(result_3, result_3.timestamps) .≈ - LOLE{1,1,Hour}.(MeanEstimate.([.14707, .40951, .21268, .40951]))) - @test EUE(result_3) ≈ EUE{4,1,Hour,MWh}(MeanEstimate(11.73276)) - @test all(EUE.(result_3, result_3.timestamps) .≈ - EUE{1,1,Hour,MWh}.(MeanEstimate.([1.75783, 3.13343, 2.47954, 4.36196]))) + EUE{4, 5, Minute, MWh}(MeanEstimate.(TestSystems.singlenode_a_eue / 12)) + @test all( + EUE.(result_1a5, result_1a5.timestamps) .≈ + EUE{1, 5, Minute, MWh}.(MeanEstimate.(TestSystems.singlenode_a_eues ./ 12)), + ) + + result_1b, surplus_1b = assess(TestSystems.singlenode_b, simspec, resultspecs...) + + @test LOLE(result_1b) ≈ LOLE{6, 1, Hour}(MeanEstimate(0.96)) + @test all( + LOLE.(result_1b, result_1b.timestamps) .≈ + LOLE{1, 1, Hour}.(MeanEstimate.([0.19, 0.19, 0.19, 0.1, 0.1, 0.19])), + ) + @test EUE(result_1b) ≈ EUE{6, 1, Hour, MWh}(MeanEstimate(7.11)) + @test all( + EUE.(result_1b, result_1b.timestamps) .≈ + EUE{1, 1, Hour, MWh}.(MeanEstimate.([1.29, 1.29, 1.29, 0.85, 1.05, 1.34])), + ) + + result_3, surplus_3 = assess(TestSystems.threenode, simspec, resultspecs...) + + @test LOLE(result_3) ≈ LOLE{4, 1, Hour}(MeanEstimate(1.17877)) + @test all( + LOLE.(result_3, result_3.timestamps) .≈ + LOLE{1, 1, Hour}.(MeanEstimate.([0.14707, 0.40951, 0.21268, 0.40951])), + ) + @test EUE(result_3) ≈ EUE{4, 1, Hour, MWh}(MeanEstimate(11.73276)) + @test all( + EUE.(result_3, result_3.timestamps) .≈ + EUE{1, 1, Hour, MWh}.(MeanEstimate.([1.75783, 3.13343, 2.47954, 4.36196])), + ) # TODO: Surplus tests assess(TestSystems.singlenode_a, simspec_threaded, resultspecs...) - end diff --git a/test/ResourceAdequacy/simulation/sequentialmontecarlo.jl b/test/ResourceAdequacy/simulation/sequentialmontecarlo.jl index 53b52b1f..b3ee97ce 100644 --- a/test/ResourceAdequacy/simulation/sequentialmontecarlo.jl +++ b/test/ResourceAdequacy/simulation/sequentialmontecarlo.jl @@ -1,18 +1,22 @@ @testset "SequentialMonteCarlo" begin - - @testset "DispatchProblem" begin - - end + @testset "DispatchProblem" begin end nstderr_tol = 3 simspec = SequentialMonteCarlo(samples=100_000, seed=1, threaded=false) smallsample = SequentialMonteCarlo(samples=10, seed=123) - resultspecs = (Shortfall(), Surplus(), Flow(), Utilization(), - ShortfallSamples(), SurplusSamples(), - FlowSamples(), UtilizationSamples(), - GeneratorAvailability()) + resultspecs = ( + Shortfall(), + Surplus(), + Flow(), + Utilization(), + ShortfallSamples(), + SurplusSamples(), + FlowSamples(), + UtilizationSamples(), + GeneratorAvailability(), + ) timestamps_a = TestSystems.singlenode_a.timestamps timestamps_a5 = TestSystems.singlenode_a_5min.timestamps @@ -27,27 +31,21 @@ regionscol = TestSystems.threenode.regions.names assess(TestSystems.singlenode_a, smallsample, resultspecs...) - shortfall_1a, _, flow_1a, util_1a, - shortfall2_1a, _, flow2_1a, util2_1a, _ = + shortfall_1a, _, flow_1a, util_1a, shortfall2_1a, _, flow2_1a, util2_1a, _ = assess(TestSystems.singlenode_a, simspec, resultspecs...) assess(TestSystems.singlenode_a_5min, smallsample, resultspecs...) - shortfall_1a5, _, flow_1a5, util_1a5, - shortfall2_1a5, _, flow2_1a5, util2_1a5, _ = + shortfall_1a5, _, flow_1a5, util_1a5, shortfall2_1a5, _, flow2_1a5, util2_1a5, _ = assess(TestSystems.singlenode_a_5min, simspec, resultspecs...) assess(TestSystems.singlenode_b, smallsample, resultspecs...) - shortfall_1b, _, flow_1b, util_1b, - shortfall2_1b, _, flow2_1b, util2_1b, _ = + shortfall_1b, _, flow_1b, util_1b, shortfall2_1b, _, flow2_1b, util2_1b, _ = assess(TestSystems.singlenode_b, simspec, resultspecs...) assess(TestSystems.threenode, smallsample, resultspecs...) - shortfall_3, _, flow_3, util_3, - shortfall2_3, _, flow2_3, util2_3, _ = + shortfall_3, _, flow_3, util_3, shortfall2_3, _, flow2_3, util2_3, _ = assess(TestSystems.threenode, simspec, resultspecs...) - - @testset "Shortfall Results" begin # Single-region system A @@ -57,32 +55,52 @@ @test LOLE(shortfall_1a, "Region") ≈ LOLE(shortfall2_1a, "Region") @test EUE(shortfall_1a, "Region") ≈ EUE(shortfall2_1a, "Region") - @test withinrange(LOLE(shortfall_1a), - TestSystems.singlenode_a_lole, nstderr_tol) - @test withinrange(EUE(shortfall_1a), - TestSystems.singlenode_a_eue, nstderr_tol) - @test withinrange(LOLE(shortfall_1a, "Region"), - TestSystems.singlenode_a_lole, nstderr_tol) - @test withinrange(EUE(shortfall_1a, "Region"), - TestSystems.singlenode_a_eue, nstderr_tol) - - @test all(LOLE.(shortfall_1a, timestamps_a) .≈ - LOLE.(shortfall2_1a, timestamps_a)) - @test all(EUE.(shortfall_1a, timestamps_a) .≈ - EUE.(shortfall2_1a, timestamps_a)) - @test all(LOLE(shortfall_1a, "Region", :) .≈ - LOLE(shortfall2_1a, "Region", :)) - @test all(EUE(shortfall_1a, "Region", :) .≈ - EUE(shortfall2_1a, "Region", :)) - - @test all(withinrange.(LOLE.(shortfall_1a, timestamps_a), - TestSystems.singlenode_a_lolps, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall_1a, timestamps_a), - TestSystems.singlenode_a_eues, nstderr_tol)) - @test all(withinrange.(LOLE(shortfall_1a, "Region", :), - TestSystems.singlenode_a_lolps, nstderr_tol)) - @test all(withinrange.(EUE(shortfall_1a, "Region", :), - TestSystems.singlenode_a_eues, nstderr_tol)) + @test withinrange(LOLE(shortfall_1a), TestSystems.singlenode_a_lole, nstderr_tol) + @test withinrange(EUE(shortfall_1a), TestSystems.singlenode_a_eue, nstderr_tol) + @test withinrange( + LOLE(shortfall_1a, "Region"), + TestSystems.singlenode_a_lole, + nstderr_tol, + ) + @test withinrange( + EUE(shortfall_1a, "Region"), + TestSystems.singlenode_a_eue, + nstderr_tol, + ) + + @test all(LOLE.(shortfall_1a, timestamps_a) .≈ LOLE.(shortfall2_1a, timestamps_a)) + @test all(EUE.(shortfall_1a, timestamps_a) .≈ EUE.(shortfall2_1a, timestamps_a)) + @test all(LOLE(shortfall_1a, "Region", :) .≈ LOLE(shortfall2_1a, "Region", :)) + @test all(EUE(shortfall_1a, "Region", :) .≈ EUE(shortfall2_1a, "Region", :)) + + @test all( + withinrange.( + LOLE.(shortfall_1a, timestamps_a), + TestSystems.singlenode_a_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE.(shortfall_1a, timestamps_a), + TestSystems.singlenode_a_eues, + nstderr_tol, + ), + ) + @test all( + withinrange.( + LOLE(shortfall_1a, "Region", :), + TestSystems.singlenode_a_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE(shortfall_1a, "Region", :), + TestSystems.singlenode_a_eues, + nstderr_tol, + ), + ) # Single-region system A - 5 min version @@ -91,32 +109,58 @@ @test LOLE(shortfall_1a5, "Region") ≈ LOLE(shortfall2_1a5, "Region") @test EUE(shortfall_1a5, "Region") ≈ EUE(shortfall2_1a5, "Region") - @test withinrange(LOLE(shortfall_1a5), - TestSystems.singlenode_a_lole, nstderr_tol) - @test withinrange(EUE(shortfall_1a5), - TestSystems.singlenode_a_eue/12, nstderr_tol) - @test withinrange(LOLE(shortfall_1a5, "Region"), - TestSystems.singlenode_a_lole, nstderr_tol) - @test withinrange(EUE(shortfall_1a5, "Region"), - TestSystems.singlenode_a_eue/12, nstderr_tol) - - @test all(LOLE.(shortfall_1a5, timestamps_a5) .≈ - LOLE.(shortfall2_1a5, timestamps_a5)) - @test all(EUE.(shortfall_1a5, timestamps_a5) .≈ - EUE.(shortfall2_1a5, timestamps_a5)) - @test all(LOLE(shortfall_1a5, "Region", :) .≈ - LOLE(shortfall2_1a5, "Region", :)) - @test all(EUE(shortfall_1a5, "Region", :) .≈ - EUE(shortfall2_1a5, "Region", :)) - - @test all(withinrange.(LOLE.(shortfall_1a5, timestamps_a5), - TestSystems.singlenode_a_lolps, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall_1a5, timestamps_a5), - TestSystems.singlenode_a_eues ./ 12, nstderr_tol)) - @test all(withinrange.(LOLE(shortfall_1a5, "Region", :), - TestSystems.singlenode_a_lolps, nstderr_tol)) - @test all(withinrange.(EUE(shortfall_1a5, "Region", :), - TestSystems.singlenode_a_eues ./ 12, nstderr_tol)) + @test withinrange(LOLE(shortfall_1a5), TestSystems.singlenode_a_lole, nstderr_tol) + @test withinrange( + EUE(shortfall_1a5), + TestSystems.singlenode_a_eue / 12, + nstderr_tol, + ) + @test withinrange( + LOLE(shortfall_1a5, "Region"), + TestSystems.singlenode_a_lole, + nstderr_tol, + ) + @test withinrange( + EUE(shortfall_1a5, "Region"), + TestSystems.singlenode_a_eue / 12, + nstderr_tol, + ) + + @test all( + LOLE.(shortfall_1a5, timestamps_a5) .≈ LOLE.(shortfall2_1a5, timestamps_a5), + ) + @test all(EUE.(shortfall_1a5, timestamps_a5) .≈ EUE.(shortfall2_1a5, timestamps_a5)) + @test all(LOLE(shortfall_1a5, "Region", :) .≈ LOLE(shortfall2_1a5, "Region", :)) + @test all(EUE(shortfall_1a5, "Region", :) .≈ EUE(shortfall2_1a5, "Region", :)) + + @test all( + withinrange.( + LOLE.(shortfall_1a5, timestamps_a5), + TestSystems.singlenode_a_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE.(shortfall_1a5, timestamps_a5), + TestSystems.singlenode_a_eues ./ 12, + nstderr_tol, + ), + ) + @test all( + withinrange.( + LOLE(shortfall_1a5, "Region", :), + TestSystems.singlenode_a_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE(shortfall_1a5, "Region", :), + TestSystems.singlenode_a_eues ./ 12, + nstderr_tol, + ), + ) # Single-region system B @@ -125,32 +169,52 @@ @test LOLE(shortfall_1b, "Region") ≈ LOLE(shortfall2_1b, "Region") @test EUE(shortfall_1b, "Region") ≈ EUE(shortfall2_1b, "Region") - @test withinrange(LOLE(shortfall_1b), - TestSystems.singlenode_b_lole, nstderr_tol) - @test withinrange(EUE(shortfall_1b), - TestSystems.singlenode_b_eue, nstderr_tol) - @test withinrange(LOLE(shortfall_1b, "Region"), - TestSystems.singlenode_b_lole, nstderr_tol) - @test withinrange(EUE(shortfall_1b, "Region"), - TestSystems.singlenode_b_eue, nstderr_tol) - - @test all(LOLE.(shortfall_1b, timestamps_b) .≈ - LOLE.(shortfall2_1b, timestamps_b)) - @test all(EUE.(shortfall_1b, timestamps_b) .≈ - EUE.(shortfall2_1b, timestamps_b)) - @test all(LOLE(shortfall_1b, "Region", :) .≈ - LOLE(shortfall2_1b, "Region", :)) - @test all(EUE(shortfall_1b, "Region", :) .≈ - EUE(shortfall2_1b, "Region", :)) - - @test all(withinrange.(LOLE.(shortfall_1b, timestamps_b), - TestSystems.singlenode_b_lolps, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall_1b, timestamps_b), - TestSystems.singlenode_b_eues, nstderr_tol)) - @test all(withinrange.(LOLE(shortfall_1b, "Region", :), - TestSystems.singlenode_b_lolps, nstderr_tol)) - @test all(withinrange.(EUE(shortfall_1b, "Region", :), - TestSystems.singlenode_b_eues, nstderr_tol)) + @test withinrange(LOLE(shortfall_1b), TestSystems.singlenode_b_lole, nstderr_tol) + @test withinrange(EUE(shortfall_1b), TestSystems.singlenode_b_eue, nstderr_tol) + @test withinrange( + LOLE(shortfall_1b, "Region"), + TestSystems.singlenode_b_lole, + nstderr_tol, + ) + @test withinrange( + EUE(shortfall_1b, "Region"), + TestSystems.singlenode_b_eue, + nstderr_tol, + ) + + @test all(LOLE.(shortfall_1b, timestamps_b) .≈ LOLE.(shortfall2_1b, timestamps_b)) + @test all(EUE.(shortfall_1b, timestamps_b) .≈ EUE.(shortfall2_1b, timestamps_b)) + @test all(LOLE(shortfall_1b, "Region", :) .≈ LOLE(shortfall2_1b, "Region", :)) + @test all(EUE(shortfall_1b, "Region", :) .≈ EUE(shortfall2_1b, "Region", :)) + + @test all( + withinrange.( + LOLE.(shortfall_1b, timestamps_b), + TestSystems.singlenode_b_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE.(shortfall_1b, timestamps_b), + TestSystems.singlenode_b_eues, + nstderr_tol, + ), + ) + @test all( + withinrange.( + LOLE(shortfall_1b, "Region", :), + TestSystems.singlenode_b_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE(shortfall_1b, "Region", :), + TestSystems.singlenode_b_eues, + nstderr_tol, + ), + ) # Three-region system @@ -159,45 +223,50 @@ @test all(LOLE.(shortfall_3, regionscol) .≈ LOLE.(shortfall2_3, regionscol)) @test all(EUE.(shortfall_3, regionscol) .≈ EUE.(shortfall2_3, regionscol)) - @test withinrange(LOLE(shortfall_3), - TestSystems.threenode_lole, nstderr_tol) - @test withinrange(EUE(shortfall_3), - TestSystems.threenode_eue, nstderr_tol) - @test all(withinrange.(LOLE.(shortfall_3, timestamps_3), - TestSystems.threenode_lolps, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall_3, timestamps_3), - TestSystems.threenode_eues, nstderr_tol)) - - @test all(LOLE.(shortfall_3, timestamps_3) .≈ - LOLE.(shortfall2_3, timestamps_3)) - @test all(EUE.(shortfall_3, timestamps_3) .≈ - EUE.(shortfall2_3, timestamps_3)) + @test withinrange(LOLE(shortfall_3), TestSystems.threenode_lole, nstderr_tol) + @test withinrange(EUE(shortfall_3), TestSystems.threenode_eue, nstderr_tol) + @test all( + withinrange.( + LOLE.(shortfall_3, timestamps_3), + TestSystems.threenode_lolps, + nstderr_tol, + ), + ) + @test all( + withinrange.( + EUE.(shortfall_3, timestamps_3), + TestSystems.threenode_eues, + nstderr_tol, + ), + ) + + @test all(LOLE.(shortfall_3, timestamps_3) .≈ LOLE.(shortfall2_3, timestamps_3)) + @test all(EUE.(shortfall_3, timestamps_3) .≈ EUE.(shortfall2_3, timestamps_3)) @test all(LOLE(shortfall_3, :, :) .≈ LOLE(shortfall2_3, :, :)) @test all(EUE(shortfall_3, :, :) .≈ EUE(shortfall2_3, :, :)) @test withinrange( - LOLE(shortfall_3, "Region C", ZonedDateTime(2018,10,30,1,TestSystems.tz)), - 0.1, nstderr_tol) + LOLE(shortfall_3, "Region C", ZonedDateTime(2018, 10, 30, 1, TestSystems.tz)), + 0.1, + nstderr_tol, + ) @test withinrange( - LOLE(shortfall_3, "Region C", ZonedDateTime(2018,10,30,2,TestSystems.tz)), - 0.1, nstderr_tol) + LOLE(shortfall_3, "Region C", ZonedDateTime(2018, 10, 30, 2, TestSystems.tz)), + 0.1, + nstderr_tol, + ) # TODO: Test spatially-disaggregated results - may need to develop # test systems with unique network flow solutions println("SpatioTemporal LOLPs:") - display(vcat( - hcat("", timestamprow_3), - hcat(regionscol, LOLE(shortfall_3, :, :)) - )); println() + display(vcat(hcat("", timestamprow_3), hcat(regionscol, LOLE(shortfall_3, :, :)))) + println() println("SpatioTemporal EUEs:") - display(vcat( - hcat("", timestamprow_3), - hcat(regionscol, EUE(shortfall_3, :, :)) - )); println() - + display(vcat(hcat("", timestamprow_3), hcat(regionscol, EUE(shortfall_3, :, :)))) + println() end @testset "Flow and Utilization Results" begin @@ -226,197 +295,299 @@ # Three-region system println("Network Flows:") - display(vcat( - hcat("", timestamprow_3), - hcat(flow_3.interfaces, flow_3[:, :]) - )); println() + display(vcat(hcat("", timestamprow_3), hcat(flow_3.interfaces, flow_3[:, :]))) + println() @test all(flow_3[:] .≈ flow2_3[:]) @test all(flow_3[:, :] .≈ flow2_3[:, :]) println("Network Utilizations:") - display(vcat( - hcat("", timestamprow_3), - hcat(flow_3.interfaces, util_3[:, :]) - )); println() + display(vcat(hcat("", timestamprow_3), hcat(flow_3.interfaces, util_3[:, :]))) + println() @test all(util_3[:] .≈ util2_3[:]) @test all(util_3[:, :] .≈ util2_3[:, :]) - end @testset "RTS" begin - sys = PRAS.rts_gmlc() - assess(sys, SequentialMonteCarlo(samples=100), - GeneratorAvailability(), LineAvailability(), - StorageAvailability(), GeneratorStorageAvailability(), - StorageEnergy(), GeneratorStorageEnergy(), - StorageEnergySamples(), GeneratorStorageEnergySamples()) - + assess( + sys, + SequentialMonteCarlo(samples=100), + GeneratorAvailability(), + LineAvailability(), + StorageAvailability(), + GeneratorStorageAvailability(), + StorageEnergy(), + GeneratorStorageEnergy(), + StorageEnergySamples(), + GeneratorStorageEnergySamples(), + ) end @testset "Test System 1: 2 Gens, 2 Regions" begin - simspec = SequentialMonteCarlo(samples=1_000_000, seed=111) dt = first(TestSystems.test1.timestamps) regions = TestSystems.test1.regions.names - shortfall, surplus, flow, utilization = - assess(TestSystems.test1, simspec, - Shortfall(), Surplus(), Flow(), Utilization()) + shortfall, surplus, flow, utilization = assess( + TestSystems.test1, + simspec, + Shortfall(), + Surplus(), + Flow(), + Utilization(), + ) # Shortfall - LOLE @test withinrange(LOLE(shortfall), TestSystems.test1_lole, nstderr_tol) @test withinrange(LOLE(shortfall, dt), TestSystems.test1_lole, nstderr_tol) - @test all(withinrange.(LOLE.(shortfall, regions), - TestSystems.test1_loles, nstderr_tol)) - @test all(withinrange.(LOLE.(shortfall, regions, dt), - TestSystems.test1_loles, nstderr_tol)) + @test all( + withinrange.(LOLE.(shortfall, regions), TestSystems.test1_loles, nstderr_tol), + ) + @test all( + withinrange.( + LOLE.(shortfall, regions, dt), + TestSystems.test1_loles, + nstderr_tol, + ), + ) # Shortfall - EUE @test withinrange(EUE(shortfall), TestSystems.test1_eue, nstderr_tol) @test withinrange(EUE(shortfall, dt), TestSystems.test1_eue, nstderr_tol) - @test all(withinrange.(EUE.(shortfall, regions), - TestSystems.test1_eues, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall, regions, dt), - TestSystems.test1_eues, nstderr_tol)) + @test all( + withinrange.(EUE.(shortfall, regions), TestSystems.test1_eues, nstderr_tol), + ) + @test all( + withinrange.(EUE.(shortfall, regions, dt), TestSystems.test1_eues, nstderr_tol), + ) # Surplus - @test withinrange(surplus[dt], TestSystems.test1_esurplus, - simspec.nsamples, nstderr_tol) - @test all(withinrange.(getindex.(surplus, regions, dt), - TestSystems.test1_esurpluses, - simspec.nsamples, nstderr_tol)) + @test withinrange( + surplus[dt], + TestSystems.test1_esurplus, + simspec.nsamples, + nstderr_tol, + ) + @test all( + withinrange.( + getindex.(surplus, regions, dt), + TestSystems.test1_esurpluses, + simspec.nsamples, + nstderr_tol, + ), + ) # Flow - @test withinrange(flow["Region A" => "Region B"], - TestSystems.test1_i1_flow, - simspec.nsamples, nstderr_tol) - @test withinrange(flow["Region A" => "Region B", dt], - TestSystems.test1_i1_flow, - simspec.nsamples, nstderr_tol) + @test withinrange( + flow["Region A" => "Region B"], + TestSystems.test1_i1_flow, + simspec.nsamples, + nstderr_tol, + ) + @test withinrange( + flow["Region A" => "Region B", dt], + TestSystems.test1_i1_flow, + simspec.nsamples, + nstderr_tol, + ) # Utilization - @test withinrange(utilization["Region A" => "Region B"], - TestSystems.test1_i1_util, - simspec.nsamples, nstderr_tol) - @test withinrange(utilization["Region A" => "Region B", dt], - TestSystems.test1_i1_util, - simspec.nsamples, nstderr_tol) - + @test withinrange( + utilization["Region A" => "Region B"], + TestSystems.test1_i1_util, + simspec.nsamples, + nstderr_tol, + ) + @test withinrange( + utilization["Region A" => "Region B", dt], + TestSystems.test1_i1_util, + simspec.nsamples, + nstderr_tol, + ) end @testset "Test System 2: Gen + Storage, 1 Region" begin - simspec = SequentialMonteCarlo(samples=1_000_000, seed=112) region = first(TestSystems.test2.regions.names) stor = first(TestSystems.test2.storages.names) dts = TestSystems.test2.timestamps shortfall, surplus, energy = - assess(TestSystems.test2, simspec, - Shortfall(), Surplus(), StorageEnergy()) + assess(TestSystems.test2, simspec, Shortfall(), Surplus(), StorageEnergy()) # Shortfall - LOLE - @test withinrange(LOLE(shortfall), - TestSystems.test2_lole, nstderr_tol) - @test withinrange(LOLE(shortfall, region), - TestSystems.test2_lole, nstderr_tol) - @test all(withinrange.(LOLE.(shortfall, dts), - TestSystems.test2_lolps, nstderr_tol)) - @test all(withinrange.(LOLE.(shortfall, region, dts), - TestSystems.test2_lolps, nstderr_tol)) + @test withinrange(LOLE(shortfall), TestSystems.test2_lole, nstderr_tol) + @test withinrange(LOLE(shortfall, region), TestSystems.test2_lole, nstderr_tol) + @test all(withinrange.(LOLE.(shortfall, dts), TestSystems.test2_lolps, nstderr_tol)) + @test all( + withinrange.( + LOLE.(shortfall, region, dts), + TestSystems.test2_lolps, + nstderr_tol, + ), + ) # Shortfall - EUE - @test withinrange(EUE(shortfall), - TestSystems.test2_eue, nstderr_tol) - @test withinrange(EUE(shortfall, region), - TestSystems.test2_eue, nstderr_tol) - @test all(withinrange.(EUE.(shortfall, dts), - TestSystems.test2_eues, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall, region, dts), - TestSystems.test2_eues, nstderr_tol)) + @test withinrange(EUE(shortfall), TestSystems.test2_eue, nstderr_tol) + @test withinrange(EUE(shortfall, region), TestSystems.test2_eue, nstderr_tol) + @test all(withinrange.(EUE.(shortfall, dts), TestSystems.test2_eues, nstderr_tol)) + @test all( + withinrange.(EUE.(shortfall, region, dts), TestSystems.test2_eues, nstderr_tol), + ) # Surplus - @test all(withinrange.(getindex.(surplus, dts), - TestSystems.test2_esurplus, - simspec.nsamples, nstderr_tol)) - @test all(withinrange.(getindex.(surplus, region, dts), - TestSystems.test2_esurplus, - simspec.nsamples, nstderr_tol)) + @test all( + withinrange.( + getindex.(surplus, dts), + TestSystems.test2_esurplus, + simspec.nsamples, + nstderr_tol, + ), + ) + @test all( + withinrange.( + getindex.(surplus, region, dts), + TestSystems.test2_esurplus, + simspec.nsamples, + nstderr_tol, + ), + ) # Energy - @test all(withinrange.(getindex.(energy, dts), - TestSystems.test2_eenergy, - simspec.nsamples, nstderr_tol)) - @test all(withinrange.(getindex.(energy, stor, dts), - TestSystems.test2_eenergy, - simspec.nsamples, nstderr_tol)) - + @test all( + withinrange.( + getindex.(energy, dts), + TestSystems.test2_eenergy, + simspec.nsamples, + nstderr_tol, + ), + ) + @test all( + withinrange.( + getindex.(energy, stor, dts), + TestSystems.test2_eenergy, + simspec.nsamples, + nstderr_tol, + ), + ) end @testset "Test System 3: Gen + Storage, 2 Regions" begin - simspec = SequentialMonteCarlo(samples=1_000_000, seed=113) regions = TestSystems.test3.regions.names stor = first(TestSystems.test3.storages.names) dts = TestSystems.test3.timestamps - shortfall, surplus, flow, utilization, energy = - assess(TestSystems.test3, simspec, - Shortfall(), Surplus(), Flow(), Utilization(), StorageEnergy()) + shortfall, surplus, flow, utilization, energy = assess( + TestSystems.test3, + simspec, + Shortfall(), + Surplus(), + Flow(), + Utilization(), + StorageEnergy(), + ) # Shortfall - LOLE - @test withinrange(LOLE(shortfall), - TestSystems.test3_lole, nstderr_tol) - @test all(withinrange.(LOLE.(shortfall, regions), - TestSystems.test3_lole_r, nstderr_tol)) - @test all(withinrange.(LOLE.(shortfall, dts), - TestSystems.test3_lole_t, nstderr_tol)) - @test all(withinrange.(LOLE.(shortfall, regions, permutedims(dts)), - TestSystems.test3_lole_rt, nstderr_tol)) + @test withinrange(LOLE(shortfall), TestSystems.test3_lole, nstderr_tol) + @test all( + withinrange.(LOLE.(shortfall, regions), TestSystems.test3_lole_r, nstderr_tol), + ) + @test all( + withinrange.(LOLE.(shortfall, dts), TestSystems.test3_lole_t, nstderr_tol), + ) + @test all( + withinrange.( + LOLE.(shortfall, regions, permutedims(dts)), + TestSystems.test3_lole_rt, + nstderr_tol, + ), + ) # Shortfall - EUE - @test withinrange(EUE(shortfall), - TestSystems.test3_eue, nstderr_tol) - @test all(withinrange.(EUE.(shortfall, regions), - TestSystems.test3_eue_r, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall, dts), - TestSystems.test3_eue_t, nstderr_tol)) - @test all(withinrange.(EUE.(shortfall, regions, permutedims(dts)), - TestSystems.test3_eue_rt, nstderr_tol)) + @test withinrange(EUE(shortfall), TestSystems.test3_eue, nstderr_tol) + @test all( + withinrange.(EUE.(shortfall, regions), TestSystems.test3_eue_r, nstderr_tol), + ) + @test all(withinrange.(EUE.(shortfall, dts), TestSystems.test3_eue_t, nstderr_tol)) + @test all( + withinrange.( + EUE.(shortfall, regions, permutedims(dts)), + TestSystems.test3_eue_rt, + nstderr_tol, + ), + ) # Surplus - @test all(withinrange.(getindex.(surplus, dts), # fails? - TestSystems.test3_esurplus_t, - simspec.nsamples, nstderr_tol)) - @test all(withinrange.(getindex.(surplus, regions, permutedims(dts)), # fails? - TestSystems.test3_esurplus_rt, - simspec.nsamples, nstderr_tol)) + @test all( + withinrange.( + getindex.(surplus, dts), # fails? + TestSystems.test3_esurplus_t, + simspec.nsamples, + nstderr_tol, + ), + ) + @test all( + withinrange.( + getindex.(surplus, regions, permutedims(dts)), # fails? + TestSystems.test3_esurplus_rt, + simspec.nsamples, + nstderr_tol, + ), + ) # Flow - @test all(withinrange.(getindex.(flow, "Region A"=>"Region B"), - TestSystems.test3_flow, - simspec.nsamples, nstderr_tol)) - @test all(withinrange.(getindex.(flow, "Region A"=>"Region B", dts), - TestSystems.test3_flow_t, - simspec.nsamples, nstderr_tol)) + @test all( + withinrange.( + getindex.(flow, "Region A" => "Region B"), + TestSystems.test3_flow, + simspec.nsamples, + nstderr_tol, + ), + ) + @test all( + withinrange.( + getindex.(flow, "Region A" => "Region B", dts), + TestSystems.test3_flow_t, + simspec.nsamples, + nstderr_tol, + ), + ) # Utilization - @test all(withinrange.(getindex.(utilization, "Region A"=>"Region B"), - TestSystems.test3_util, - simspec.nsamples, nstderr_tol)) - @test all(withinrange.(getindex.(utilization, "Region A"=>"Region B", dts), - TestSystems.test3_util_t, - simspec.nsamples, nstderr_tol)) + @test all( + withinrange.( + getindex.(utilization, "Region A" => "Region B"), + TestSystems.test3_util, + simspec.nsamples, + nstderr_tol, + ), + ) + @test all( + withinrange.( + getindex.(utilization, "Region A" => "Region B", dts), + TestSystems.test3_util_t, + simspec.nsamples, + nstderr_tol, + ), + ) # Energy - @test all(withinrange.(getindex.(energy, dts), # fails? - TestSystems.test3_eenergy, - simspec.nsamples, nstderr_tol)) - @test all(withinrange.(getindex.(energy, stor, dts), # fails? - TestSystems.test3_eenergy, - simspec.nsamples, nstderr_tol)) - + @test all( + withinrange.( + getindex.(energy, dts), # fails? + TestSystems.test3_eenergy, + simspec.nsamples, + nstderr_tol, + ), + ) + @test all( + withinrange.( + getindex.(energy, stor, dts), # fails? + TestSystems.test3_eenergy, + simspec.nsamples, + nstderr_tol, + ), + ) end - end diff --git a/test/ResourceAdequacy/utils.jl b/test/ResourceAdequacy/utils.jl index 4845bb25..086c225d 100644 --- a/test/ResourceAdequacy/utils.jl +++ b/test/ResourceAdequacy/utils.jl @@ -1,5 +1,4 @@ @testset "Utils" begin - @testset "Convolution" begin # x = rand(10000) @@ -11,7 +10,6 @@ end @testset "Distribution Assessment" begin - distr = DiscreteNonParametric([-2, -1, 0, 1, 2], fill(0.2, 5)) lolp, eul = assess(distr) es = ResourceAdequacy.surplus(distr) @@ -27,7 +25,5 @@ @test isapprox(lolp, 0.0) @test isapprox(eul, 0.0) @test isapprox(es, 3) - end - end diff --git a/test/dummydata.jl b/test/dummydata.jl index aeebcb4b..0d7e4815 100644 --- a/test/dummydata.jl +++ b/test/dummydata.jl @@ -13,17 +13,17 @@ testresource_idx = 2 testresource = resourcenames[testresource_idx] notaresource = "NotAResource" -interfacenames = ["A"=>"B", "B"=>"C", "A"=>"C"] +interfacenames = ["A" => "B", "B" => "C", "A" => "C"] ninterfaces = length(interfacenames) testinterface_idx = 3 testinterface = interfacenames[testinterface_idx] -notaninterface = "X"=>"Y" +notaninterface = "X" => "Y" -periods = ZonedDateTime(2012,4,1,0,tz):Hour(1):ZonedDateTime(2012,4,7,23,tz) +periods = ZonedDateTime(2012, 4, 1, 0, tz):Hour(1):ZonedDateTime(2012, 4, 7, 23, tz) nperiods = length(periods) testperiod_idx = 29 testperiod = periods[testperiod_idx] -notaperiod = ZonedDateTime(2010,1,1,0,tz) +notaperiod = ZonedDateTime(2010, 1, 1, 0, tz) d = rand(0:999, nresources, nperiods, nsamples) diff --git a/test/runtests.jl b/test/runtests.jl index b602f4c7..99657bb7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,17 +8,17 @@ using TimeZones import PRAS.ResourceAdequacy: MeanEstimate withinrange(x::ReliabilityMetric, y::Real, n::Real) = - isapprox(val(x), y, atol=n*stderror(x)) + isapprox(val(x), y, atol=n * stderror(x)) withinrange(x::Tuple{<:Real, <:Real}, y::Real, nsamples::Int, n::Real) = - isapprox(first(x), y, atol=n*last(x)/sqrt(nsamples)) + isapprox(first(x), y, atol=n * last(x) / sqrt(nsamples)) Base.isapprox(x::T, y::T) where {T <: Tuple} = all(isapprox.(x, y)) Base.isapprox(x::T, y::T) where {T <: ReliabilityMetric} = isapprox(val(x), val(y)) && isapprox(stderror(x), stderror(y)) -Base.isapprox(x::Tuple{Float64,Float64}, y::Vector{<:Real}) = +Base.isapprox(x::Tuple{Float64, Float64}, y::Vector{<:Real}) = isapprox(x[1], mean(y)) && isapprox(x[2], std(y)) @testset "PRAS" begin diff --git a/test/testsystems.jl b/test/testsystems.jl index 861da1cf..ba2e9408 100644 --- a/test/testsystems.jl +++ b/test/testsystems.jl @@ -11,25 +11,35 @@ empty_float(x) = Matrix{Float64}(undef, 0, x) ## Single-Region System A -gens1 = Generators{4,1,Hour,MW}( - ["Gen1", "Gen2", "Gen3", "VG"], ["Gens", "Gens", "Gens", "VG"], +gens1 = Generators{4, 1, Hour, MW}( + ["Gen1", "Gen2", "Gen3", "VG"], + ["Gens", "Gens", "Gens", "VG"], [fill(10, 3, 4); [5 6 7 8]], [fill(0.1, 3, 4); fill(0.0, 1, 4)], - [fill(0.9, 3, 4); fill(1.0, 1, 4)]) + [fill(0.9, 3, 4); fill(1.0, 1, 4)], +) -emptystors1 = Storages{4,1,Hour,MW,MWh}((empty_str for _ in 1:2)..., - (empty_int(4) for _ in 1:3)..., - (empty_float(4) for _ in 1:5)...) +emptystors1 = Storages{4, 1, Hour, MW, MWh}( + (empty_str for _ in 1:2)..., + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:5)..., +) -emptygenstors1 = GeneratorStorages{4,1,Hour,MW,MWh}( +emptygenstors1 = GeneratorStorages{4, 1, Hour, MW, MWh}( (empty_str for _ in 1:2)..., - (empty_int(4) for _ in 1:3)..., (empty_float(4) for _ in 1:3)..., - (empty_int(4) for _ in 1:3)..., (empty_float(4) for _ in 1:2)...) + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:3)..., + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:2)..., +) singlenode_a = SystemModel( - gens1, emptystors1, emptygenstors1, - DateTime(2010,1,1,0):Hour(1):DateTime(2010,1,1,3), - [25, 28, 27, 24]) + gens1, + emptystors1, + emptygenstors1, + DateTime(2010, 1, 1, 0):Hour(1):DateTime(2010, 1, 1, 3), + [25, 28, 27, 24], +) singlenode_a_lole = 0.355 singlenode_a_lolps = [0.028, 0.271, 0.028, 0.028] @@ -38,25 +48,35 @@ singlenode_a_eues = [0.29, 0.832, 0.29, 0.178] ## Single-Region System A - 5 minute version -gens1_5min = Generators{4,5,Minute,MW}( - ["Gen1", "Gen2", "Gen3", "VG"], ["Gens", "Gens", "Gens", "VG"], +gens1_5min = Generators{4, 5, Minute, MW}( + ["Gen1", "Gen2", "Gen3", "VG"], + ["Gens", "Gens", "Gens", "VG"], [fill(10, 3, 4); [5 6 7 8]], [fill(0.1, 3, 4); fill(0.0, 1, 4)], - [fill(0.9, 3, 4); fill(1.0, 1, 4)]) + [fill(0.9, 3, 4); fill(1.0, 1, 4)], +) -emptystors1_5min = Storages{4,5,Minute,MW,MWh}((empty_str for _ in 1:2)..., - (empty_int(4) for _ in 1:3)..., - (empty_float(4) for _ in 1:5)...) +emptystors1_5min = Storages{4, 5, Minute, MW, MWh}( + (empty_str for _ in 1:2)..., + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:5)..., +) -emptygenstors1_5min = GeneratorStorages{4,5,Minute,MW,MWh}( +emptygenstors1_5min = GeneratorStorages{4, 5, Minute, MW, MWh}( (empty_str for _ in 1:2)..., - (empty_int(4) for _ in 1:3)..., (empty_float(4) for _ in 1:3)..., - (empty_int(4) for _ in 1:3)..., (empty_float(4) for _ in 1:2)...) + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:3)..., + (empty_int(4) for _ in 1:3)..., + (empty_float(4) for _ in 1:2)..., +) singlenode_a_5min = ResourceAdequacy.SystemModel( - gens1_5min, emptystors1_5min, emptygenstors1_5min, - DateTime(2010,1,1,0,0):Minute(5):DateTime(2010,1,1,0,15), - [25, 28, 27, 24]) + gens1_5min, + emptystors1_5min, + emptygenstors1_5min, + DateTime(2010, 1, 1, 0, 0):Minute(5):DateTime(2010, 1, 1, 0, 15), + [25, 28, 27, 24], +) singlenode_a_lole = 0.355 singlenode_a_lolps = [0.028, 0.271, 0.028, 0.028] @@ -65,85 +85,143 @@ singlenode_a_eues = [0.29, 0.832, 0.29, 0.178] ## Single-Region System B -gens2 = Generators{6,1,Hour,MW}( - ["Gen1", "Gen2", "VG"], ["Gens", "Gens", "VG"], +gens2 = Generators{6, 1, Hour, MW}( + ["Gen1", "Gen2", "VG"], + ["Gens", "Gens", "VG"], [10 10 10 15 15 15; 20 20 20 25 25 25; 7 8 9 9 8 7], [fill(0.1, 2, 6); fill(0.0, 1, 6)], - [fill(0.9, 2, 6); fill(1.0, 1, 6)]) + [fill(0.9, 2, 6); fill(1.0, 1, 6)], +) -emptystors2 = Storages{6,1,Hour,MW,MWh}((empty_str for _ in 1:2)..., - (empty_int(6) for _ in 1:3)..., - (empty_float(6) for _ in 1:5)...) +emptystors2 = Storages{6, 1, Hour, MW, MWh}( + (empty_str for _ in 1:2)..., + (empty_int(6) for _ in 1:3)..., + (empty_float(6) for _ in 1:5)..., +) -emptygenstors2 = GeneratorStorages{6,1,Hour,MW,MWh}( +emptygenstors2 = GeneratorStorages{6, 1, Hour, MW, MWh}( (empty_str for _ in 1:2)..., - (empty_int(6) for _ in 1:3)..., (empty_float(6) for _ in 1:3)..., - (empty_int(6) for _ in 1:3)..., (empty_float(6) for _ in 1:2)...) + (empty_int(6) for _ in 1:3)..., + (empty_float(6) for _ in 1:3)..., + (empty_int(6) for _ in 1:3)..., + (empty_float(6) for _ in 1:2)..., +) -genstors2 = GeneratorStorages{6,1,Hour,MW,MWh}( - ["Genstor1", "Genstor2"], ["Genstorage", "Genstorage"], - fill(0, 2, 6), fill(0, 2, 6), fill(4, 2, 6), - fill(1.0, 2, 6), fill(1.0, 2, 6), fill(.99, 2, 6), - fill(0, 2, 6), fill(0, 2, 6), fill(0, 2, 6), - fill(0.0, 2, 6), fill(1.0, 2, 6)) +genstors2 = GeneratorStorages{6, 1, Hour, MW, MWh}( + ["Genstor1", "Genstor2"], + ["Genstorage", "Genstorage"], + fill(0, 2, 6), + fill(0, 2, 6), + fill(4, 2, 6), + fill(1.0, 2, 6), + fill(1.0, 2, 6), + fill(0.99, 2, 6), + fill(0, 2, 6), + fill(0, 2, 6), + fill(0, 2, 6), + fill(0.0, 2, 6), + fill(1.0, 2, 6), +) singlenode_b = SystemModel( - gens2, emptystors2, emptygenstors2, - DateTime(2015,6,1,0):Hour(1):DateTime(2015,6,1,5), - [28,29,30,31,32,33]) + gens2, + emptystors2, + emptygenstors2, + DateTime(2015, 6, 1, 0):Hour(1):DateTime(2015, 6, 1, 5), + [28, 29, 30, 31, 32, 33], +) singlenode_b_lole = 0.96 singlenode_b_lolps = [0.19, 0.19, 0.19, 0.1, 0.1, 0.19] singlenode_b_eue = 7.11 singlenode_b_eues = [1.29, 1.29, 1.29, 0.85, 1.05, 1.34] - # Single-Region System B, with storage #TODO: Storage tests -stors2 = Storages{6,1,Hour,MW,MWh}( - ["Stor1", "Stor2"], ["Storage", "Storage"], - repeat([1,0], 1, 6), repeat([1,0], 1, 6), fill(4, 2, 6), - fill(1.0, 2, 6), fill(1.0, 2, 6), fill(.99, 2, 6), - fill(0.0, 2, 6), fill(1.0, 2, 6)) +stors2 = Storages{6, 1, Hour, MW, MWh}( + ["Stor1", "Stor2"], + ["Storage", "Storage"], + repeat([1, 0], 1, 6), + repeat([1, 0], 1, 6), + fill(4, 2, 6), + fill(1.0, 2, 6), + fill(1.0, 2, 6), + fill(0.99, 2, 6), + fill(0.0, 2, 6), + fill(1.0, 2, 6), +) singlenode_stor = SystemModel( - gens2, stors2, genstors2, - DateTime(2015,6,1,0):Hour(1):DateTime(2015,6,1,5), - [28,29,30,31,32,33]) - + gens2, + stors2, + genstors2, + DateTime(2015, 6, 1, 0):Hour(1):DateTime(2015, 6, 1, 5), + [28, 29, 30, 31, 32, 33], +) ## Multi-Region System -regions = Regions{4,MW}(["Region A", "Region B", "Region C"], - [19 20 21 20; 20 21 21 22; 22 21 23 22]) +regions = Regions{4, MW}( + ["Region A", "Region B", "Region C"], + [19 20 21 20; 20 21 21 22; 22 21 23 22], +) -generators = Generators{4,1,Hour,MW}( +generators = Generators{4, 1, Hour, MW}( ["Gen1", "VG A", "Gen 2", "Gen 3", "VG B", "Gen 4", "Gen 5", "VG C"], ["Gens", "VG", "Gens", "Gens", "VG", "Gens", "Gens", "VG"], - [10 10 10 10; 4 3 2 3; # A - 10 10 10 10; 10 10 10 10; 6 5 3 4; # B - 10 10 15 10; 20 20 25 20; 2 1 2 1], # C - [fill(0.1, 1, 4); fill(0.0, 1, 4); # A - fill(0.1, 2, 4); fill(0.0, 1, 4); # B - fill(0.1, 2, 4); fill(0.0, 1, 4)], # C - [fill(0.9, 1, 4); fill(1.0, 1, 4); # A - fill(0.9, 2, 4); fill(1.0, 1, 4); # B - fill(0.9, 2, 4); fill(1.0, 1, 4)]) # C) - -interfaces = Interfaces{4,MW}( - [1,1,2], [2,3,3], fill(100, 3, 4), fill(100, 3, 4)) - -lines = Lines{4,1,Hour,MW}( - ["L1", "L2", "L3"], ["Lines", "Lines", "Lines"], - fill(8, 3, 4), fill(8, 3, 4), fill(0., 3, 4), fill(1., 3, 4)) - -threenode = - SystemModel( - regions, interfaces, generators, [1:2, 3:5, 6:8], - emptystors1, fill(1:0, 3), emptygenstors1, fill(1:0, 3), - lines, [1:1, 2:2, 3:3], - DateTime(2018,10,30,0):Hour(1):DateTime(2018,10,30,3)) + [ + 10 10 10 10 + 4 3 2 3 # A + 10 10 10 10 + 10 10 10 10 + 6 5 3 4 # B + 10 10 15 10 + 20 20 25 20 + 2 1 2 1 + ], # C + [ + fill(0.1, 1, 4) + fill(0.0, 1, 4) # A + fill(0.1, 2, 4) + fill(0.0, 1, 4) # B + fill(0.1, 2, 4) + fill(0.0, 1, 4) + ], # C + [ + fill(0.9, 1, 4) + fill(1.0, 1, 4) # A + fill(0.9, 2, 4) + fill(1.0, 1, 4) # B + fill(0.9, 2, 4) + fill(1.0, 1, 4) + ], +) # C) + +interfaces = Interfaces{4, MW}([1, 1, 2], [2, 3, 3], fill(100, 3, 4), fill(100, 3, 4)) + +lines = Lines{4, 1, Hour, MW}( + ["L1", "L2", "L3"], + ["Lines", "Lines", "Lines"], + fill(8, 3, 4), + fill(8, 3, 4), + fill(0.0, 3, 4), + fill(1.0, 3, 4), +) + +threenode = SystemModel( + regions, + interfaces, + generators, + [1:2, 3:5, 6:8], + emptystors1, + fill(1:0, 3), + emptygenstors1, + fill(1:0, 3), + lines, + [1:1, 2:2, 3:3], + DateTime(2018, 10, 30, 0):Hour(1):DateTime(2018, 10, 30, 3), +) threenode_lole = 1.3756 threenode_lolps = [0.14707, 0.40951, 0.40951, 0.40951] @@ -151,46 +229,67 @@ threenode_eue = 12.12885 threenode_eues = [1.75783, 3.13343, 2.87563, 4.36196] threenode_lole_copperplate = 1.17877 -threenode_lolps_copperplate = [.14707, .40951, .21268, .40951] +threenode_lolps_copperplate = [0.14707, 0.40951, 0.21268, 0.40951] threenode_eue_copperplate = 11.73276 threenode_eues_copperplate = [1.75783, 3.13343, 2.47954, 4.36196] # Test System 1 (2 Gens, 2 Regions) -regions = Regions{1, MW}( - ["Region A", "Region B"], reshape([8, 9], 2, 1)) +regions = Regions{1, MW}(["Region A", "Region B"], reshape([8, 9], 2, 1)) -gens = Generators{1,1,Hour,MW}( - ["Gen 1", "Gen 2"], ["Generators", "Generators"], - fill(15, 2, 1), fill(0.1, 2, 1), fill(0.9, 2, 1)) +gens = Generators{1, 1, Hour, MW}( + ["Gen 1", "Gen 2"], + ["Generators", "Generators"], + fill(15, 2, 1), + fill(0.1, 2, 1), + fill(0.9, 2, 1), +) -emptystors = Storages{1,1,Hour,MW,MWh}((String[] for _ in 1:2)..., - (zeros(Int, 0, 1) for _ in 1:3)..., - (zeros(Float64, 0, 1) for _ in 1:5)...) +emptystors = Storages{1, 1, Hour, MW, MWh}( + (String[] for _ in 1:2)..., + (zeros(Int, 0, 1) for _ in 1:3)..., + (zeros(Float64, 0, 1) for _ in 1:5)..., +) -emptygenstors = GeneratorStorages{1,1,Hour,MW,MWh}( +emptygenstors = GeneratorStorages{1, 1, Hour, MW, MWh}( (String[] for _ in 1:2)..., - (zeros(Int, 0, 1) for _ in 1:3)..., (zeros(Float64, 0, 1) for _ in 1:3)..., - (zeros(Int, 0, 1) for _ in 1:3)..., (zeros(Float64, 0, 1) for _ in 1:2)...) + (zeros(Int, 0, 1) for _ in 1:3)..., + (zeros(Float64, 0, 1) for _ in 1:3)..., + (zeros(Int, 0, 1) for _ in 1:3)..., + (zeros(Float64, 0, 1) for _ in 1:2)..., +) -interfaces = Interfaces{1,MW}([1], [2], fill(8, 1, 1), fill(8, 1, 1)) +interfaces = Interfaces{1, MW}([1], [2], fill(8, 1, 1), fill(8, 1, 1)) -lines = Lines{1,1,Hour,MW}( - ["Line 1"], ["Lines"], - fill(8, 1, 1), fill(8, 1, 1), fill(0.1, 1, 1), fill(0.9, 1, 1) +lines = Lines{1, 1, Hour, MW}( + ["Line 1"], + ["Lines"], + fill(8, 1, 1), + fill(8, 1, 1), + fill(0.1, 1, 1), + fill(0.9, 1, 1), ) -zdt = ZonedDateTime(2020,1,1,0, tz"UTC") -test1 = SystemModel(regions, interfaces, - gens, [1:1, 2:2], emptystors, fill(1:0, 2), emptygenstors, fill(1:0, 2), - lines, [1:1], zdt:Hour(1):zdt +zdt = ZonedDateTime(2020, 1, 1, 0, tz"UTC") +test1 = SystemModel( + regions, + interfaces, + gens, + [1:1, 2:2], + emptystors, + fill(1:0, 2), + emptygenstors, + fill(1:0, 2), + lines, + [1:1], + zdt:Hour(1):zdt, ) -test1_lole = .19 -test1_loles = [.1, .1] +test1_lole = 0.19 +test1_loles = [0.1, 0.1] -test1_eue = .647 -test1_eues = [.314, .333] +test1_eue = 0.647 +test1_eues = [0.314, 0.333] test1_esurplus = 10.647 test1_esurpluses = [5.733, 4.914] @@ -200,21 +299,37 @@ test1_i1_util = 0.231625 # Test System 2 (Gen + Stor, 1 Region) -timestamps = ZonedDateTime(2020,1,1,0, tz"UTC"):Hour(1):ZonedDateTime(2020,1,1,1, tz"UTC") +timestamps = + ZonedDateTime(2020, 1, 1, 0, tz"UTC"):Hour(1):ZonedDateTime(2020, 1, 1, 1, tz"UTC") -gen = Generators{2,1,Hour,MW}( - ["Gen 1"], ["Generators"], - fill(10, 1, 2), fill(0.1, 1, 2), fill(0.9, 1, 2)) +gen = Generators{2, 1, Hour, MW}( + ["Gen 1"], + ["Generators"], + fill(10, 1, 2), + fill(0.1, 1, 2), + fill(0.9, 1, 2), +) -stor = Storages{2,1,Hour,MW,MWh}( - ["Stor 1"], ["Storages"], - fill(10, 1, 2), fill(10, 1, 2), fill(10, 1, 2), - fill(1., 1, 2), fill(1., 1, 2), fill(1., 1, 2), fill(0.1, 1, 2), fill(0.9, 1, 2)) +stor = Storages{2, 1, Hour, MW, MWh}( + ["Stor 1"], + ["Storages"], + fill(10, 1, 2), + fill(10, 1, 2), + fill(10, 1, 2), + fill(1.0, 1, 2), + fill(1.0, 1, 2), + fill(1.0, 1, 2), + fill(0.1, 1, 2), + fill(0.9, 1, 2), +) -emptygenstors = GeneratorStorages{2,1,Hour,MW,MWh}( +emptygenstors = GeneratorStorages{2, 1, Hour, MW, MWh}( (String[] for _ in 1:2)..., - (zeros(Int, 0, 2) for _ in 1:3)..., (zeros(Float64, 0, 2) for _ in 1:3)..., - (zeros(Int, 0, 2) for _ in 1:3)..., (zeros(Float64, 0, 2) for _ in 1:2)...) + (zeros(Int, 0, 2) for _ in 1:3)..., + (zeros(Float64, 0, 2) for _ in 1:3)..., + (zeros(Int, 0, 2) for _ in 1:3)..., + (zeros(Float64, 0, 2) for _ in 1:2)..., +) test2 = SystemModel(gen, stor, emptygenstors, timestamps, [8, 9]) @@ -231,20 +346,37 @@ test2_eenergy = [1.62, 2.2842] # Test System 3 (Gen + Stor, 2 Regions) regions = Regions{2, MW}(["Region A", "Region B"], [8 9; 6 7]) -gen = Generators{2,1,Hour,MW}( - ["Gen 1"], ["Generators"], - fill(25, 1, 2), fill(0.1, 1, 2), fill(0.9, 1, 2)) - -interfaces = Interfaces{2,MW}([1], [2], fill(15, 1, 2), fill(15, 1, 2)) -line = Lines{2,1,Hour,MW}( - ["Line 1"], ["Lines"], - fill(15, 1, 2), fill(15, 1, 2), fill(0.1, 1, 2), fill(0.9, 1, 2) +gen = Generators{2, 1, Hour, MW}( + ["Gen 1"], + ["Generators"], + fill(25, 1, 2), + fill(0.1, 1, 2), + fill(0.9, 1, 2), +) + +interfaces = Interfaces{2, MW}([1], [2], fill(15, 1, 2), fill(15, 1, 2)) +line = Lines{2, 1, Hour, MW}( + ["Line 1"], + ["Lines"], + fill(15, 1, 2), + fill(15, 1, 2), + fill(0.1, 1, 2), + fill(0.9, 1, 2), ) -test3 = SystemModel(regions, interfaces, - gen, [1:1, 2:1], stor, [1:0, 1:1], - emptygenstors, fill(1:0, 2), - line, [1:1], timestamps) +test3 = SystemModel( + regions, + interfaces, + gen, + [1:1, 2:1], + stor, + [1:0, 1:1], + emptygenstors, + fill(1:0, 2), + line, + [1:1], + timestamps, +) test3_lole = 0.320951 test3_lole_r = [0.2, 0.255341] @@ -257,7 +389,7 @@ test3_eue_r = [1.581902, 1.597387] test3_eue_rt = [0.8 0.781902; 1.14 0.457387] test3_esurplus_t = [3.879, 11.53228] -test3_esurplus_rt = [3.879 6.618087; 0. 4.914189] +test3_esurplus_rt = [3.879 6.618087; 0.0 4.914189] test3_flow = 9.5424075 test3_flow_t = [11.421, 7.663815]