Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement :all strategy for computing rp_periods #215

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading