Iteration utilities

Basic iteration

All time structures are iterable over their operational time periods

julia> using TimeStruct
julia> function iterate_ex(periods::TimeStructure) for t in periods writeln(t) end enditerate_ex (generic function with 1 method)

Iteration with previous

In many settings, e.g. tracking of storage, it is convenient to have access to the previous time period. By using the custom iterator withprev it is possible to return both the previous and current time period as a tuple when iterating:

julia> using TimeStruct
julia> periods = SimpleTimes(5, 1);
julia> collect(withprev(periods))5-element Vector{Any}: (nothing, t1) (t1, t2) (t2, t3) (t3, t4) (t4, t5)

Iteration with chunks of time periods

Sometimes it is convenient to iterate through the time periods as chunks of a fixed number of periods or minimum duration, e.g. in production planning with minimum production runs. To simplify this process there are several iterator wrappers that allows this kind of iteration pattern.

The chunk function iterates through a time structure returning subsequences of length at most n starting at each time period.

julia> periods = SimpleTimes(5,1)SimpleTimes{Int64}(5, [1, 1, 1, 1, 1])
julia> collect(collect(ts) for ts in chunk(periods, 3))5-element Vector{Vector{TimeStruct.SimplePeriod{Int64}}}: [t1, t2, t3] [t2, t3, t4] [t3, t4, t5] [t4, t5] [t5]

This wrapper can be used for e.g. modelling of startup modelling with a minimum uptime. The following example shows how this can be implemented as part of a JuMP model:

using JuMP, TimeStruct

periods = SimpleTimes(5,1)

m = Model()
@variable(m, startup[periods], Bin)
@variable(m, shutdown[periods], Bin)

for ts in chunk(periods, 3)
    @constraint(m, sum(shutdown[t] for t in ts) <= 3 * (1 - startup[first(ts)]))
end
3 startup[t1] + shutdown[t1] + shutdown[t2] + shutdown[t3] ≤ 3
3 startup[t2] + shutdown[t2] + shutdown[t3] + shutdown[t4] ≤ 3
3 startup[t3] + shutdown[t3] + shutdown[t4] + shutdown[t5] ≤ 3
3 startup[t4] + shutdown[t4] + shutdown[t5] ≤ 3
3 startup[t5] + shutdown[t5] ≤ 3

Similarly, if modelling startup decisions with a minimum downtime, it is possible to reverse the original time periods and then chunk:

for ts in chunk(Iterators.reverse(periods), 3)
    @constraint(m, sum(shutdown[t] for t in ts) <= 3 * (1 - startup[first(ts)]))
end
3 startup[t5] + shutdown[t3] + shutdown[t4] + shutdown[t5] ≤ 3
3 startup[t4] + shutdown[t2] + shutdown[t3] + shutdown[t4] ≤ 3
3 startup[t3] + shutdown[t1] + shutdown[t2] + shutdown[t3] ≤ 3
3 startup[t2] + shutdown[t1] + shutdown[t2] ≤ 3
3 startup[t1] + shutdown[t1] ≤ 3

It is also possible to get cyclic behaviour by setting the cyclic argument to true. If reaching the end before the required number of time periods, the chunk will continue from the first time period.

for ts in chunk(periods, 3; cyclic = true)
    @constraint(m, sum(shutdown[t] for t in ts) <= 3 * (1 - startup[first(ts)]))
end
3 startup[t1] + shutdown[t1] + shutdown[t2] + shutdown[t3] ≤ 3
3 startup[t2] + shutdown[t2] + shutdown[t3] + shutdown[t4] ≤ 3
3 startup[t3] + shutdown[t3] + shutdown[t4] + shutdown[t5] ≤ 3
3 startup[t4] + shutdown[t1] + shutdown[t4] + shutdown[t5] ≤ 3
3 startup[t5] + shutdown[t1] + shutdown[t2] + shutdown[t5] ≤ 3

Chunks based on duration

If working with a time structure that has varying duration for its time periods, it can be more convenient with chunks based on their combined duration.

The chunk_duration function iterates through a time structure returning subsequences of duration at least dur starting at each time period.

julia> periods = SimpleTimes(5,[1, 2, 1, 1.5, 0.5, 2])SimpleTimes{Float64}(5, [1.0, 2.0, 1.0, 1.5, 0.5, 2.0])
julia> collect(collect(ts) for ts in chunk_duration(periods, 3))5-element Vector{Vector{TimeStruct.SimplePeriod{Float64}}}: [t1, t2] [t2, t3] [t3, t4, t5] [t4, t5] [t5]

Indexing of operational time structures

It is possible to use indices for operational time structures, either directly using SimpleTimes or CalendarTimes or by accessing an operational scenario.

julia> periods = TwoLevel(3, 100, SimpleTimes(10,1));
julia> scenario = first(opscenarios(periods))sp1-sc1
julia> scenario[3]sp1-t3