Skip to content

Commit

Permalink
Implement :all strategy for computing rp_periods
Browse files Browse the repository at this point in the history
  • Loading branch information
abelsiqueira committed Oct 31, 2023
1 parent 293cb60 commit 327dc8d
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 21 deletions.
97 changes: 82 additions & 15 deletions src/time-resolution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function resolution_matrix(
end

"""
rp_periods = compute_rp_periods(array_time_steps)
rp_periods = compute_rp_periods(array_time_steps; strategy = :greedy)
Given the time steps of various flows/assets in the `array_time_steps` input, compute the representative period splits.
Each element of `array_time_steps` is an array of ranges with the following assumptions:
Expand All @@ -68,7 +68,13 @@ Each element of `array_time_steps` is an array of ranges with the following assu
Notice that this implies that they form a disjunct partition of `1:N`.
The output will also be an array of ranges with the conditions above.
The output is constructed greedily, i.e., it selects the next largest breakpoint following the algorithm below:
## Strategies
### :greedy
If `strategy = :greedy` (default), then the output is constructed greedily,
i.e., it selects the next largest breakpoint following the algorithm below:
0. Input: `Vᴵ₁, …, Vᴵₚ`, a list of time step ranges. Each element of `Vᴵⱼ` is a range `r = r.start:r.end`. Output: `V`.
1. Compute the end of the representative period `N` (all `Vᴵⱼ` should have the same end)
Expand All @@ -80,7 +86,7 @@ The output is constructed greedily, i.e., it selects the next largest breakpoint
7. If `e = N`, then END
8. Otherwise, define `s = e + 1` and go to step 4.
## Examples
#### Examples
```jldoctest
time_steps1 = [1:4, 5:8, 9:12]
Expand Down Expand Up @@ -109,10 +115,58 @@ compute_rp_periods([time_steps1, time_steps2])
7:10
11:12
```
### :all
If `strategy = :all`, then the output selects includes all the breakpoints from the input.
Another way of describing it, is to select the minimum end-point instead of the maximum end-point in the `:greedy` strategy.
#### Examples
```jldoctest
time_steps1 = [1:4, 5:8, 9:12]
time_steps2 = [1:3, 4:6, 7:9, 10:12]
compute_rp_periods([time_steps1, time_steps2]; strategy = :all)
# output
6-element Vector{UnitRange{Int64}}:
1:3
4:4
5:6
7:8
9:9
10:12
```
```jldoctest
time_steps1 = [1:1, 2:3, 4:6, 7:10, 11:12]
time_steps2 = [1:2, 3:4, 5:5, 6:7, 8:9, 10:12]
compute_rp_periods([time_steps1, time_steps2]; strategy = :all)
# output
10-element Vector{UnitRange{Int64}}:
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:9
10:10
11:12
```
"""
function compute_rp_periods(
array_time_steps::AbstractVector{<:AbstractVector{<:UnitRange{<:Integer}}},
array_time_steps::AbstractVector{<:AbstractVector{<:UnitRange{<:Integer}}};
strategy = :greedy,
)
valid_strategies = [:greedy, :all]
if !(strategy in valid_strategies)
error("`strategy` should be one of $valid_strategies. See docs for more info.")
end
# Get Vᴵ₁, the last range of it, the last element of the range
representative_period_end = array_time_steps[1][end][end]
for time_steps in array_time_steps
Expand All @@ -121,18 +175,31 @@ function compute_rp_periods(
@assert representative_period_end == time_steps[end][end]
end
rp_periods = UnitRange{Int}[] # List of ranges
period_start = 1

while period_start < representative_period_end
# The first range end larger than period_start for each range in each time_steps.
breakpoints = (
first(r[end] for r in time_steps if r[end] period_start) for
time_steps in array_time_steps
)
period_end = maximum(breakpoints)
@assert period_end period_start
push!(rp_periods, period_start:period_end)
period_start = period_end + 1
period_start = 1
if strategy == :greedy
while period_start < representative_period_end
# The first range end larger than period_start for each range in each time_steps.
breakpoints = (
first(r[end] for r in time_steps if r[end] period_start) for
time_steps in array_time_steps
)
period_end = maximum(breakpoints)
@assert period_end period_start
push!(rp_periods, period_start:period_end)
period_start = period_end + 1
end
elseif strategy == :all
# We need all end points of each interval
end_points_per_array = map(array_time_steps) do x # For each set of time_steps
last.(x) # Retrieve the last element of each interval
end
# Then we concatenate, remove duplicates, and sort.
end_points = vcat(end_points_per_array...) |> unique |> sort
for period_end in end_points
push!(rp_periods, period_start:period_end)
period_start = period_end + 1
end
end
return rp_periods
end
44 changes: 38 additions & 6 deletions test/test-time-resolution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,47 @@
time_steps2 = [1:3, 4:6, 7:9, 10:12] # every 3 hours
time_steps3 = [i:i for i 1:12] # hourly

@test compute_rp_periods([time_steps1, time_steps2]) == time_steps1
@test compute_rp_periods([time_steps1, time_steps2, time_steps3]) == time_steps1
@test compute_rp_periods([time_steps2, time_steps3]) == time_steps2
@testset "strategy greedy (default)" begin
@test compute_rp_periods([time_steps1, time_steps2]) == time_steps1
@test compute_rp_periods([time_steps1, time_steps2, time_steps3]) == time_steps1
@test compute_rp_periods([time_steps2, time_steps3]) == time_steps2
end

@testset "strategy all" begin
@test compute_rp_periods([time_steps1, time_steps2]; strategy = :all) ==
[1:3, 4:4, 5:6, 7:8, 9:9, 10:12]
@test compute_rp_periods(
[time_steps1, time_steps2, time_steps3];
strategy = :all,
) == time_steps3
@test compute_rp_periods([time_steps2, time_steps3]; strategy = :all) ==
time_steps3
end

# Irregular
time_steps4 = [1:6, 7:9, 10:11, 12:12]
time_steps5 = [1:2, 3:4, 5:12]
@test compute_rp_periods([time_steps1, time_steps4]) == [1:6, 7:9, 10:12]
@test compute_rp_periods([time_steps1, time_steps5]) == [1:4, 5:12]
@test compute_rp_periods([time_steps4, time_steps5]) == [1:6, 7:12]

@testset "strategy greedy (default)" begin
@test compute_rp_periods([time_steps1, time_steps4]) == [1:6, 7:9, 10:12]
@test compute_rp_periods([time_steps1, time_steps5]) == [1:4, 5:12]
@test compute_rp_periods([time_steps4, time_steps5]) == [1:6, 7:12]
end

@testset "strategy all" begin
@test compute_rp_periods([time_steps1, time_steps4]; strategy = :all) ==
[1:4, 5:6, 7:8, 9:9, 10:11, 12:12]
@test compute_rp_periods([time_steps1, time_steps5]; strategy = :all) ==
[1:2, 3:4, 5:8, 9:12]
@test compute_rp_periods([time_steps4, time_steps5]; strategy = :all) ==
[1:2, 3:4, 5:6, 7:9, 10:11, 12:12]
end

@testset "Bad strategy" begin
@test_throws ErrorException compute_rp_periods(
[time_steps1, time_steps2],
strategy = :bad,
)
end
end
end

0 comments on commit 327dc8d

Please sign in to comment.