Iteration utilities
Basic iteration
All time structures are iterable over their operational time periods:
julia> using TimeStructjulia> function iterate_ex(periods::TimeStructure) for t in periods println(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 TimeStructjulia> periods = SimpleTimes(5, 1);julia> collect(withprev(periods))5-element Vector{Any}: (nothing, t1) (t1, t2) (t2, t3) (t3, t4) (t4, t5)
A variant of this is the withnext iterator that returns the current and next period (or nothing if none).
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 allow 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], 5)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., modeling of startup processes 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)]))
end3 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] ≤ 3Similarly, if modeling 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)]))
end3 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] ≤ 3It is also possible to get cyclic behavior 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)]))
end3 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] ≤ 3Chunks based on duration
If working with a time structure that has varying duration for its time periods, it can be more convenient to use chunks based on their combined duration.
The chunk_duration function iterates through a time structure, returning subsequences with a duration of 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], 8.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-sc1julia> scenario[3]sp1-t3