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 end
iterate_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