From 1de18d2468770639ed5d2d51db2963cc055a46b2 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Thu, 29 Aug 2024 14:26:59 +0200 Subject: [PATCH 01/20] Restructured representative periods --- src/TimeStruct.jl | 5 +- src/opscenarios.jl | 22 +- src/repr_periods.jl | 252 ------------------ .../core_types.jl} | 80 +++--- src/representative/rep_periods.jl | 200 ++++++++++++++ src/representative/strat_periods.jl | 116 ++++++++ src/stochastic.jl | 4 +- test/runtests.jl | 44 ++- 8 files changed, 417 insertions(+), 306 deletions(-) delete mode 100644 src/repr_periods.jl rename src/{representative.jl => representative/core_types.jl} (78%) create mode 100644 src/representative/rep_periods.jl create mode 100644 src/representative/strat_periods.jl diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index d1f327e..3f72a11 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -9,11 +9,12 @@ import .Base: include("structures.jl") include("simple.jl") include("calendar.jl") -include("representative.jl") +include(joinpath("representative", "core_types.jl")) include("stochastic.jl") include("twolevel.jl") include("strat_periods.jl") -include("repr_periods.jl") +include(joinpath("representative", "rep_periods.jl")) +include(joinpath("representative", "strat_periods.jl")) include("opscenarios.jl") include("utils.jl") include("discount.jl") diff --git a/src/opscenarios.jl b/src/opscenarios.jl index fe63c7c..484639d 100644 --- a/src/opscenarios.jl +++ b/src/opscenarios.jl @@ -185,7 +185,7 @@ Iterator that iterates over operational scenarios in a `RepresentativePeriod` ti function opscenarios( rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}, ) where {T,OP} - return RepOpScens(rep.rper, rep.mult_rp, rep.operational) + return RepOpScens(_rper(rep), mult_repr(rep), rep.operational) end Base.length(ros::RepOpScens) = length(ros.op_scens) @@ -372,8 +372,10 @@ _opscen(sro::StratReprOpscenario) = sro.opscen _strat_per(sro::StratReprOpscenario) = sro.sp _rper(sro::StratReprOpscenario) = sro.rp +mult_repr(sro::StratReprOpscenario) = sro.mult_rp + function Base.show(io::IO, srop::StratReprOpscenario) - return print(io, "sp$(srop.sp)-rp$(srop.rp)-sc$(srop.opscen)") + return print(io, "sp$(srop.sp)-rp$(_rper(srop))-sc$(srop.opscen)") end StrategicIndexable(::Type{<:StratReprOpscenario}) = HasStratIndex() @@ -389,11 +391,11 @@ function Base.iterate(os::StratReprOpscenario, state = nothing) iterate(os.operational, state) isnothing(next) && return nothing - period = ReprPeriod(os.rp, next[1], os.mult_rp * multiple(next[1])) + period = ReprPeriod(_rper(os), next[1], mult_repr(os) * multiple(next[1])) return OperationalPeriod( os.sp, period, - os.mult_sp * os.mult_rp * multiple(next[1]), + os.mult_sp * mult_repr(os) * multiple(next[1]), ), next[2] end @@ -406,17 +408,17 @@ end function Base.last(sro::StratReprOpscenario) per = last(sro.operational) - rper = ReprPeriod(sro.rp, per, sro.mult_rp * multiple(per)) + rper = ReprPeriod(_rper(sro), per, mult_repr(sro) * multiple(per)) return OperationalPeriod( sro.sp, rper, - sro.mult_sp * sro.mult_rp * multiple(per), + sro.mult_sp * mult_repr(sro) * multiple(per), ) end function Base.getindex(os::StratReprOpscenario, index) mult = stripunit(os.duration * os.op_per_strat / duration(os.operational)) - period = ReprPeriod(os.rp, os.operational[index], mult) + period = ReprPeriod(_rper(os), os.operational[index], mult) return OperationalPeriod(os.sp, period, mult) end @@ -450,10 +452,10 @@ function Base.iterate(srop::StratReprOpscenarios, state = (nothing, 1)) scen = state[2] return StratReprOpscenario( srop.srp.sp, - srop.srp.rp, + _rper(srop.srp), scen, srop.srp.mult_sp, - srop.srp.mult_rp, + mult_repr(srop.srp), probability(next[1]), next[1], ), @@ -490,7 +492,7 @@ function Base.last( ss::SingleScenario{T,RepresentativePeriod{T,OP}}, ) where {T,OP} period = last(ss.ts.operational) - return ReprPeriod(ss.ts.rper, period, ss.ts.mult_rp * multiple(period)) + return ReprPeriod(_rper(ss.ts), period, mult_repr(ss.ts) * multiple(period)) end function Base.getindex(ss::SingleScenario, index) diff --git a/src/repr_periods.jl b/src/repr_periods.jl deleted file mode 100644 index 62c113a..0000000 --- a/src/repr_periods.jl +++ /dev/null @@ -1,252 +0,0 @@ - -""" - AbstractRepresentativePeriod - -Abstract type used for time structures that represent a representative -period. -""" -abstract type AbstractRepresentativePeriod{T} <: TimeStructure{T} end - -function _rper(rp::AbstractRepresentativePeriod) - return error("_rper() not implemented for $(typeof(rp))") -end -isfirst(rp::AbstractRepresentativePeriod) = _rper(rp) == 1 -mult_repr(rp::AbstractRepresentativePeriod) = 1 - -function Base.isless( - rp1::AbstractRepresentativePeriod, - rp2::AbstractRepresentativePeriod, -) - return _rper(rp1) < _rper(rp2) -end - -abstract type RepresentativeIndexable end - -struct HasReprIndex <: RepresentativeIndexable end -struct NoReprIndex <: RepresentativeIndexable end - -RepresentativeIndexable(::Type) = NoReprIndex() -RepresentativeIndexable(::Type{<:AbstractRepresentativePeriod}) = HasReprIndex() -RepresentativeIndexable(::Type{<:TimePeriod}) = HasReprIndex() - -""" - RepresentativePeriod - -A structure representing a single representative period supporting -iteration over its time periods. -""" -struct RepresentativePeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} - rper::Int - mult_rp::Float64 - operational::OP -end -Base.show(io::IO, rp::RepresentativePeriod) = print(io, "rp-$(rp.rper)") -_rper(rp::RepresentativePeriod) = rp.rper -mult_repr(rp::RepresentativePeriod) = rp.mult_rp - -# Iterate the time periods of a representative period -function Base.iterate(rp::RepresentativePeriod, state = nothing) - next = - isnothing(state) ? iterate(rp.operational) : - iterate(rp.operational, state) - next === nothing && return nothing - mult = rp.mult_rp * multiple(next[1]) - return ReprPeriod(rp.rper, next[1], mult), next[2] -end - -function Base.last(rp::RepresentativePeriod) - per = last(rp.operational) - mult = rp.mult_rp * multiple(per) - return ReprPeriod(rp.rper, per, mult) -end - -Base.length(rp::RepresentativePeriod) = length(rp.operational) -Base.eltype(::Type{RepresentativePeriod}) = ReprPeriod - -# Iteration through representative periods -struct ReprPeriods{T,OP} - ts::RepresentativePeriods{T,OP} -end - -""" - repr_periods(ts) - -Iterator that iterates over representative periods in an `RepresentativePeriods` time structure. -""" -repr_periods(ts::RepresentativePeriods) = ReprPeriods(ts) - -Base.length(rpers::ReprPeriods) = rpers.ts.len - -function Base.iterate(rpers::ReprPeriods) - return RepresentativePeriod( - 1, - _multiple_adj(rpers.ts, 1), - rpers.ts.rep_periods[1], - ), - 1 -end - -function Base.iterate(rpers::ReprPeriods, state) - state == rpers.ts.len && return nothing - return RepresentativePeriod( - state + 1, - _multiple_adj(rpers.ts, state + 1), - rpers.ts.rep_periods[state+1], - ), - state + 1 -end - -function Base.last(rpers::ReprPeriods) - return RepresentativePeriod( - rpers.ts.len, - _multiple_adj(rpers.ts, rpers.ts.len), - rpers.ts.rep_periods[rpers.ts.len], - ) -end - -struct StratReprPeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} - sp::Int - rp::Int - mult_sp::Float64 - mult_rp::Float64 - operational::OP -end - -StrategicIndexable(::Type{<:StratReprPeriod}) = HasStratIndex() - -multiple(srp::StratReprPeriod, t::OperationalPeriod) = t.multiple / srp.mult_sp - -function Base.show(io::IO, srp::StratReprPeriod) - return print(io, "sp$(srp.sp)-rp$(srp.rp)") -end - -_rper(srp::StratReprPeriod) = srp.rp -_strat_per(srp::StratReprPeriod) = srp.sp - -mult_repr(srp::StratReprPeriod) = srp.mult_rp - -# Iterate the time periods of a StratReprPeriod -function Base.iterate(srp::StratReprPeriod, state = nothing) - next = - isnothing(state) ? iterate(srp.operational) : - iterate(srp.operational, state) - isnothing(next) && return nothing - - per = next[1] - mult = srp.mult_sp * multiple(per) - return OperationalPeriod(srp.sp, per, mult), next[2] -end - -Base.length(srp::StratReprPeriod) = length(srp.operational) - -function Base.last(srp::StratReprPeriod) - per = last(srp.operational) - mult = srp.mult_sp * multiple(per) - return OperationalPeriod(srp.sp, per, mult) -end - -function Base.eltype(::Type{StratReprPeriod{T}}) where {T} - return OperationalPeriod -end - -# Iteration through representative periods -struct StratReprPeriods - sper::StrategicPeriod - repr::Any -end - -""" - repr_periods(sper) - - Iterator that iterates over representative periods for a specific strategic period. -""" -function repr_periods(sper::StrategicPeriod) - return StratReprPeriods(sper, repr_periods(sper.operational)) -end - -""" - repr_periods(ts) - - Returns a collection of all representative periods for a TwoLevel time structure. -""" -function repr_periods(ts::TwoLevel) - return collect( - Iterators.flatten(repr_periods(sp) for sp in strategic_periods(ts)), - ) -end - -Base.length(reps::StratReprPeriods) = length(reps.repr) - -function Base.iterate(reps::StratReprPeriods, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(reps.repr) : iterate(reps.repr, state[1]) - isnothing(next) && return nothing - - rper = state[2] - mult_sp = reps.sper.mult_sp - return StratReprPeriod( - reps.sper.sp, - rper, - mult_sp, - mult_repr(next[1]), - next[1], - ), - (next[2], rper + 1) -end - -function Base.last(reps::StratReprPeriods) - per = last(reps.repr) - - return StratReprPeriod( - reps.sper.sp, - _rper(per), - reps.sper.mult_sp, - mult_repr(per), - per, - ) -end - -struct SingleReprPeriodWrapper{T,RP<:TimeStructure{T}} <: TimeStructure{T} - ts::RP -end - -function Base.iterate(srp::SingleReprPeriodWrapper, state = nothing) - !isnothing(state) && return nothing - return SingleReprPeriod(srp.ts), 1 -end -Base.length(srp::SingleReprPeriodWrapper) = 1 -function Base.eltype(::Type{SingleReprPeriodWrapper{T,RP}}) where {T,RP} - return SingleReprPeriod{T,RP} -end -Base.last(srp::SingleReprPeriodWrapper) = SingleReprPeriod(srp.ts) - -struct SingleReprPeriod{T,RP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} - ts::RP -end -_rper(rp::SingleReprPeriod) = 1 -_strat_per(rp::SingleReprPeriod) = 1 -mult_repr(rp::SingleReprPeriod) = 1.0 - -Base.length(srp::SingleReprPeriod) = length(srp.ts) -Base.eltype(::Type{SingleReprPeriod{T,RP}}) where {T,RP} = eltype(RP) - -StrategicIndexable(::Type{<:SingleReprPeriod}) = HasStratIndex() - -function Base.iterate(srp::SingleReprPeriod, state = nothing) - if isnothing(state) - return iterate(srp.ts) - end - return iterate(srp.ts, state) -end - -Base.last(srp::SingleReprPeriod) = last(srp.ts) - -# Default solution is to behave as a single representative period -repr_periods(ts::TimeStructure) = SingleReprPeriodWrapper(ts) - -# If the strategic level is a wrapped time structure, shortcut to get -# correct behaviour -repr_periods(ts::SingleStrategicPeriod) = repr_periods(ts.ts) diff --git a/src/representative.jl b/src/representative/core_types.jl similarity index 78% rename from src/representative.jl rename to src/representative/core_types.jl index 56053d2..8c4a199 100644 --- a/src/representative.jl +++ b/src/representative/core_types.jl @@ -126,68 +126,70 @@ function RepresentativePeriods( ) end -_total_duration(ts::RepresentativePeriods) = ts.duration +_total_duration(rpers::RepresentativePeriods) = rpers.duration -function _multiple_adj(ts::RepresentativePeriods, rper) +function _multiple_adj(rpers::RepresentativePeriods, rp) mult = - _total_duration(ts) * ts.period_share[rper] / - _total_duration(ts.rep_periods[rper]) + _total_duration(rpers) * rpers.period_share[rp] / + _total_duration(rpers.rep_periods[rp]) return stripunit(mult) end -# Iteration through all time periods for the representative periods -function Base.iterate(ts::RepresentativePeriods) - rp = 1 - next = iterate(ts.rep_periods[rp]) - next === nothing && return nothing - mult = _multiple_adj(ts, rp) * multiple(next[1]) - return ReprPeriod(rp, next[1], mult), (rp, next[2]) -end - -function Base.iterate(ts::RepresentativePeriods, state) - rp = state[1] - next = iterate(ts.rep_periods[rp], state[2]) +function Base.iterate(rpers::RepresentativePeriods, state = (nothing, 1)) + rp = state[2] + next = + isnothing(state[1]) ? iterate(rpers.rep_periods[rp]) : + iterate(rpers.rep_periods[rp], state[1]) if next === nothing rp = rp + 1 - if rp > ts.len + if rp > rpers.len return nothing end - next = iterate(ts.rep_periods[rp]) + next = iterate(rpers.rep_periods[rp]) end - mult = _multiple_adj(ts, rp) * multiple(next[1]) - return ReprPeriod(rp, next[1], mult), (rp, next[2]) + return ReprPeriod(rpers, next[1], rp), (next[2], rp) +end + +function Base.length(rpers::RepresentativePeriods) + return sum(length(rp) for rp in rpers.rep_periods) end -function Base.length(ts::RepresentativePeriods) - return sum(length(rpers) for rpers in ts.rep_periods) +function Base.last(rpers::RepresentativePeriods) + per = last(rpers.rep_periods[rpers.len]) + return ReprPeriod(rpers, per, rpers.len) end -function Base.last(ts::RepresentativePeriods) - per = last(ts.rep_periods[ts.len]) - mult = _multiple_adj(ts, ts.len) * multiple(per) - return ReprPeriod(ts.len, per, mult) +function Base.eltype(::Type{RepresentativePeriods{S,T,OP}}) where {S,T,OP} + return ReprPeriod{eltype(OP)} end -Base.eltype(::Type{RepresentativePeriods}) = ReprPeriod +""" + ReprPeriod{P} <: TimePeriod where {P<:TimePeriod} -# A single operational time period used when iterating through -# a represenative period -struct ReprPeriod{T} <: TimePeriod +Time period for a single operational period. It is created through iterating through a +[`RepresentativePeriods`](@ref) time structure. It is as well created as period within +[`OperationalPeriod`](@ref) when the time structure includes [`RepresentativePeriods`](@ref). +""" +struct ReprPeriod{P} <: TimePeriod where {P<:TimePeriod} rp::Int - period::T + period::P mult::Float64 end +_oper(t::ReprPeriod) = _oper(t.period) +_opscen(t::ReprPeriod) = _opscen(t.period) +_rper(t::ReprPeriod) = t.rp + +isfirst(t::ReprPeriod) = isfirst(t.period) +duration(t::ReprPeriod) = duration(t.period) +multiple(t::ReprPeriod) = t.mult +probability(t::ReprPeriod) = probability(t.period) Base.show(io::IO, rp::ReprPeriod) = print(io, "rp$(rp.rp)-$(rp.period)") function Base.isless(t1::ReprPeriod, t2::ReprPeriod) return t1.rp < t2.rp || (t1.rp == t2.rp && t1.period < t2.period) end -isfirst(t::ReprPeriod) = isfirst(t.period) -duration(t::ReprPeriod) = duration(t.period) -probability(t::ReprPeriod) = probability(t.period) -multiple(t::ReprPeriod) = t.mult - -_oper(t::ReprPeriod) = _oper(t.period) -_opscen(t::ReprPeriod) = _opscen(t.period) -_rper(t::ReprPeriod) = t.rp +function ReprPeriod(rpers::RepresentativePeriods, per::TimePeriod, rp::Int) + mult = _multiple_adj(rpers, rp) * multiple(per) + return ReprPeriod(rp, per, mult) +end diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl new file mode 100644 index 0000000..0fc9e2a --- /dev/null +++ b/src/representative/rep_periods.jl @@ -0,0 +1,200 @@ + +""" + AbstractRepresentativePeriod{T} <: TimeStructure{T} + +Abstract type used for time structures that represent a representative period. +These periods is obtained when iterating through the representative periods of a time +structure declared using the function [`repr_period`](@ref.) +""" +abstract type AbstractRepresentativePeriod{T} <: TimeStructure{T} end + +function _rper(rp::AbstractRepresentativePeriod) + return error("_rper() not implemented for $(typeof(rp))") +end +isfirst(rp::AbstractRepresentativePeriod) = _rper(rp) == 1 +mult_repr(rp::AbstractRepresentativePeriod) = 1 + +function Base.isless( + rp1::AbstractRepresentativePeriod, + rp2::AbstractRepresentativePeriod, +) + return _rper(rp1) < _rper(rp2) +end + +abstract type RepresentativeIndexable end + +struct HasReprIndex <: RepresentativeIndexable end +struct NoReprIndex <: RepresentativeIndexable end + +RepresentativeIndexable(::Type) = NoReprIndex() +RepresentativeIndexable(::Type{<:AbstractRepresentativePeriod}) = HasReprIndex() +RepresentativeIndexable(::Type{<:TimePeriod}) = HasReprIndex() + +""" + SingleReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} + +A type representing a single representative period supporting iteration over its +time periods. It is created through iterating through [`SingleReprPeriodWrapper`](@ref). +""" +struct SingleReprPeriod{T,OP<:TimeStructure{T}} <: + AbstractRepresentativePeriod{T} + ts::OP +end + +_rper(rp::SingleReprPeriod) = 1 + +mult_repr(rp::SingleReprPeriod) = 1.0 + +Base.length(rp::SingleReprPeriod) = length(rp.ts) +Base.eltype(::Type{SingleReprPeriod{T,OP}}) where {T,OP} = eltype(OP) + +StrategicIndexable(::Type{<:SingleReprPeriod}) = HasStratIndex() + +function Base.iterate(rp::SingleReprPeriod, state = nothing) + next = isnothing(state) ? iterate(rp.ts) : iterate(rp.ts, state) + return next +end +Base.last(rp::SingleReprPeriod) = last(rp.ts) + +""" + SingleReprPeriodWrapper{T,OP<:TimeStructure{T}} <: TimeStructure{T} + +Iterator for iterating through the individual presentative periods of a time structure +without [`RepresentativePeriods`](@ref). It is automatically created through the function +[`repr_periods`](@ref). +""" +struct SingleReprPeriodWrapper{T,OP<:TimeStructure{T}} <: TimeStructure{T} + ts::OP +end + +""" + repr_periods(ts::TimeStructure) + +This function returns an iterator for iterating through the individual representative +periods of a `TimeStructure`. The type of the iterator is dependent on the type of the +input `TimeStructure`, + +When the `TimeStructure` is a `TimeStructure`, `repr_periods` returns a +[`SingleReprPeriodWrapper`](@ref). This coresponds to the default behavior. +""" +repr_periods(ts::TimeStructure) = SingleReprPeriodWrapper(ts) + +Base.length(rpers::SingleReprPeriodWrapper) = 1 +function Base.eltype(::Type{SingleReprPeriodWrapper{T,OP}}) where {T,OP} + return SingleReprPeriod{T,OP} +end + +function Base.iterate(rpers::SingleReprPeriodWrapper, state = nothing) + !isnothing(state) && return nothing + return SingleReprPeriod(rpers.ts), 1 +end +Base.last(rpers::SingleReprPeriodWrapper) = SingleReprPeriod(rpers.ts) + +""" +When the `TimeStructure` is a [`TwoLevel`](@ref), `repr_periods` returns an `Array` of +all [`StratReprPeriod`](@ref)s. +""" +function repr_periods(ts::TwoLevel) + return collect( + Iterators.flatten(repr_periods(sp) for sp in strategic_periods(ts)), + ) +end + +""" + RepresentativePeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} + +A type representing a single representative period supporting iteration over its +time periods. It is created through iterating through [`ReprPeriods`](@ref). +""" +struct RepresentativePeriod{T,OP<:TimeStructure{T}} <: + AbstractRepresentativePeriod{T} + rp::Int + mult_rp::Float64 + operational::OP +end + +_rper(rp::RepresentativePeriod) = rp.rp + +mult_repr(rp::RepresentativePeriod) = rp.mult_rp + +Base.show(io::IO, rp::RepresentativePeriod) = print(io, "rp-$(_rper(rp))") +function Base.eltype(_::Type{RepresentativePeriod{T,OP}}) where {T,OP} + return ReprPeriod{eltype(OP)} +end +Base.length(rp::RepresentativePeriod) = length(rp.operational) + +# Provide a constructor to simplify the design +function ReprPeriod( + rp::RepresentativePeriod, + per::P, +) where {P<:Union{TimePeriod,TimeStructure}} + mult = mult_repr(rp) * multiple(per) + return ReprPeriod(_rper(rp), per, mult) +end + +# Iterate the time periods of a RepresentativePeriod +function Base.iterate(rp::RepresentativePeriod, state = nothing) + next = + isnothing(state) ? iterate(rp.operational) : + iterate(rp.operational, state) + isnothing(next) && return nothing + + return ReprPeriod(rp, next[1]), next[2] +end +function Base.getindex(rp::RepresentativePeriod, index::Int) + per = rp.operational[index] + return ReprPeriod(rp, per) +end +function Base.eachindex(rp::RepresentativePeriod) + return eachindex(rp.operational) +end +function Base.last(rp::RepresentativePeriod) + per = last(rp.operational) + return ReprPeriod(rp, per) +end + +""" + ReprPeriods{T,OP} + +Iterator for iterating through the individual presentative periods of a +[`RepresentativePeriods`](@ref) time structure. It is automatically created through the +function [`repr_periods`](@ref). +""" +struct ReprPeriods{S,T,OP} + ts::RepresentativePeriods{S,T,OP} +end + +""" +When the `TimeStructure` is a [`RepresentativePeriods`](@ref), `repr_periods` returns the +iterator [`ReprPeriods`](@ref). +""" +repr_periods(ts::RepresentativePeriods) = ReprPeriods(ts) + +function Base.eltype(_::ReprPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return RepresentativePeriod{T,OP} +end +Base.length(rpers::ReprPeriods) = rpers.ts.len + +# Provide a constructor to simplify the design +function RepresentativePeriod(rpers::ReprPeriods, per::Int) + return RepresentativePeriod( + per, + _multiple_adj(rpers.ts, per), + rpers.ts.rep_periods[per], + ) +end + +# Iterate the time periods of a ReprPeriods +function Base.iterate(rpers::ReprPeriods, state = nothing) + per = isnothing(state) ? 1 : state + 1 + per === length(rpers) + 1 && return nothing + + return RepresentativePeriod(rpers, per), per +end +function Base.getindex(rpers::ReprPeriods, index::Int) + return RepresentativePeriod(rpers, index) +end +function Base.eachindex(rpers::ReprPeriods) + return eachindex(rpers.ts.rep_periods) +end +Base.last(rpers::ReprPeriods) = RepresentativePeriod(rpers, length(rpers)) diff --git a/src/representative/strat_periods.jl b/src/representative/strat_periods.jl new file mode 100644 index 0000000..ad79531 --- /dev/null +++ b/src/representative/strat_periods.jl @@ -0,0 +1,116 @@ + +""" + StratReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} + +A type representing a single representative period supporting iteration over its +time periods. It is created through iterating through [`StratReprPeriods`](@ref). +""" +struct StratReprPeriod{T,OP<:TimeStructure{T}} <: + AbstractRepresentativePeriod{T} + sp::Int + rp::Int + mult_sp::Float64 + mult_rp::Float64 + operational::OP +end + +_rper(rp::StratReprPeriod) = rp.rp +_strat_per(rp::StratReprPeriod) = rp.sp + +multiple(rp::StratReprPeriod, t::OperationalPeriod) = t.multiple / rp.mult_sp +mult_repr(rp::StratReprPeriod) = rp.mult_rp + +StrategicIndexable(::Type{<:StratReprPeriod}) = HasStratIndex() + +function Base.show(io::IO, rp::StratReprPeriod) + return print(io, "sp$(_strat_per(rp))-rp$(_rper(rp))") +end +Base.eltype(_::Type{StratReprPeriod{T,OP}}) where {T,OP} = OperationalPeriod +Base.length(rp::StratReprPeriod) = length(rp.operational) + +# Provide a constructor to simplify the design +function OperationalPeriod(rp::StratReprPeriod, per) + mult = rp.mult_sp * multiple(per) + return OperationalPeriod(rp.sp, per, mult) +end + +# Iterate the time periods of a StratReprPeriod +function Base.iterate(rp::StratReprPeriod, state = nothing) + next = + isnothing(state) ? iterate(rp.operational) : + iterate(rp.operational, state) + isnothing(next) && return nothing + + return OperationalPeriod(rp, next[1]), next[2] +end +function Base.getindex(rp::StratReprPeriod, index::Int) + per = rp.operational[index] + return OperationalPeriod(rp, per) +end +function Base.eachindex(rp::StratReprPeriod) + return eachindex(rp.operational) +end +function Base.last(rp::StratReprPeriod) + per = last(rp.operational) + return OperationalPeriod(rp, per) +end + +""" + StratReprPeriods{OP} + +Iterator for iterating through the individual presentative periods of a +[`StrategicPeriod`](@ref) time structure. It is automatically created through the function +[`repr_periods`](@ref). +""" +struct StratReprPeriods{OP} + sp::Int + mult_sp::Float64 + repr::OP +end + +function StratReprPeriods( + sp::StrategicPeriod{S,T,OP}, + repr, +) where {S,T,OP<:TimeStructure{T}} + return StratReprPeriods(_strat_per(sp), mult_strat(sp), repr) +end + +""" +When the `TimeStructure` is a [`StrategicPeriod`](@ref), `repr_periods` returns the iterator +[`StratReprPeriods`](@ref). +""" +function repr_periods(sp::StrategicPeriod) + return StratReprPeriods(sp, repr_periods(sp.operational)) +end +Base.length(rpers::StratReprPeriods) = length(rpers.repr) + +# Provide a constructor to simplify the design +function StratReprPeriod(rpers::StratReprPeriods, state, per) + return StratReprPeriod(rpers.sp, state, rpers.mult_sp, mult_repr(per), per) +end + +# Iterate the time periods of a StratReprPeriods +function Base.iterate(rpers::StratReprPeriods, state = (nothing, 1)) + next = + isnothing(state[1]) ? iterate(rpers.repr) : + iterate(rpers.repr, state[1]) + isnothing(next) && return nothing + + return StratReprPeriod(rpers, state[2], next[1]), (next[2], state[2] + 1) +end +function Base.getindex(rpers::StratReprPeriods, index::Int) + return StratReprPeriod(rpers, index) +end +function Base.eachindex(rpers::StratReprPeriods) + return eachindex(rpers.rep_periods) +end +function Base.last(rpers::StratReprPeriods) + per = last(rpers.repr) + return StratReprPeriod(rpers, _rper(per), per) +end + +""" +When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `repr_periods` returns the +correct behavior based on the substructure. +""" +repr_periods(ts::SingleStrategicPeriod) = repr_periods(ts.ts) diff --git a/src/stochastic.jl b/src/stochastic.jl index e243d9b..0f79cbc 100644 --- a/src/stochastic.jl +++ b/src/stochastic.jl @@ -111,7 +111,9 @@ function Base.length(itr::OperationalScenarios) return sum(length(itr.scenarios[sc]) for sc in 1:itr.len) end -Base.eltype(::Type{OperationalScenarios{T}}) where {T} = ScenarioPeriod +function Base.eltype(::Type{OperationalScenarios{T,OP}}) where {T,OP} + return ScenarioPeriod{eltype(OP)} +end function Base.last(itr::OperationalScenarios) return ScenarioPeriod( diff --git a/test/runtests.jl b/test/runtests.jl index ec06e4d..565f074 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -183,14 +183,54 @@ end [0.4, 0.6], [SimpleTimes(24, 1), SimpleTimes(24, 1)], ) - @test length(rep) == length(collect(rep)) + + # Test of direct functions of `RepresentativePeriods` + rps = collect(rep) + @test length(rep) == length(rps) @test TimeStruct._total_duration(rep) == 8760 + @test TimeStruct._multiple_adj(rep, 1) == 8760 * 0.4 / 24 + @test last(rep) == rps[end] + @test eltype(rep) == TimeStruct.ReprPeriod{TimeStruct.SimplePeriod{Int}} + + # Test of direct functions of `ReprPeriod` + @test isfirst(rps[1]) + @test !isfirst(rps[end]) + @test duration(rps[15]) == 1 + @test multiple(rps[1]) == 8760 * 0.4 / 24 + @test multiple(rps[25]) == 8760 * 0.6 / 24 + @test probability(rps[1]) == 1 + @test rps[1] < rps[2] + @test rps[1] < rps[25] + + # Test of direct functions of `ReprPeriods` + rpers = repr_periods(rep) + @test eltype(rpers) == + TimeStruct.RepresentativePeriod{Int64,SimpleTimes{Int64}} + @test last(rpers) == collect(rpers)[end] + @test length(rpers) == 2 + index = eachindex(rpers) + @test sum(rpers[idx] == collect(rpers)[idx] for idx in index) == 2 + + # Test of direct functions of `RepresentativePeriod` + rp = rpers[index[2]] + @test TimeStruct._rper(rp) == 2 + @test TimeStruct.mult_repr(rp) == 8760 * 0.6 / 24 + @test eltype(rp) == TimeStruct.ReprPeriod{TimeStruct.SimplePeriod{Int64}} + @test length(rp) == 24 + index = eachindex(rp) + @test sum(rp[idx] == collect(rp)[idx] for idx in index) == 24 + @test last(rp) == rp[24] # SimpleTimes as one representative period simple = SimpleTimes(10, 1) - pers = [t for rp in repr_periods(simple) for t in rp] + rpers = repr_periods(simple) + pers = [t for rp in rpers for t in rp] @test pers == collect(simple) + @test last(pers) == last(simple) @test sum(duration(t) * multiple(t) for t in pers) == 10 + @test length(rpers) == 1 + @test eltype(rpers) == TimeStruct.SingleReprPeriod{Int64,SimpleTimes{Int64}} + @test last(rpers) == collect(rpers)[1] # Testing the internal constructor day = SimpleTimes(24, 1) From 1461b25e9e10cbf3670b2dca969d8e7d66993162 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 3 Sep 2024 12:23:06 +0200 Subject: [PATCH 02/20] Fixed error on x86 --- test/runtests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 565f074..fd140bd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -205,7 +205,7 @@ end # Test of direct functions of `ReprPeriods` rpers = repr_periods(rep) @test eltype(rpers) == - TimeStruct.RepresentativePeriod{Int64,SimpleTimes{Int64}} + TimeStruct.RepresentativePeriod{Int,SimpleTimes{Int}} @test last(rpers) == collect(rpers)[end] @test length(rpers) == 2 index = eachindex(rpers) @@ -215,7 +215,7 @@ end rp = rpers[index[2]] @test TimeStruct._rper(rp) == 2 @test TimeStruct.mult_repr(rp) == 8760 * 0.6 / 24 - @test eltype(rp) == TimeStruct.ReprPeriod{TimeStruct.SimplePeriod{Int64}} + @test eltype(rp) == TimeStruct.ReprPeriod{TimeStruct.SimplePeriod{Int}} @test length(rp) == 24 index = eachindex(rp) @test sum(rp[idx] == collect(rp)[idx] for idx in index) == 24 @@ -229,7 +229,7 @@ end @test last(pers) == last(simple) @test sum(duration(t) * multiple(t) for t in pers) == 10 @test length(rpers) == 1 - @test eltype(rpers) == TimeStruct.SingleReprPeriod{Int64,SimpleTimes{Int64}} + @test eltype(rpers) == TimeStruct.SingleReprPeriod{Int,SimpleTimes{Int}} @test last(rpers) == collect(rpers)[1] # Testing the internal constructor From 6dba3fbb9787231f230a14bb014dc90e291802c8 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Mon, 9 Sep 2024 08:15:15 +0200 Subject: [PATCH 03/20] Included typo fixed from PR --- src/representative/rep_periods.jl | 12 ++++++------ src/representative/strat_periods.jl | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl index 0fc9e2a..129c43e 100644 --- a/src/representative/rep_periods.jl +++ b/src/representative/rep_periods.jl @@ -3,7 +3,7 @@ AbstractRepresentativePeriod{T} <: TimeStructure{T} Abstract type used for time structures that represent a representative period. -These periods is obtained when iterating through the representative periods of a time +These periods are obtained when iterating through the representative periods of a time structure declared using the function [`repr_period`](@ref.) """ abstract type AbstractRepresentativePeriod{T} <: TimeStructure{T} end @@ -34,7 +34,7 @@ RepresentativeIndexable(::Type{<:TimePeriod}) = HasReprIndex() SingleReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} A type representing a single representative period supporting iteration over its -time periods. It is created through iterating through [`SingleReprPeriodWrapper`](@ref). +time periods. It is created when iterating through [`SingleReprPeriodWrapper`](@ref). """ struct SingleReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} @@ -59,7 +59,7 @@ Base.last(rp::SingleReprPeriod) = last(rp.ts) """ SingleReprPeriodWrapper{T,OP<:TimeStructure{T}} <: TimeStructure{T} -Iterator for iterating through the individual presentative periods of a time structure +Iterator for iterating through the individual representative periods of a time structure without [`RepresentativePeriods`](@ref). It is automatically created through the function [`repr_periods`](@ref). """ @@ -70,9 +70,9 @@ end """ repr_periods(ts::TimeStructure) -This function returns an iterator for iterating through the individual representative +This function returns a type for iterating through the individual representative periods of a `TimeStructure`. The type of the iterator is dependent on the type of the -input `TimeStructure`, +input `TimeStructure`. When the `TimeStructure` is a `TimeStructure`, `repr_periods` returns a [`SingleReprPeriodWrapper`](@ref). This coresponds to the default behavior. @@ -156,7 +156,7 @@ end """ ReprPeriods{T,OP} -Iterator for iterating through the individual presentative periods of a +Iterator for iterating through the individual representative periods of a [`RepresentativePeriods`](@ref) time structure. It is automatically created through the function [`repr_periods`](@ref). """ diff --git a/src/representative/strat_periods.jl b/src/representative/strat_periods.jl index ad79531..b5700fa 100644 --- a/src/representative/strat_periods.jl +++ b/src/representative/strat_periods.jl @@ -58,7 +58,7 @@ end """ StratReprPeriods{OP} -Iterator for iterating through the individual presentative periods of a +Iterator for iterating through the individual representative periods of a [`StrategicPeriod`](@ref) time structure. It is automatically created through the function [`repr_periods`](@ref). """ From e7429d4b6ce4ba4b96db237727837ac347a76637 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Mon, 9 Sep 2024 08:27:12 +0200 Subject: [PATCH 04/20] Additional fixed and JuliaFormatter --- src/representative/rep_periods.jl | 6 +++--- src/representative/strat_periods.jl | 4 ++-- test/runtests.jl | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl index 129c43e..65795df 100644 --- a/src/representative/rep_periods.jl +++ b/src/representative/rep_periods.jl @@ -59,7 +59,7 @@ Base.last(rp::SingleReprPeriod) = last(rp.ts) """ SingleReprPeriodWrapper{T,OP<:TimeStructure{T}} <: TimeStructure{T} -Iterator for iterating through the individual representative periods of a time structure +Type for iterating through the individual representative periods of a time structure without [`RepresentativePeriods`](@ref). It is automatically created through the function [`repr_periods`](@ref). """ @@ -104,7 +104,7 @@ end RepresentativePeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} A type representing a single representative period supporting iteration over its -time periods. It is created through iterating through [`ReprPeriods`](@ref). +time periods. It is created when iterating through [`ReprPeriods`](@ref). """ struct RepresentativePeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} @@ -156,7 +156,7 @@ end """ ReprPeriods{T,OP} -Iterator for iterating through the individual representative periods of a +Type for iterating through the individual representative periods of a [`RepresentativePeriods`](@ref) time structure. It is automatically created through the function [`repr_periods`](@ref). """ diff --git a/src/representative/strat_periods.jl b/src/representative/strat_periods.jl index b5700fa..b4156ee 100644 --- a/src/representative/strat_periods.jl +++ b/src/representative/strat_periods.jl @@ -3,7 +3,7 @@ StratReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} A type representing a single representative period supporting iteration over its -time periods. It is created through iterating through [`StratReprPeriods`](@ref). +time periods. It is created when iterating through [`StratReprPeriods`](@ref). """ struct StratReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} @@ -58,7 +58,7 @@ end """ StratReprPeriods{OP} -Iterator for iterating through the individual representative periods of a +Type for iterating through the individual representative periods of a [`StrategicPeriod`](@ref) time structure. It is automatically created through the function [`repr_periods`](@ref). """ diff --git a/test/runtests.jl b/test/runtests.jl index fd140bd..aa0c735 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -204,8 +204,7 @@ end # Test of direct functions of `ReprPeriods` rpers = repr_periods(rep) - @test eltype(rpers) == - TimeStruct.RepresentativePeriod{Int,SimpleTimes{Int}} + @test eltype(rpers) == TimeStruct.RepresentativePeriod{Int,SimpleTimes{Int}} @test last(rpers) == collect(rpers)[end] @test length(rpers) == 2 index = eachindex(rpers) From 7f3c9996fc36be1c26ddf8b1d2d93d4c7c6c6728 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Thu, 12 Sep 2024 07:20:52 +0200 Subject: [PATCH 05/20] Moved strategic files to separate folder --- src/TimeStruct.jl | 4 ++-- src/{twolevel.jl => strategic/core_types.jl} | 0 src/{ => strategic}/strat_periods.jl | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{twolevel.jl => strategic/core_types.jl} (100%) rename src/{ => strategic}/strat_periods.jl (100%) diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 3f72a11..69557f6 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -11,8 +11,8 @@ include("simple.jl") include("calendar.jl") include(joinpath("representative", "core_types.jl")) include("stochastic.jl") -include("twolevel.jl") -include("strat_periods.jl") +include(joinpath("strategic", "core_types.jl")) +include(joinpath("strategic", "strat_periods.jl")) include(joinpath("representative", "rep_periods.jl")) include(joinpath("representative", "strat_periods.jl")) include("opscenarios.jl") diff --git a/src/twolevel.jl b/src/strategic/core_types.jl similarity index 100% rename from src/twolevel.jl rename to src/strategic/core_types.jl diff --git a/src/strat_periods.jl b/src/strategic/strat_periods.jl similarity index 100% rename from src/strat_periods.jl rename to src/strategic/strat_periods.jl From ccffbc7dc6805c5d09bb8727bcc052189419cabe Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Thu, 12 Sep 2024 08:29:31 +0200 Subject: [PATCH 06/20] Restructured strategic periods --- src/representative/rep_periods.jl | 35 ++--- src/strategic/core_types.jl | 68 ++++---- src/strategic/strat_periods.jl | 252 ++++++++++++++++-------------- 3 files changed, 186 insertions(+), 169 deletions(-) diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl index 65795df..7d247c6 100644 --- a/src/representative/rep_periods.jl +++ b/src/representative/rep_periods.jl @@ -45,15 +45,14 @@ _rper(rp::SingleReprPeriod) = 1 mult_repr(rp::SingleReprPeriod) = 1.0 -Base.length(rp::SingleReprPeriod) = length(rp.ts) -Base.eltype(::Type{SingleReprPeriod{T,OP}}) where {T,OP} = eltype(OP) - StrategicIndexable(::Type{<:SingleReprPeriod}) = HasStratIndex() +Base.length(rp::SingleReprPeriod) = length(rp.ts) function Base.iterate(rp::SingleReprPeriod, state = nothing) next = isnothing(state) ? iterate(rp.ts) : iterate(rp.ts, state) return next end +Base.eltype(::Type{SingleReprPeriod{T,OP}}) where {T,OP} = eltype(OP) Base.last(rp::SingleReprPeriod) = last(rp.ts) """ @@ -79,15 +78,16 @@ When the `TimeStructure` is a `TimeStructure`, `repr_periods` returns a """ repr_periods(ts::TimeStructure) = SingleReprPeriodWrapper(ts) +# Add basic functions of iterators Base.length(rpers::SingleReprPeriodWrapper) = 1 -function Base.eltype(::Type{SingleReprPeriodWrapper{T,OP}}) where {T,OP} - return SingleReprPeriod{T,OP} -end function Base.iterate(rpers::SingleReprPeriodWrapper, state = nothing) !isnothing(state) && return nothing return SingleReprPeriod(rpers.ts), 1 end +function Base.eltype(::Type{SingleReprPeriodWrapper{T,OP}}) where {T,OP} + return SingleReprPeriod{T,OP} +end Base.last(rpers::SingleReprPeriodWrapper) = SingleReprPeriod(rpers.ts) """ @@ -118,10 +118,6 @@ _rper(rp::RepresentativePeriod) = rp.rp mult_repr(rp::RepresentativePeriod) = rp.mult_rp Base.show(io::IO, rp::RepresentativePeriod) = print(io, "rp-$(_rper(rp))") -function Base.eltype(_::Type{RepresentativePeriod{T,OP}}) where {T,OP} - return ReprPeriod{eltype(OP)} -end -Base.length(rp::RepresentativePeriod) = length(rp.operational) # Provide a constructor to simplify the design function ReprPeriod( @@ -132,7 +128,8 @@ function ReprPeriod( return ReprPeriod(_rper(rp), per, mult) end -# Iterate the time periods of a RepresentativePeriod +# Add basic functions of iterators +Base.length(rp::RepresentativePeriod) = length(rp.operational) function Base.iterate(rp::RepresentativePeriod, state = nothing) next = isnothing(state) ? iterate(rp.operational) : @@ -141,6 +138,9 @@ function Base.iterate(rp::RepresentativePeriod, state = nothing) return ReprPeriod(rp, next[1]), next[2] end +function Base.eltype(_::Type{RepresentativePeriod{T,OP}}) where {T,OP} + return ReprPeriod{eltype(OP)} +end function Base.getindex(rp::RepresentativePeriod, index::Int) per = rp.operational[index] return ReprPeriod(rp, per) @@ -170,11 +170,6 @@ iterator [`ReprPeriods`](@ref). """ repr_periods(ts::RepresentativePeriods) = ReprPeriods(ts) -function Base.eltype(_::ReprPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} - return RepresentativePeriod{T,OP} -end -Base.length(rpers::ReprPeriods) = rpers.ts.len - # Provide a constructor to simplify the design function RepresentativePeriod(rpers::ReprPeriods, per::Int) return RepresentativePeriod( @@ -184,13 +179,17 @@ function RepresentativePeriod(rpers::ReprPeriods, per::Int) ) end -# Iterate the time periods of a ReprPeriods +# Add basic functions of iterators +Base.length(rpers::ReprPeriods) = rpers.ts.len function Base.iterate(rpers::ReprPeriods, state = nothing) per = isnothing(state) ? 1 : state + 1 - per === length(rpers) + 1 && return nothing + per > length(rpers) && return nothing return RepresentativePeriod(rpers, per), per end +function Base.eltype(_::ReprPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return RepresentativePeriod{T,OP} +end function Base.getindex(rpers::ReprPeriods, index::Int) return RepresentativePeriod(rpers, index) end diff --git a/src/strategic/core_types.jl b/src/strategic/core_types.jl index 645c0cf..d2dabb0 100644 --- a/src/strategic/core_types.jl +++ b/src/strategic/core_types.jl @@ -163,72 +163,64 @@ function TwoLevel(len::Integer, duration::Real, oper::RepresentativePeriods) ) end -_total_duration(itr::TwoLevel) = sum(itr.duration) +_total_duration(ts::TwoLevel) = sum(ts.duration) -function _multiple_adj(itr::TwoLevel, sp) +function _multiple_adj(ts::TwoLevel, sp) mult = - itr.duration[sp] * itr.op_per_strat / - _total_duration(itr.operational[sp]) + ts.duration[sp] * ts.op_per_strat / + _total_duration(ts.operational[sp]) return stripunit(mult) end -function Base.iterate(itr::TwoLevel) - sp = 1 - next = iterate(itr.operational[sp]) - next === nothing && return nothing - per = next[1] - - mult = _multiple_adj(itr, sp) * multiple(per) - return OperationalPeriod(sp, per, mult), (sp, next[2]) +function Base.length(ts::TwoLevel) + return sum(length(op) for op in ts.operational) end - -function Base.iterate(itr::TwoLevel, state) - sp = state[1] - next = iterate(itr.operational[sp], state[2]) +function Base.iterate(ts::TwoLevel, state = (nothing, 1)) + sp = state[2] + next = + isnothing(state[1]) ? iterate(ts.operational[sp]) : + iterate(ts.operational[sp], state[1]) if next === nothing sp = sp + 1 - if sp > itr.len + if sp > ts.len return nothing end - next = iterate(itr.operational[sp]) + next = iterate(ts.operational[sp]) end - per = next[1] - - mult = _multiple_adj(itr, sp) * multiple(per) - return OperationalPeriod(sp, per, mult), (sp, next[2]) + return OperationalPeriod(ts, next[1], sp), (next[2], sp) end - -function Base.length(itr::TwoLevel) - return sum(length(op) for op in itr.operational) +function Base.last(ts::TwoLevel) + per = last(ts.operational[ts.len]) + return OperationalPeriod(ts, per, ts.len) end Base.eltype(::Type{TwoLevel{S,T,OP}}) where {S,T,OP} = OperationalPeriod -function Base.last(itr::TwoLevel) - per = last(itr.operational[itr.len]) - mult = _multiple_adj(itr, itr.len) * multiple(per) - return OperationalPeriod(itr.len, per, mult) -end - """ struct OperationalPeriod <: TimePeriod -Time period for iteration of a TwoLevel time structure. + +Time period for a single operational period. It is created through iterating through a +[`TwoLevel`](@ref) time structure. """ struct OperationalPeriod <: TimePeriod sp::Int period::TimePeriod multiple::Float64 end - -isfirst(t::OperationalPeriod) = isfirst(t.period) -duration(t::OperationalPeriod) = duration(t.period) -probability(t::OperationalPeriod) = probability(t.period) -multiple(t::OperationalPeriod) = t.multiple +function OperationalPeriod(ts::TwoLevel, per::TimePeriod, sp::Int) + mult = _multiple_adj(ts, sp) * multiple(per) + return OperationalPeriod(sp, per, mult) +end _oper(t::OperationalPeriod) = _oper(t.period) -_strat_per(t::OperationalPeriod) = t.sp _opscen(t::OperationalPeriod) = _opscen(t.period) _rper(t::OperationalPeriod) = _rper(t.period) +_strat_per(t::OperationalPeriod) = t.sp + +isfirst(t::OperationalPeriod) = isfirst(t.period) +duration(t::OperationalPeriod) = duration(t.period) +multiple(t::OperationalPeriod) = t.multiple +probability(t::OperationalPeriod) = probability(t.period) function Base.show(io::IO, t::OperationalPeriod) return print(io, "sp$(t.sp)-$(t.period)") diff --git a/src/strategic/strat_periods.jl b/src/strategic/strat_periods.jl index 8aa6b6a..78686bf 100644 --- a/src/strategic/strat_periods.jl +++ b/src/strategic/strat_periods.jl @@ -2,23 +2,20 @@ """ AbstractStrategicPeriod{S,T} <: TimeStructure{T} -Abstract base type for all strategic periods returned -when iterating through the periods using the -`strat_periods` function. +Abstract type used for time structures that represent a strategic period. +These periods are obtained when iterating through the strategic periods of a time +structure declared using the function [`strat_periods`](@ref.) """ abstract type AbstractStrategicPeriod{S,T} <: TimeStructure{T} end -function duration_strat(sp::AbstractStrategicPeriod) - return error("duration_strat() not implemented for $(typeof(sp))") -end - function _strat_per(sp::AbstractStrategicPeriod) return error("_strat_per() not implemented for $(typeof(sp))") end - isfirst(sp::AbstractStrategicPeriod) = _strat_per(sp) == 1 - mult_strat(sp::AbstractStrategicPeriod) = 1 +function duration_strat(sp::AbstractStrategicPeriod) + return error("duration_strat() not implemented for $(typeof(sp))") +end function Base.isless(sp1::AbstractStrategicPeriod, sp2::AbstractStrategicPeriod) return _strat_per(sp1) < _strat_per(sp2) @@ -51,11 +48,78 @@ function remaining(sp::AbstractStrategicPeriod, ts::TimeStructure) ) end + """ - StrategicPeriod <: AbstractStrategicPeriod + SingleStrategicPeriodWrapper{T,SP<:TimeStructure{T}} <: AbstractStrategicPeriod{T,T} -Time period for iteration of strategic periods of a `TwoLevel` -time structure. +A type representing a single strategic period supporting iteration over its +time periods. It is created when iterating through [`SingleStrategicPeriodWrapper`](@ref). +""" +struct SingleStrategicPeriod{T,SP<:TimeStructure{T}} <: + AbstractStrategicPeriod{T,T} + ts::SP +end + +_strat_per(sp::SingleStrategicPeriod) = 1 + +mult_strat(sp::SingleStrategicPeriod) = 1.0 +duration_strat(sp::SingleStrategicPeriod) = _total_duration(sp.ts) + +# Add basic functions of iterators +Base.length(sp::SingleStrategicPeriod) = length(sp.ts) +function Base.iterate(sp::SingleStrategicPeriod, state = nothing) + next = isnothing(state) ? iterate(sp.ts) : iterate(sp.ts, state) + return next +end +Base.eltype(::Type{SingleStrategicPeriod{T,SP}}) where {T,SP} = eltype(SP) +Base.last(sp::SingleStrategicPeriod) = last(sp.ts) + +""" + SingleStrategicPeriodWrapper{T,SP<:TimeStructure{T}} <: TimeStructure{T} + +Type for iterating through the individual strategic periods of a time structure +without [`TwoLevel`](@ref). It is automatically created through the function +[`strat_periods`](@ref). +""" +struct SingleStrategicPeriodWrapper{T,SP<:TimeStructure{T}} <: TimeStructure{T} + ts::SP +end + +""" + strat_periods(ts::TimeStructure) + +This function returns a type for iterating through the individual strategic +periods of a `TimeStructure`. The type of the iterator is dependent on the type of the +input `TimeStructure`. The elements returned of the iterator will be subtypes of +[`AbstractStrategicPeriod`](@ref). + +When the `TimeStructure` is a `TimeStructure`, `strat_periods` returns a +[`SingleStrategicPeriodWrapper`](@ref). This corresponds to the default behavior. +""" +strat_periods(ts::TimeStructure) = SingleStrategicPeriodWrapper(ts) + +""" + strategic_periods(ts) + +Convenience constructor for [`strat_periods`](@ref). Both names can be used interchangable. +""" +strategic_periods(ts) = strat_periods(ts) + +Base.length(sps::SingleStrategicPeriodWrapper) = 1 +function Base.iterate(sps::SingleStrategicPeriodWrapper, state = nothing) + !isnothing(state) && return nothing + return SingleStrategicPeriod(sps.ts), 1 +end +function Base.eltype(::Type{SingleStrategicPeriodWrapper{T,SP}}) where {T,SP} + return SingleStrategicPeriod{T,SP} +end +Base.last(sps::SingleStrategicPeriodWrapper) = SingleStrategicPeriod(sps.ts) + +""" + StrategicPeriod{S,T,OP<:TimeStructure{T}} <: AbstractStrategicPeriod{S,T} + +A type representing a single strategic period supporting iteration over its +time periods. It is created when iterating through [`StratPeriods`](@ref). """ struct StrategicPeriod{S,T,OP<:TimeStructure{T}} <: AbstractStrategicPeriod{S,T} sp::Int @@ -64,11 +128,42 @@ struct StrategicPeriod{S,T,OP<:TimeStructure{T}} <: AbstractStrategicPeriod{S,T} operational::OP end -Base.show(io::IO, sp::StrategicPeriod) = print(io, "sp$(sp.sp)") - -duration_strat(sp::StrategicPeriod) = sp.duration _strat_per(sp::StrategicPeriod) = sp.sp + mult_strat(sp::StrategicPeriod) = sp.mult_sp +duration_strat(sp::StrategicPeriod) = sp.duration + +Base.show(io::IO, sp::StrategicPeriod) = print(io, "sp$(sp.sp)") +# Provide a constructor to simplify the design +function OperationalPeriod(sp::StrategicPeriod, per::TimePeriod) + mult = mult_strat(sp) * multiple(per) + return OperationalPeriod(sp.sp, per, mult) +end + +# Add basic functions of iterators +Base.length(sp::StrategicPeriod) = length(sp.operational) +function Base.iterate(sp::StrategicPeriod, state = nothing) + next = + isnothing(state) ? iterate(sp.operational) : + iterate(sp.operational, state) + next === nothing && return nothing + + return OperationalPeriod(sp, next[1]), next[2] +end +function Base.eltype(_::Type{StrategicPeriod{S,T,OP}}) where {S,T,OP} + return OperationalPeriod +end +function Base.getindex(sp::StrategicPeriod, index::Int) + per = sp.operational[index] + return OperationalPeriod(sp, per) +end +function Base.eachindex(sp::StrategicPeriod) + return eachindex(sp.operational) +end +function Base.last(sp::StrategicPeriod) + per = last(sp.operational) + return OperationalPeriod(sp, per) +end """ multiple_strat(sp::StrategicPeriod, t) @@ -86,6 +181,13 @@ end """ multiple_strat(sp::StrategicPeriod, t) = multiple(t) / duration_strat(sp) +""" + StratPeriods{S,T,OP} + +Type for iterating through the individual strategic periods of a +[`TwoLevel`](@ref) time structure. It is automatically created through the +function [`strat_periods`](@ref). +""" struct StratPeriods{S,T,OP} ts::TwoLevel{S,T,OP} end @@ -95,120 +197,44 @@ function remaining(sp::AbstractStrategicPeriod, sps::StratPeriods) end """ - strat_periods(ts::TimeStructure) - -Iterator to go through the strategic periods of a time structure. - -The elements returned will be subtypes of `AbstractStrategicPeriod`. -If the time structure do not have strategic periods, the overall -time structure will be wrapped as a single strategic period. +When the `TimeStructure` is a [`TwoLevel`](@ref), `strat_periods` returns the +iterator [`StratPeriods`](@ref). ## Example ```julia periods = TwoLevel(5, SimpleTimes(10,1)) - total_dur = sum(duration_strat(sp) for sp in strategic_periods(periods)) ``` """ strat_periods(ts::TwoLevel) = StratPeriods(ts) -Base.length(sps::StratPeriods) = sps.ts.len - -function Base.iterate(sps::StratPeriods) - mult_sp = _multiple_adj(sps.ts, 1) +# Provide a constructor to simplify the design +function StrategicPeriod(sps::StratPeriods, sp::Int) return StrategicPeriod( - 1, - sps.ts.duration[1], - mult_sp, - sps.ts.operational[1], - ), - 1 -end - -function Base.iterate(sps::StratPeriods, state) - state == sps.ts.len && return nothing - mult_sp = _multiple_adj(sps.ts, state + 1) - sp = StrategicPeriod( - state + 1, - sps.ts.duration[state+1], - mult_sp, - sps.ts.operational[state+1], + sp, + sps.ts.duration[sp], + _multiple_adj(sps.ts, sp), + sps.ts.operational[sp], ) - return sp, state + 1 -end - -function Base.last(sps::StratPeriods) - n = sps.ts.len - dur = sps.ts.duration[n] - mult_sp = _multiple_adj(sps.ts, n) - op = sps.ts.operational[n] - return StrategicPeriod(n, dur, mult_sp, op) -end - -Base.length(itr::StrategicPeriod) = length(itr.operational) -function Base.eltype(::Type{StrategicPeriod{S,T,OP}}) where {S,T,OP} - return OperationalPeriod -end - -# Function for defining the time periods when iterating through a strategic period -function Base.iterate(itr::StrategicPeriod, state = nothing) - next = - isnothing(state) ? iterate(itr.operational) : - iterate(itr.operational, state) - next === nothing && return nothing - per = next[1] - - mult = itr.mult_sp * multiple(per) - return OperationalPeriod(itr.sp, per, mult), next[2] end -function Base.last(itr::StrategicPeriod) - per = last(itr.operational) - mult = itr.mult_sp * multiple(per) - return OperationalPeriod(itr.sp, per, mult) -end +# Add basic functions of iterators +Base.length(sps::StratPeriods) = sps.ts.len +function Base.iterate(sps::StratPeriods, state = nothing) + sp = isnothing(state) ? 1 : state + 1 + sp > length(sps) && return nothing -struct SingleStrategicPeriodWrapper{T,SP<:TimeStructure{T}} <: TimeStructure{T} - ts::SP + return StrategicPeriod(sps, sp), sp end - -function Base.iterate(ssp::SingleStrategicPeriodWrapper, state = nothing) - !isnothing(state) && return nothing - return SingleStrategicPeriod(ssp.ts), 1 +function Base.eltype(_::StratPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return StrategicPeriod{S,T,OP} end -Base.length(ssp::SingleStrategicPeriodWrapper) = 1 -function Base.eltype(::Type{SingleStrategicPeriodWrapper{T,SP}}) where {T,SP} - return SingleStrategicPeriod{T,SP} +function Base.getindex(sps::StratPeriods, index::Int) + return StrategicPeriod(sps, index) end -Base.last(ssp::SingleStrategicPeriodWrapper) = SingleStrategicPeriod(ssp.ts) - -struct SingleStrategicPeriod{T,SP<:TimeStructure{T}} <: - AbstractStrategicPeriod{T,T} - ts::SP +function Base.eachindex(sps::StratPeriods) + return eachindex(sps.ts.rep_periods) end -Base.length(ssp::SingleStrategicPeriod) = length(ssp.ts) -Base.eltype(::Type{SingleStrategicPeriod{T,SP}}) where {T,SP} = eltype(SP) - -function Base.iterate(ssp::SingleStrategicPeriod, state = nothing) - if isnothing(state) - return iterate(ssp.ts) - end - return iterate(ssp.ts, state) +function Base.last(sps::StratPeriods) + return StrategicPeriod(sps, length(sps)) end - -Base.last(ssp::SingleStrategicPeriod) = last(ssp.ts) - -duration_strat(ssp::SingleStrategicPeriod) = _total_duration(ssp.ts) -_strat_per(ssp::SingleStrategicPeriod) = 1 - -# Default solution is to behave as a single strategic period -strat_periods(ts::TimeStructure) = SingleStrategicPeriodWrapper(ts) - -# Allow strategic_periods() in addition to strat_periods() - -""" - strategic_periods(ts) - -Internal convenience constructor for [`strat_periods`](@ref). -""" -strategic_periods(ts) = strat_periods(ts) From 765a09384f86f6a0ee13e3b8835c10a5fe5d7900 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Thu, 12 Sep 2024 08:44:13 +0200 Subject: [PATCH 07/20] Restructured simple periods --- src/representative/core_types.jl | 13 ++++---- src/simple.jl | 55 +++++++++++++++----------------- src/strategic/core_types.jl | 3 +- src/structures.jl | 8 ++++- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/representative/core_types.jl b/src/representative/core_types.jl index 8c4a199..6a52bf7 100644 --- a/src/representative/core_types.jl +++ b/src/representative/core_types.jl @@ -135,6 +135,10 @@ function _multiple_adj(rpers::RepresentativePeriods, rp) return stripunit(mult) end +# Add basic functions of iterators +function Base.length(rpers::RepresentativePeriods) + return sum(length(rp) for rp in rpers.rep_periods) +end function Base.iterate(rpers::RepresentativePeriods, state = (nothing, 1)) rp = state[2] next = @@ -149,19 +153,14 @@ function Base.iterate(rpers::RepresentativePeriods, state = (nothing, 1)) end return ReprPeriod(rpers, next[1], rp), (next[2], rp) end - -function Base.length(rpers::RepresentativePeriods) - return sum(length(rp) for rp in rpers.rep_periods) +function Base.eltype(::Type{RepresentativePeriods{S,T,OP}}) where {S,T,OP} + return ReprPeriod{eltype(OP)} end - function Base.last(rpers::RepresentativePeriods) per = last(rpers.rep_periods[rpers.len]) return ReprPeriod(rpers, per, rpers.len) end -function Base.eltype(::Type{RepresentativePeriods{S,T,OP}}) where {S,T,OP} - return ReprPeriod{eltype(OP)} -end """ ReprPeriod{P} <: TimePeriod where {P<:TimePeriod} diff --git a/src/simple.jl b/src/simple.jl index a051d44..ea8ded9 100644 --- a/src/simple.jl +++ b/src/simple.jl @@ -36,45 +36,42 @@ function SimpleTimes(len::Integer, duration::Number) end SimpleTimes(dur::Vector{T}) where {T<:Number} = SimpleTimes(length(dur), dur) -Base.eltype(::Type{SimpleTimes{T}}) where {T} = SimplePeriod{T} +_total_duration(st::SimpleTimes) = sum(st.duration) + +# Add basic functions of iterators Base.length(st::SimpleTimes) = st.len +function Base.iterate(itr::SimpleTimes{T}, state = nothing) where {T} + next = isnothing(state) ? 1 : state + 1 + next > itr.len && return nothing -_total_duration(st::SimpleTimes) = sum(st.duration) + return SimplePeriod{T}(next, itr.duration[next]), next +end +function Base.getindex(itr::SimpleTimes{T}, index) where {T} + return SimplePeriod{T}(index, itr.duration[index]) +end +function Base.eachindex(itr::SimpleTimes{T}) where {T} + return Base.OneTo(itr.len) +end +function Base.last(ts::SimpleTimes{T}) where {T} + return SimplePeriod{T}(ts.len, ts.duration[ts.len]) +end +Base.eltype(::Type{SimpleTimes{T}}) where {T} = SimplePeriod{T} """ - struct SimplePeriod <: TimePeriod -A single time period returned when iterating through a SimpleTimes structure + struct SimplePeriod{T<:Number} <: TimePeriod + +Time period for a single operational period. It is created through iterating through a +[`SimpleTimes`](@ref) time structure. """ struct SimplePeriod{T<:Number} <: TimePeriod op::Int duration::T end -duration(t::SimplePeriod) = t.duration -multiple(t::SimplePeriod) = 1 -isfirst(t::SimplePeriod) = t.op == 1 _oper(t::SimplePeriod) = t.op -Base.isless(t1::SimplePeriod, t2::SimplePeriod) = t1.op < t2.op -Base.show(io::IO, t::SimplePeriod) = print(io, "t$(t.op)") - -function Base.iterate(itr::SimpleTimes{T}) where {T} - return SimplePeriod{T}(1, itr.duration[1]), 1 -end - -function Base.iterate(itr::SimpleTimes{T}, state) where {T} - state == itr.len && return nothing - return SimplePeriod{T}(state + 1, itr.duration[state+1]), state + 1 -end - -function Base.last(ts::SimpleTimes) - return SimplePeriod(ts.len, ts.duration[ts.len]) -end - -function Base.getindex(itr::SimpleTimes{T}, index) where {T} - return SimplePeriod{T}(index, itr.duration[index]) -end +isfirst(t::SimplePeriod) = t.op == 1 +duration(t::SimplePeriod) = t.duration -function Base.eachindex(itr::SimpleTimes{T}) where {T} - return Base.OneTo(itr.len) -end +Base.show(io::IO, t::SimplePeriod) = print(io, "t$(t.op)") +Base.isless(t1::SimplePeriod, t2::SimplePeriod) = t1.op < t2.op diff --git a/src/strategic/core_types.jl b/src/strategic/core_types.jl index d2dabb0..2e63ad7 100644 --- a/src/strategic/core_types.jl +++ b/src/strategic/core_types.jl @@ -172,6 +172,7 @@ function _multiple_adj(ts::TwoLevel, sp) return stripunit(mult) end +# Add basic functions of iterators function Base.length(ts::TwoLevel) return sum(length(op) for op in ts.operational) end @@ -189,12 +190,12 @@ function Base.iterate(ts::TwoLevel, state = (nothing, 1)) end return OperationalPeriod(ts, next[1], sp), (next[2], sp) end +Base.eltype(::Type{TwoLevel{S,T,OP}}) where {S,T,OP} = OperationalPeriod function Base.last(ts::TwoLevel) per = last(ts.operational[ts.len]) return OperationalPeriod(ts, per, ts.len) end -Base.eltype(::Type{TwoLevel{S,T,OP}}) where {S,T,OP} = OperationalPeriod """ struct OperationalPeriod <: TimePeriod diff --git a/src/structures.jl b/src/structures.jl index 9bf4f7b..a8166e5 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -3,6 +3,7 @@ Duration = Number """ abstract type TimeStructure{T} + Abstract type representing different time structures that consists of one or more time periods. The type 'T' gives the data type used for the duration of the time periods. @@ -11,6 +12,7 @@ abstract type TimeStructure{T<:Duration} end """ abstract type TimePeriod + Abstract type used for a uniform interface for iterating through time structures and indexing of time profiles. """ @@ -18,18 +20,21 @@ abstract type TimePeriod end """ duration(t::TimePeriod) + The duration of a time period in number of operational time units. """ duration(t::TimePeriod) = error("duration() not implemented for $(typeof(t))") """ isfirst(t::TimePeriod) + Returns true if the time period is the first in a sequence and has no previous time period """ isfirst(t::TimePeriod) = error("isfirst() not implemented for$(typeof(t))") """ multiple(t::TimePeriod) + Returns the number of times a time period should be counted for the whole time structure. """ @@ -42,10 +47,11 @@ Returns the probability associated with the time period. probability(t::TimePeriod) = 1.0 # Functions used for indexing into time profiles +# TODO: Consider either setting all as default to one, including _oper, or none _oper(t::TimePeriod) = error("_oper() not implemented for $(typeof(t))") -_strat_per(t::TimePeriod) = 1 _opscen(t::TimePeriod) = 1 _rper(t::TimePeriod) = 1 +_strat_per(t::TimePeriod) = 1 _branch(t::TimePeriod) = 1 _total_duration(tss::Vector) = sum(duration(ts) for ts in tss) From 77664f3c0f16098f31c98f3a680dd1f05dea5882 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Thu, 12 Sep 2024 08:57:46 +0200 Subject: [PATCH 08/20] Restructured calendar periods --- src/calendar.jl | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/calendar.jl b/src/calendar.jl index fd19a0b..f9e662a 100644 --- a/src/calendar.jl +++ b/src/calendar.jl @@ -66,45 +66,28 @@ function CalendarTimes( return CalendarTimes(first, length, period) end -Base.eltype(::Type{CalendarTimes{T}}) where {T} = CalendarPeriod{T} -Base.length(ts::CalendarTimes) = ts.length - _total_duration(ts::CalendarTimes) = sum(duration(t) for t in ts) -""" - struct CalendarPeriod <: TimePeriod -A single time period returned when iterating through a CalendarTimes structure -with duration measured in hours (by default). -""" -struct CalendarPeriod{T} <: TimePeriod - start_dt::T - stop_dt::T - op::Int -end - -function duration(t::CalendarPeriod; dfunc = Dates.Hour) - return Dates.value(dfunc(t.stop_dt - t.start_dt)) -end -isfirst(t::CalendarPeriod) = t.op == 1 -multiple(t::CalendarPeriod) = 1 -_oper(t::CalendarPeriod) = t.op -start_date(t::CalendarPeriod) = t.start_dt - -Base.isless(t1::CalendarPeriod, t2::CalendarPeriod) = t1.op < t2.op -Base.show(io::IO, t::CalendarPeriod) = print(io, "ct$(t.op)") - +# Add basic functions of iterators +Base.length(ts::CalendarTimes) = ts.length function Base.iterate(ts::CalendarTimes) return CalendarPeriod(ts.start_date, ts.start_date + ts.period, 1), (1, ts.start_date) end - function Base.iterate(ts::CalendarTimes, state) state[1] == ts.length && return nothing start_time = state[2] + ts.period return CalendarPeriod(start_time, start_time + ts.period, state[1] + 1), (state[1] + 1, start_time) end - +Base.eltype(::Type{CalendarTimes{T}}) where {T} = CalendarPeriod{T} +function Base.getindex(ts::CalendarTimes, index) + start_time = ts.start_date + (index - 1) * ts.period + return CalendarPeriod(start_time, start_time + ts.period, index) +end +function Base.eachindex(ts::CalendarTimes) + return Base.OneTo(ts.length) +end function Base.last(ts::CalendarTimes) n = ts.length start = ts.start_date + (n - 1) * ts.period @@ -112,11 +95,26 @@ function Base.last(ts::CalendarTimes) return CalendarPeriod(start, stop, n) end -function Base.getindex(ts::CalendarTimes, index) - start_time = ts.start_date + (index - 1) * ts.period - return CalendarPeriod(start_time, start_time + ts.period, index) +""" + struct CalendarPeriod <: TimePeriod + +Time period for a single operational period. It is created through iterating through a +[`CalendarTimes`](@ref) time structure with duration measured in hours (by default). +""" +struct CalendarPeriod{T} <: TimePeriod + start_dt::T + stop_dt::T + op::Int end -function Base.eachindex(ts::CalendarTimes) - return Base.OneTo(ts.length) +_oper(t::CalendarPeriod) = t.op + +isfirst(t::CalendarPeriod) = t.op == 1 +function duration(t::CalendarPeriod; dfunc = Dates.Hour) + return Dates.value(dfunc(t.stop_dt - t.start_dt)) end +multiple(t::CalendarPeriod) = 1 +start_date(t::CalendarPeriod) = t.start_dt + +Base.show(io::IO, t::CalendarPeriod) = print(io, "ct$(t.op)") +Base.isless(t1::CalendarPeriod, t2::CalendarPeriod) = t1.op < t2.op From 98535cc63a1615a099927b89b63f6993e37aafa7 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Thu, 12 Sep 2024 09:01:06 +0200 Subject: [PATCH 09/20] Moved op scenarios files to separate folder --- src/TimeStruct.jl | 4 ++-- src/{stochastic.jl => op_scenarios/core_types.jl} | 0 src/{ => op_scenarios}/opscenarios.jl | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{stochastic.jl => op_scenarios/core_types.jl} (100%) rename src/{ => op_scenarios}/opscenarios.jl (100%) diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 69557f6..19dd8a7 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -10,12 +10,12 @@ include("structures.jl") include("simple.jl") include("calendar.jl") include(joinpath("representative", "core_types.jl")) -include("stochastic.jl") +include(joinpath("op_scenarios", "core_types.jl")) include(joinpath("strategic", "core_types.jl")) include(joinpath("strategic", "strat_periods.jl")) include(joinpath("representative", "rep_periods.jl")) include(joinpath("representative", "strat_periods.jl")) -include("opscenarios.jl") +include(joinpath("op_scenarios", "opscenarios.jl")) include("utils.jl") include("discount.jl") diff --git a/src/stochastic.jl b/src/op_scenarios/core_types.jl similarity index 100% rename from src/stochastic.jl rename to src/op_scenarios/core_types.jl diff --git a/src/opscenarios.jl b/src/op_scenarios/opscenarios.jl similarity index 100% rename from src/opscenarios.jl rename to src/op_scenarios/opscenarios.jl From 44e907bc41ec21ee9057f6d1a9f8c49b25efa8e0 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 08:22:00 +0200 Subject: [PATCH 10/20] Restructured op op_scenarios --- src/TimeStruct.jl | 2 + src/op_scenarios/core_types.jl | 100 +++--- src/op_scenarios/opscenarios.jl | 576 +++++++----------------------- src/op_scenarios/rep_periods.jl | 125 +++++++ src/op_scenarios/strat_periods.jl | 285 +++++++++++++++ 5 files changed, 594 insertions(+), 494 deletions(-) create mode 100644 src/op_scenarios/rep_periods.jl create mode 100644 src/op_scenarios/strat_periods.jl diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 19dd8a7..5b611e1 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -16,6 +16,8 @@ include(joinpath("strategic", "strat_periods.jl")) include(joinpath("representative", "rep_periods.jl")) include(joinpath("representative", "strat_periods.jl")) include(joinpath("op_scenarios", "opscenarios.jl")) +include(joinpath("op_scenarios", "rep_periods.jl")) +include(joinpath("op_scenarios", "strat_periods.jl")) include("utils.jl") include("discount.jl") diff --git a/src/op_scenarios/core_types.jl b/src/op_scenarios/core_types.jl index 0f79cbc..97f974a 100644 --- a/src/op_scenarios/core_types.jl +++ b/src/op_scenarios/core_types.jl @@ -76,80 +76,82 @@ function OperationalScenarios(oper::Vector{<:TimeStructure{T}}) where {T} ) end -function _total_duration(os::OperationalScenarios) - return maximum(_total_duration(sc) for sc in os.scenarios) +function _total_duration(oscs::OperationalScenarios) + return maximum(_total_duration(osc) for osc in oscs.scenarios) end -function _multiple_adj(os::OperationalScenarios, scen) - return stripunit(_total_duration(os) / _total_duration(os.scenarios[scen])) +function _multiple_adj(oscs::OperationalScenarios, scen) + return stripunit(_total_duration(oscs) / _total_duration(oscs.scenarios[scen])) end # Iteration through all time periods for the operational scenarios -function Base.iterate(itr::OperationalScenarios) - sc = 1 - next = iterate(itr.scenarios[sc]) - next === nothing && return nothing - mult = _multiple_adj(itr, sc) - return ScenarioPeriod(sc, itr.probability[sc], mult, next[1]), (sc, next[2]) +function Base.length(oscs::OperationalScenarios) + return sum(length(oscs.scenarios[osc]) for osc in 1:oscs.len) end - -function Base.iterate(itr::OperationalScenarios, state) - sc = state[1] - next = iterate(itr.scenarios[sc], state[2]) +function Base.eltype(::Type{OperationalScenarios{T,OP}}) where {T,OP} + return ScenarioPeriod{eltype(OP)} +end +function Base.iterate(oscs::OperationalScenarios, state = (nothing, 1)) + osc = state[2] + next = + isnothing(state[1]) ? iterate(oscs.scenarios[osc]) : + iterate(oscs.scenarios[osc], state[1]) if next === nothing - sc = sc + 1 - if sc > itr.len + osc = osc + 1 + if osc > oscs.len return nothing end - next = iterate(itr.scenarios[sc]) + next = iterate(oscs.scenarios[osc]) end - mult = _multiple_adj(itr, sc) - return ScenarioPeriod(sc, itr.probability[sc], mult, next[1]), (sc, next[2]) + return ScenarioPeriod(oscs, next[1], osc), (next[2], osc) end - -function Base.length(itr::OperationalScenarios) - return sum(length(itr.scenarios[sc]) for sc in 1:itr.len) -end - -function Base.eltype(::Type{OperationalScenarios{T,OP}}) where {T,OP} - return ScenarioPeriod{eltype(OP)} -end - -function Base.last(itr::OperationalScenarios) +function Base.last(oscs::OperationalScenarios) return ScenarioPeriod( - itr.len, - itr.probability[itr.len], - _multiple_adj(itr, itr.len), - last(itr.scenarios[itr.len]), + oscs.len, + oscs.probability[oscs.len], + _multiple_adj(oscs, oscs.len), + last(oscs.scenarios[oscs.len]), ) end -# A time period with scenario number and probability +""" + ScenarioPeriod{P} <: TimePeriod where {P<:TimePeriod} + +Time period for a single operational period. It is created through iterating through a +[`OperationalScenarios`](@ref) time structure. It is as well created as period within +[`OperationalPeriod`](@ref) when the time structure includes [`OperationalScenarios`](@ref). +""" struct ScenarioPeriod{P} <: TimePeriod where {P<:TimePeriod} - sc::Int + osc::Int prob::Float64 multiple::Float64 period::P end -function ScenarioPeriod(scenario::Int, prob::Number, multiple::Number, period) +_oper(t::ScenarioPeriod) = _oper(t.period) +_opscen(t::ScenarioPeriod) = t.osc + +isfirst(t::ScenarioPeriod) = isfirst(t.period) +duration(t::ScenarioPeriod) = duration(t.period) +multiple(t::ScenarioPeriod) = t.multiple +probability(t::ScenarioPeriod) = t.prob + +Base.show(io::IO, t::ScenarioPeriod) = print(io, "sc$(t.osc)-$(t.period)") +function Base.isless(t1::ScenarioPeriod, t2::ScenarioPeriod) + return t1.osc < t2.osc || (t1.osc == t2.osc && t1.period < t2.period) +end + +# Convenience constructors for the type +function ScenarioPeriod(osc::Int, prob::Number, multiple::Number, period) return ScenarioPeriod( - scenario, + osc, Base.convert(Float64, prob), Base.convert(Float64, multiple), period, ) end - -Base.show(io::IO, up::ScenarioPeriod) = print(io, "sc$(up.sc)-$(up.period)") -function Base.isless(t1::ScenarioPeriod, t2::ScenarioPeriod) - return t1.sc < t2.sc || (t1.sc == t2.sc && t1.period < t2.period) +function ScenarioPeriod(oscs::OperationalScenarios, per::TimePeriod, osc::Int) + prob = oscs.probability[osc] + mult = _multiple_adj(oscs, osc) + return ScenarioPeriod(osc, prob, mult, per) end - -isfirst(t::ScenarioPeriod) = isfirst(t.period) -duration(t::ScenarioPeriod) = duration(t.period) -probability(t::ScenarioPeriod) = t.prob -multiple(t::ScenarioPeriod) = t.multiple - -_oper(t::ScenarioPeriod) = _oper(t.period) -_opscen(t::ScenarioPeriod) = t.sc diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 484639d..2f1e649 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -1,12 +1,19 @@ -abstract type AbstractOperationalScenario{T} <: TimeStructure{T} end -function probability(scen::AbstractOperationalScenario) - return error("probabilty not implemented for type $(typeof(scen))") -end +""" + AbstractOperationalScenario{T} <: TimeStructure{T} + +Abstract type used for time structures that represent an operational scenario. +These periods are obtained when iterating through the operational scenarios of a time +structure declared by the function [`opscenarios`](@ref.) +""" +abstract type AbstractOperationalScenario{T} <: TimeStructure{T} end function _opscen(scen::AbstractOperationalScenario) return error("_opscen() not implemented for type $(typeof(scen))") end +function probability(scen::AbstractOperationalScenario) + return error("probabilty not implemented for type $(typeof(scen))") +end mult_scen(scen::AbstractOperationalScenario) = 1.0 @@ -20,500 +27,179 @@ ScenarioIndexable(::Type{<:AbstractOperationalScenario}) = HasScenarioIndex() ScenarioIndexable(::Type{<:TimePeriod}) = HasScenarioIndex() """ - struct OperationalScenario -A structure representing a single operational scenario supporting -iteration over its time periods. -""" -struct OperationalScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} - scen::Int - probability::Float64 - mult_sc::Float64 - operational::OP -end -Base.show(io::IO, os::OperationalScenario) = print(io, "sc-$(os.scen)") -probability(os::OperationalScenario) = os.probability -mult_scen(os::OperationalScenario) = os.mult_sc -_opscen(os::OperationalScenario) = os.scen + SingleScenario{T,SC<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} -# Iterate the time periods of an operational scenario -function Base.iterate(os::OperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(os.operational) : - iterate(os.operational, state) - next === nothing && return nothing - return ScenarioPeriod( - os.scen, - os.probability, - os.mult_sc * multiple(next[1]), - next[1], - ), - next[2] +A type representing a single operational scenario supporting iteration over its +time periods. It is created when iterating through [`SingleScenarioWrapper`](@ref). +""" +struct SingleScenario{T,SC<:TimeStructure{T}} <: AbstractOperationalScenario{T} + ts::SC end -Base.length(os::OperationalScenario) = length(os.operational) -Base.eltype(::Type{OperationalScenario}) = ScenarioPeriod +_opscen(osc::SingleScenario) = 1 +_repr_per(osc::SingleScenario) = 1 +_strat_per(osc::SingleScenario) = 1 -function Base.last(sc::OperationalScenario) - return ScenarioPeriod( - sc.scen, - sc.probability, - sc.mult_sc, - last(sc.operational), - ) -end +probability(osc::SingleScenario) = 1.0 -function Base.getindex(os::OperationalScenario, index) - return ScenarioPeriod( - os.scen, - os.probability, - os.multiple, - os.operational[index], - ) -end - -function Base.eachindex(os::OperationalScenario) - return eachindex(os.operational) -end +StrategicIndexable(::Type{<:SingleScenario}) = HasStratIndex() +RepresentativeIndexable(::Type{<:SingleScenario}) = HasRepresentativeIndex() -# Iteration through scenarios -struct OpScens{T} - ts::OperationalScenarios{T} +# Add basic functions of iterators +Base.length(osc::SingleScenario) = length(osc.ts) +Base.eltype(::Type{SingleScenario{T,SC}}) where {T,SC} = eltype(SC) +function Base.iterate(osc::SingleScenario, state = nothing) + if isnothing(state) + return iterate(osc.ts) + end + return iterate(osc.ts, state) end - -#duration(os::OpScens) = duration(os.ts) - -""" - opscenarios(ts) -Iterator that iterates over operational scenarios in an `OperationalScenarios` time structure. -""" -opscenarios(ts::OperationalScenarios) = OpScens(ts) - -Base.length(ops::OpScens) = ops.ts.len - -function Base.iterate(ops::OpScens) - mult_sc = _multiple_adj(ops.ts, 1) - return OperationalScenario( - 1, - ops.ts.probability[1], - mult_sc, - ops.ts.scenarios[1], - ), - 1 +function Base.getindex(osc::SingleScenario, index) + return osc.ts[index] end - -function Base.iterate(ops::OpScens, state) - state == ops.ts.len && return nothing - mult_sc = _multiple_adj(ops.ts, state + 1) - return OperationalScenario( - state + 1, - ops.ts.probability[state+1], - mult_sc, - ops.ts.scenarios[state+1], - ), - state + 1 +function Base.eachindex(osc::SingleScenario) + return eachindex(osc.ts) end - -struct ReprOperationalScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} - rper::Int - scen::Int - probability::Float64 - multiple_repr::Float64 - multiple_scen::Float64 - operational::OP +Base.last(osc::SingleScenario) = last(osc.ts) +function Base.last( # TODO: Considering removing the function as the the structure is opposite + osc::SingleScenario{T,RepresentativePeriod{T,OP}}, +) where {T,OP} + period = last(osc.ts.operational) + return ReprPeriod(_rper(osc.ts), period, mult_repr(osc.ts) * multiple(period)) end -probability(ros::ReprOperationalScenario) = ros.probability -mult_scen(ros::ReprOperationalScenario) = ros.multiple_scen -_opscen(ros::ReprOperationalScenario) = ros.scen - -_rper(ros::ReprOperationalScenario) = ros.rper -RepresentativeIndexable(::Type{<:ReprOperationalScenario}) = HasReprIndex() +""" + SingleScenarioWrapper{T,OP<:TimeStructure{T}} <: TimeStructure{T} -function Base.show(io::IO, ros::ReprOperationalScenario) - return print(io, "rp$(ros.rper)-sc$(ros.scen)") +Type for iterating through the individual operational scenarios of a time structure +without [`OperationalScenarios`](@ref). It is automatically created through the function +[`opscenarios`](@ref). +""" +struct SingleScenarioWrapper{T,SC<:TimeStructure{T}} <: TimeStructure{T} + ts::SC end -# Iterate the time periods of an operational scenario -function Base.iterate(ros::ReprOperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(ros.operational) : - iterate(ros.operational, state) - next === nothing && return nothing - period = next[1] - return ReprPeriod( - ros.rper, - ScenarioPeriod( - ros.scen, - ros.probability, - ros.multiple_scen * multiple(period), - period, - ), - ros.multiple_repr * ros.multiple_scen * multiple(period), - ), - next[2] -end -Base.length(ros::ReprOperationalScenario) = length(ros.operational) -Base.eltype(ros::ReprOperationalScenario) = ReprPeriod -function Base.last(ros::ReprOperationalScenario) - period = last(ros.operational) - return ReprPeriod( - ros.rper, - ScenarioPeriod( - ros.scen, - ros.probability, - ros.multiple_scen * multiple(period), - period, - ), - ros.multiple_repr * ros.multiple_scen * multiple(period), - ) -end +""" + opscenarios(ts::TimeStructure) -# Iteration through scenarios of a representative period -struct RepOpScens{T} - rper::Int - mult::Float64 - op_scens::OperationalScenarios{T} -end +This function returns a type for iterating through the individual operational scenarios +of a `TimeStructure`. The type of the iterator is dependent on the type of the input +`TimeStructure`. +When the `TimeStructure` is a `TimeStructure`, `opscenarios` returns a +[`SingleScenarioWrapper`](@ref). This corresponds to the default behavior. """ - opscenarios(rep::RepresentativePeriod) - -Iterator that iterates over operational scenarios in a `RepresentativePeriod` time structure. +opscenarios(ts::TimeStructure) = SingleScenarioWrapper(ts) """ -function opscenarios( - rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}, -) where {T,OP} - return RepOpScens(_rper(rep), mult_repr(rep), rep.operational) -end - -Base.length(ros::RepOpScens) = length(ros.op_scens) - -function Base.iterate(ros::RepOpScens) - mult_scen = _multiple_adj(ros.op_scens, 1) - return ReprOperationalScenario( - ros.rper, - 1, - ros.op_scens.probability[1], - ros.mult, - mult_scen, - ros.op_scens.scenarios[1], - ), - 1 -end +When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `opscenarios` returns the +correct behavior based on the substructure. +""" +opscenarios(ts::SingleStrategicPeriod) = opscenarios(ts.ts) -function Base.iterate(ros::RepOpScens, state) - state == length(ros.op_scens.scenarios) && return nothing - mult_scen = _multiple_adj(ros.op_scens, state + 1) - return ReprOperationalScenario( - ros.rper, - state + 1, - ros.op_scens.probability[state+1], - ros.mult, - mult_scen, - ros.op_scens.scenarios[state+1], - ), - state + 1 +# Add basic functions of iterators +Base.length(ssw::SingleScenarioWrapper) = 1 +function Base.eltype(::Type{SingleScenarioWrapper{T,SC}}) where {T,SC} + return SingleScenario{T,SC} end - -function opscenarios(ts::RepresentativePeriods) - opscens = [] - for rp in repr_periods(ts) - push!(opscens, opscenarios(rp)...) - end - return opscens +function Base.iterate(ssw::SingleScenarioWrapper, state = nothing) + !isnothing(state) && return nothing + return SingleScenario(ssw.ts), 1 end """ - struct StratOperationalScenario + OperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} -A structure representing a single operational scenario for a strategic period supporting -iteration over its time periods. +A type representing a single operational scenario supporting iteration over its +time periods. It is created when iterating through [`OpScens`](@ref). """ -struct StratOperationalScenario{T} <: AbstractOperationalScenario{T} - sp::Int +struct OperationalScenario{T,OP<:TimeStructure{T}} <: + AbstractOperationalScenario{T} scen::Int - mult_sp::Float64 - mult_scen::Float64 probability::Float64 - operational::TimeStructure{T} + mult_sc::Float64 + operational::OP end -function Base.show(io::IO, os::StratOperationalScenario) - return print(io, "sp$(os.sp)-sc$(os.scen)") -end -probability(os::StratOperationalScenario) = os.probability -mult_scen(os::StratOperationalScenario) = os.mult_scen -_opscen(os::StratOperationalScenario) = os.scen -_strat_per(os::StratOperationalScenario) = os.sp +_opscen(osc::OperationalScenario) = osc.scen -StrategicIndexable(::Type{<:StratOperationalScenario}) = HasStratIndex() -ScenarioIndexable(::Type{<:StratOperationalScenario}) = HasScenarioIndex() +probability(osc::OperationalScenario) = osc.probability +mult_scen(osc::OperationalScenario) = osc.mult_sc -# Iterate the time periods of a StratOperationalScenario -function Base.iterate(os::StratOperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(os.operational) : - iterate(os.operational, state) - isnothing(next) && return nothing +Base.show(io::IO, osc::OperationalScenario) = print(io, "sc-$(osc.scen)") - return OperationalPeriod(os.sp, next[1], os.mult_sp * multiple(next[1])), - next[2] +# Provide a constructor to simplify the design +function ScenarioPeriod( + osc::OperationalScenario, + per::P, +) where {P<:Union{TimePeriod,TimeStructure}} + mult = mult_scen(osc) * multiple(per) + return ScenarioPeriod(_opscen(osc), probability(osc), mult, per) end -Base.length(os::StratOperationalScenario) = length(os.operational) - -function Base.eltype(::Type{StratOperationalScenario{T}}) where {T} - return OperationalPeriod +# Add basic functions of iterators +Base.length(osc::OperationalScenario) = length(osc.operational) +Base.eltype(::Type{OperationalScenario}) = ScenarioPeriod +function Base.iterate(osc::OperationalScenario, state = nothing) + next = + isnothing(state) ? iterate(osc.operational) : + iterate(osc.operational, state) + next === nothing && return nothing + return ScenarioPeriod(osc, next[1]), next[2] end - -function Base.last(os::StratOperationalScenario) - per = last(os.operational) - return OperationalPeriod(os.sp, per, os.mult_sp * multiple(per)) +function Base.last(osc::OperationalScenario) + return ScenarioPeriod(osc, last(osc.operational)) end - -function Base.getindex(os::StratOperationalScenario, index) - per = os.operational[index] - return OperationalPeriod(os.sp, per, os.mult_sp * multiple(per)) +function Base.getindex(osc::OperationalScenario, index) + per = osc.operational[index] + return ScenarioPeriod(osc, per) end - -function Base.eachindex(os::StratOperationalScenario) - return eachindex(os.operational) +function Base.eachindex(osc::OperationalScenario) + return eachindex(osc.operational) end -# Iteration through scenarios -struct StratOpScens - sp::Int - mult_sp::Float64 - opscens::Any -end - -function StratOpScens(sper::StrategicPeriod, opscens) - return StratOpScens(sper.sp, sper.mult_sp, opscens) -end """ - opscenarios(sp::StrategicPeriod) + OpScens{T,OP} - Iterator that iterates over operational scenarios for a specific strategic period. +Type for iterating through the individual operational scenarios of a +[`OperationalScenarios`](@ref) time structure. It is automatically created through the +function [`opscenarios`](@ref). """ -function opscenarios(sper::StrategicPeriod{S,T,OP}) where {S,T,OP} - return StratOpScens(sper, opscenarios(sper.operational)) -end - -function opscenarios( - sp::StrategicPeriod{S1,T,RepresentativePeriods{S2,T,OP}}, -) where {S1,S2,T,OP} - opscens = StratReprOpscenario[] - for rp in repr_periods(sp) - push!(opscens, opscenarios(rp)...) - end - return opscens +struct OpScens{T,OP} + ts::OperationalScenarios{T,OP} end """ - opscenarios(ts::TwoLevel) - - Returns a collection of all operational scenarios for a TwoLevel time structure. +When the `TimeStructure` is an [`OperationalScenarios`](@ref), `opscenarios` returns the +iterator [`OpScens`](@ref). """ -function opscenarios(ts::TwoLevel{S,T,OP}) where {S,T,OP} - opscens = StratOperationalScenario[] - for sp in strategic_periods(ts) - push!(opscens, opscenarios(sp)...) - end - return opscens -end - -function opscenarios( - ts::TwoLevel{S1,T,RepresentativePeriods{S2,T,OP}}, -) where {S1,S2,T,OP} - opscens = StratReprOpscenario[] - for sp in strategic_periods(ts) - for rp in repr_periods(sp) - push!(opscens, opscenarios(rp)...) - end - end - return opscens -end - -Base.length(ops::StratOpScens) = length(ops.opscens) - -function Base.iterate(ops::StratOpScens, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(ops.opscens) : - iterate(ops.opscens, state[1]) - isnothing(next) && return nothing - - scen = state[2] - return StratOperationalScenario( - ops.sp, - scen, - ops.mult_sp, - mult_scen(next[1]), - probability(next[1]), - next[1], - ), - (next[2], scen + 1) -end - -struct StratReprOpscenario{T} <: AbstractOperationalScenario{T} - sp::Int - rp::Int - opscen::Int - mult_sp::Float64 - mult_rp::Float64 - probability::Float64 - operational::TimeStructure{T} -end - -probability(sro::StratReprOpscenario) = sro.probability -_opscen(sro::StratReprOpscenario) = sro.opscen -_strat_per(sro::StratReprOpscenario) = sro.sp -_rper(sro::StratReprOpscenario) = sro.rp - -mult_repr(sro::StratReprOpscenario) = sro.mult_rp - -function Base.show(io::IO, srop::StratReprOpscenario) - return print(io, "sp$(srop.sp)-rp$(_rper(srop))-sc$(srop.opscen)") -end - -StrategicIndexable(::Type{<:StratReprOpscenario}) = HasStratIndex() -function RepresentativeIndexable(::Type{<:StratReprOpscenario}) - return HasReprIndex() -end -ScenarioIndexable(::Type{<:StratReprOpscenario}) = HasScenarioIndex() - -# Iterate the time periods of a StratReprOpscenario -function Base.iterate(os::StratReprOpscenario, state = nothing) - next = - isnothing(state) ? iterate(os.operational) : - iterate(os.operational, state) - isnothing(next) && return nothing - - period = ReprPeriod(_rper(os), next[1], mult_repr(os) * multiple(next[1])) - return OperationalPeriod( - os.sp, - period, - os.mult_sp * mult_repr(os) * multiple(next[1]), - ), - next[2] -end - -Base.length(os::StratReprOpscenario) = length(os.operational) - -function Base.eltype(::Type{StratReprOpscenario{T}}) where {T} - return OperationalPeriod -end +opscenarios(ts::OperationalScenarios) = OpScens(ts) -function Base.last(sro::StratReprOpscenario) - per = last(sro.operational) - rper = ReprPeriod(_rper(sro), per, mult_repr(sro) * multiple(per)) - return OperationalPeriod( - sro.sp, - rper, - sro.mult_sp * mult_repr(sro) * multiple(per), +# Provide a constructor to simplify the design +function OperationalScenario(oscs::OpScens, per::Int) + return OperationalScenario( + per, + oscs.ts.probability[per], + _multiple_adj(oscs.ts, per), + oscs.ts.scenarios[per], ) end -function Base.getindex(os::StratReprOpscenario, index) - mult = stripunit(os.duration * os.op_per_strat / duration(os.operational)) - period = ReprPeriod(_rper(os), os.operational[index], mult) - return OperationalPeriod(os.sp, period, mult) +# Add basic functions of iterators +Base.length(oscs::OpScens) = oscs.ts.len +function Base.eltype(_::Type{OpScens{T,OP}}) where {T,OP<:TimeStructure{T}} + return OperationalScenario{T,OP} end +function Base.iterate(oscs::OpScens, state = nothing) + per = isnothing(state) ? 1 : state + 1 + per > length(oscs) && return nothing -function Base.eachindex(os::StratReprOpscenario) - return eachindex(os.operational) + return OperationalScenario(oscs, per), per end - -struct StratReprOpscenarios - srp::StratReprPeriod - opscens::Any +function Base.getindex(oscs::OpScens, index::Int) + return OperationalScenario(oscs, index) end - -function opscenarios( - srp::StratReprPeriod{T,RepresentativePeriod{T,OP}}, -) where {T,OP} - return StratReprOpscenarios(srp, opscenarios(srp.operational.operational)) +function Base.eachindex(oscs::OpScens) + return eachindex(oscs.ts.scenarios) end - -function opscenarios(srp::StratReprPeriod) - return StratOpScens(srp.sp, srp.mult_sp, opscenarios(srp.operational)) -end - -Base.length(srop::StratReprOpscenarios) = length(srop.opscens) - -function Base.iterate(srop::StratReprOpscenarios, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(srop.opscens) : - iterate(srop.opscens, state[1]) - isnothing(next) && return nothing - - scen = state[2] - return StratReprOpscenario( - srop.srp.sp, - _rper(srop.srp), - scen, - srop.srp.mult_sp, - mult_repr(srop.srp), - probability(next[1]), - next[1], - ), - (next[2], scen + 1) -end - -struct SingleScenarioWrapper{T,SC<:TimeStructure{T}} <: TimeStructure{T} - ts::SC -end - -function Base.iterate(ssw::SingleScenarioWrapper, state = nothing) - !isnothing(state) && return nothing - return SingleScenario(ssw.ts), 1 -end -Base.length(ssw::SingleScenarioWrapper) = 1 -function Base.eltype(::Type{SingleScenarioWrapper{T,SC}}) where {T,SC} - return SingleScenario{T,SC} -end - -struct SingleScenario{T,SC<:TimeStructure{T}} <: AbstractOperationalScenario{T} - ts::SC -end -Base.length(ss::SingleScenario) = length(ss.ts) -Base.eltype(::Type{SingleScenario{T,SC}}) where {T,SC} = eltype(SC) - -function Base.iterate(ss::SingleScenario, state = nothing) - if isnothing(state) - return iterate(ss.ts) - end - return iterate(ss.ts, state) -end -Base.last(ss::SingleScenario) = last(ss.ts) -function Base.last( - ss::SingleScenario{T,RepresentativePeriod{T,OP}}, -) where {T,OP} - period = last(ss.ts.operational) - return ReprPeriod(_rper(ss.ts), period, mult_repr(ss.ts) * multiple(period)) -end - -function Base.getindex(ss::SingleScenario, index) - return ss.ts[index] -end - -function Base.eachindex(ss::SingleScenario) - return eachindex(ss.ts) -end - -probability(ss::SingleScenario) = 1.0 -_strat_per(ss::SingleScenario) = 1 -_repr_per(ss::SingleScenario) = 1 -_opscen(ss::SingleScenario) = 1 - -StrategicIndexable(::Type{<:SingleScenario}) = HasStratIndex() -RepresentativeIndexable(::Type{<:SingleScenario}) = HasRepresentativeIndex() -ScenarioIndexable(::Type{<:SingleScenario}) = HasScenarioIndex() - -# Allow TimeStructures without operational scenarios to behave as one operational scenario -opscenarios(ts::TimeStructure) = SingleScenarioWrapper(ts) - -opscenarios(ts::SingleReprPeriod) = opscenarios(ts.ts) -opscenarios(ts::SingleStrategicPeriod) = opscenarios(ts.ts) +Base.last(oscs::OpScens) = OperationalScenario(oscs, length(oscs)) diff --git a/src/op_scenarios/rep_periods.jl b/src/op_scenarios/rep_periods.jl new file mode 100644 index 0000000..cbcd38d --- /dev/null +++ b/src/op_scenarios/rep_periods.jl @@ -0,0 +1,125 @@ +""" + ReprOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} + +A type representing a single operational scenarios supporting iteration over its +time periods. It is created when iterating through [`RepOpScens`](@ref). +""" +struct ReprOperationalScenario{T,OP<:TimeStructure{T}} <: + AbstractOperationalScenario{T} + rp::Int + scen::Int + probability::Float64 + multiple_repr::Float64 + multiple_scen::Float64 + operational::OP +end + +_opscen(osc::ReprOperationalScenario) = osc.scen +_rper(osc::ReprOperationalScenario) = osc.rp + +probability(osc::ReprOperationalScenario) = osc.probability +mult_scen(osc::ReprOperationalScenario) = osc.multiple_scen +mult_repr(osc::ReprOperationalScenario) = osc.multiple_repr + +RepresentativeIndexable(::Type{<:ReprOperationalScenario}) = HasReprIndex() + +# Provide a constructor to simplify the design +function ReprPeriod(osc::ReprOperationalScenario, per) + mult_scp = mult_scen(osc) * multiple(per) + mult_rp = mult_repr(osc) * mult_scp + scp = ScenarioPeriod(_opscen(osc), probability(osc), mult_scp, per) + return ReprPeriod(_rper(osc), scp, mult_rp) +end + +function Base.show(io::IO, osc::ReprOperationalScenario) + return print(io, "rp$(osc.rp)-sc$(osc.scen)") +end + +# Add basic functions of iterators +Base.length(osc::ReprOperationalScenario) = length(osc.operational) +function Base.eltype(osc::ReprOperationalScenario{T,OP}) where {T,OP} + return ReprPeriod{ScenarioPeriod{eltype(OP)}} +end +function Base.iterate(osc::ReprOperationalScenario, state = nothing) + next = + isnothing(state) ? iterate(osc.operational) : + iterate(osc.operational, state) + next === nothing && return nothing + + return ReprPeriod(osc, next[1]), next[2] +end +function Base.getindex(osc::ReprOperationalScenario, index::Int) + per = osc.operational[index] + return ReprPeriod(osc, per) +end +function Base.eachindex(osc::ReprOperationalScenario) + return eachindex(osc.operational) +end +function Base.last(osc::ReprOperationalScenario) + per = last(osc.operational) + return ReprPeriod(osc, per) +end + +""" + RepOpScens{OP} + +Type for iterating through the individual operational scenarios of a +[`RepresentativePeriod`](@ref) time structure. It is automatically created through the +function [`opscenarios`](@ref). +""" +struct RepOpScens{OP} + rp::Int + mult::Float64 + op_scens::OP +end + +_rper(oscs::RepOpScens) = oscs.rp + +""" + opscenarios(rep::RepresentativePeriod) + +Iterator that iterates over operational scenarios in a `RepresentativePeriod` time structure. +""" +function opscenarios( + rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}, +) where {T,OP} + return RepOpScens(_rper(rep), mult_repr(rep), rep.operational) +end + +# Provide a constructor to simplify the design +function ReprOperationalScenario(oscs::RepOpScens, state::Int) + return ReprOperationalScenario( + _rper(oscs), + state, + oscs.op_scens.probability[state], + oscs.mult, + _multiple_adj(oscs.op_scens, state), + oscs.op_scens.scenarios[state] + ) +end + +Base.length(oscs::RepOpScens) = length(oscs.op_scens.scenarios) +function Base.eltype(_::RepOpScens{SC}) where {T,OP,SC<:OperationalScenarios{T,OP}} + return ReprOperationalScenario{T,OP} +end +function Base.iterate(oscs::RepOpScens, state = nothing) + scen = isnothing(state) ? 1 : state + 1 + scen > length(oscs) && return nothing + + return ReprOperationalScenario(oscs, scen), scen +end + +""" +When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `repr_periods` returns the +correct behavior based on the substructure. +""" +opscenarios(ts::SingleReprPeriod) = opscenarios(ts.ts) +""" +When the `TimeStructure` is a [`RepresentativePeriods`](@ref), `opscenarios` returns an +`Array` of all [`ReprOperationalScenario`](@ref)s. +""" +function opscenarios(ts::RepresentativePeriods) + return collect( + Iterators.flatten(opscenarios(rp) for rp in repr_periods(ts)), + ) +end diff --git a/src/op_scenarios/strat_periods.jl b/src/op_scenarios/strat_periods.jl new file mode 100644 index 0000000..e4e4412 --- /dev/null +++ b/src/op_scenarios/strat_periods.jl @@ -0,0 +1,285 @@ +""" + StratOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} + +A type representing a single operational scenario supporting iteration over its +time periods. It is created when iterating through [`StratOpScens`](@ref). +""" +struct StratOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} + sp::Int + scen::Int + mult_sp::Float64 + mult_scen::Float64 + probability::Float64 + operational::OP +end + +_opscen(osc::StratOperationalScenario) = osc.scen +_strat_per(osc::StratOperationalScenario) = osc.sp + +probability(osc::StratOperationalScenario) = osc.probability +mult_scen(osc::StratOperationalScenario) = osc.mult_scen +mult_strat(osc::StratOperationalScenario) = osc.mult_sp + +StrategicIndexable(::Type{<:StratOperationalScenario}) = HasStratIndex() +ScenarioIndexable(::Type{<:StratOperationalScenario}) = HasScenarioIndex() + +# Provide a constructor to simplify the design +function OperationalPeriod(osc::StratOperationalScenario, per) + mult = mult_strat(osc) * multiple(per) + return OperationalPeriod(osc.sp, per, mult) +end + +function Base.show(io::IO, osc::StratOperationalScenario) + return print(io, "sp$(osc.sp)-sc$(osc.scen)") +end + +# Add basic functions of iterators +Base.length(osc::StratOperationalScenario) = length(osc.operational) +function Base.eltype(::Type{StratOperationalScenario{T,OP}}) where {T,OP} + return OperationalPeriod +end +function Base.iterate(osc::StratOperationalScenario, state = nothing) + next = + isnothing(state) ? iterate(osc.operational) : + iterate(osc.operational, state) + isnothing(next) && return nothing + + return OperationalPeriod(osc, next[1]), next[2] +end +function Base.getindex(osc::StratOperationalScenario, index) + per = osc.operational[index] + return OperationalPeriod(osc, per) +end +function Base.eachindex(osc::StratOperationalScenario) + return eachindex(osc.operational) +end +function Base.last(osc::StratOperationalScenario) + per = last(osc.operational) + return OperationalPeriod(osc, per) +end + +""" + StratOpScens{OP} + +Type for iterating through the individual operational scenarios of a +[`StrategicPeriod`](@ref) time structure. It is automatically created through the function +[`opscenarios`](@ref). +""" +struct StratOpScens{OP} + sp::Int + mult_sp::Float64 + opscens::OP +end + +_strat_per(oscs::StratOpScens) = oscs.sp + +mult_strat(oscs::StratOpScens) = oscs.mult_sp + +""" +When the `TimeStructure` is a [`StrategicPeriod`](@ref), `opscenarios` returns the iterator +[`StratOpScens`](@ref). +""" +function opscenarios(sp::StrategicPeriod{S,T,OP}) where {S,T,OP} + return StratOpScens(_strat_per(sp), mult_strat(sp), opscenarios(sp.operational)) +end + +""" +When the `TimeStructure` is a [`TwoLevel`](@ref), `opscenarios` returns a vector of +[`StratOperationalScenario`](@ref)s. +""" +function opscenarios(ts::TwoLevel{S,T,OP}) where {S,T,OP} + return collect( + Iterators.flatten(opscenarios(sp) for sp in strategic_periods(ts)), + ) +end + +# Provide a constructor to simplify the design +function StratOperationalScenario(oscs::StratOpScens, scen::Int, per) + return StratOperationalScenario( + _strat_per(oscs), + scen, + mult_strat(oscs), + mult_scen(per), + probability(per), + per + ) +end + +Base.length(oscs::StratOpScens) = length(oscs.opscens) +function Base.iterate(oscs::StratOpScens, state = (nothing, 1)) + next = + isnothing(state[1]) ? iterate(oscs.opscens) : + iterate(oscs.opscens, state[1]) + isnothing(next) && return nothing + + scen = state[2] + return StratOperationalScenario(oscs, _opscen(next[1]), next[1]), (next[2], scen + 1) +end +function Base.getindex(oscs::StratOpScens, index::Int) + per = oscs.opscens[index] + return StratOperationalScenario(oscs, index, per) +end +function Base.eachindex(oscs::StratOpScens) + return eachindex(oscs.opscens) +end +function Base.last(oscs::StratOpScens) + per = last(oscs.repr) + return StratOperationalScenario(oscs, _opscen(per), per) +end + +""" + StratReprOpscenario{T, OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} + +A type representing a single representative period supporting iteration over its +time periods. It is created when iterating through [`StratReprPeriods`](@ref). +""" +struct StratReprOpscenario{T, OP<:TimeStructure{T}} <: + AbstractOperationalScenario{T} + sp::Int + rp::Int + opscen::Int + mult_sp::Float64 + mult_rp::Float64 + probability::Float64 + operational::OP +end + +_opscen(osc::StratReprOpscenario) = osc.opscen +_rper(osc::StratReprOpscenario) = osc.rp +_strat_per(osc::StratReprOpscenario) = osc.sp + +probability(osc::StratReprOpscenario) = osc.probability +mult_scen(osc::StratReprOpscenario) = osc.multiple_scen +mult_repr(osc::StratReprOpscenario) = osc.mult_rp +mult_strat(osc::StratReprOpscenario) = osc.mult_sp + +StrategicIndexable(::Type{<:StratReprOpscenario}) = HasStratIndex() +function RepresentativeIndexable(::Type{<:StratReprOpscenario}) + return HasReprIndex() +end +ScenarioIndexable(::Type{<:StratReprOpscenario}) = HasScenarioIndex() + +# Provide a constructor to simplify the design +function OperationalPeriod(osc::StratReprOpscenario, per) + rper = ReprPeriod(_rper(osc), per, mult_repr(osc) * multiple(per)) + mult = mult_strat(osc) * mult_repr(osc) * multiple(per) + return OperationalPeriod(_strat_per(osc), rper, mult) +end + +function Base.show(io::IO, osc::StratReprOpscenario) + return print(io, "sp$(osc.sp)-rp$(_rper(osc))-sc$(osc.opscen)") +end + +# Add basic functions of iterators +Base.length(osc::StratReprOpscenario) = length(osc.operational) +function Base.eltype(::Type{StratReprOpscenario{T,OP}}) where {T,OP} + return OperationalPeriod +end +function Base.iterate(osc::StratReprOpscenario, state = nothing) + next = + isnothing(state) ? iterate(osc.operational) : + iterate(osc.operational, state) + isnothing(next) && return nothing + + return OperationalPeriod(osc, next[1]), next[2] +end +function Base.getindex(osc::StratReprOpscenario, index) + per = osc.operational[index] + return OperationalPeriod(osc, per) +end +function Base.eachindex(osc::StratReprOpscenario) + return eachindex(osc.operational) +end +function Base.last(osc::StratReprOpscenario) + per = last(osc.operational) + return OperationalPeriod(osc, per) +end + +""" + StratReprOpscenarios{OP} + +Type for iterating through the individual operational scenarios of a +[`StrategicPeriod`](@ref) time structure with [`RepresentativePeriods`](@ref). It is +automatically created through the function [`opscenarios`](@ref). +""" +struct StratReprOpscenarios{OP} + srp::StratReprPeriod + opscens::OP +end + +_rper(oscs::StratReprOpscenarios) = _rper(oscs.srp) +_strat_per(oscs::StratReprOpscenarios) = _strat_per(oscs.srp) + +mult_repr(oscs::StratReprOpscenarios) = mult_repr(oscs.srp) +mult_strat(oscs::StratReprOpscenarios) = mult_strat(oscs.srp) + +function opscenarios( + srp::StratReprPeriod{T,RepresentativePeriod{T,OP}}, +) where {T,OP} + return StratReprOpscenarios(srp, opscenarios(srp.operational.operational)) +end +function opscenarios(rp::StratReprPeriod) + return StratOpScens(_strat_per(rp), mult_strat(rp), opscenarios(rp.operational)) +end + +""" +When the `TimeStructure` is a [`StrategicPeriod`](@ref) with [`RepresentativePeriods`](@ref), +`opscenarios` returns a vector of [`StratReprOpscenario`](@ref)s. +""" +function opscenarios( + sp::StrategicPeriod{S1,T,RepresentativePeriods{S2,T,OP}}, +) where {S1,S2,T,OP} + return collect( + Iterators.flatten(opscenarios(rp) for rp in repr_periods(sp)), + ) +end + +""" +When the `TimeStructure` is a [`TwoLevel`](@ref) with [`RepresentativePeriods`](@ref), +`opscenarios` returns a vector of [`StratReprOpscenario`](@ref)s. +""" +function opscenarios( + ts::TwoLevel{S1,T,RepresentativePeriods{S2,T,OP}}, +) where {S1,S2,T,OP} + return collect( + Iterators.flatten(opscenarios(rp) for sp in strategic_periods(ts) for rp in repr_periods(sp)), + ) +end + +# Provide a constructor to simplify the design +function StratReprOpscenario(oscs::StratReprOpscenarios, scen, per) + return StratReprOpscenario( + _strat_per(oscs), + _rper(oscs), + scen, + mult_strat(oscs), + mult_repr(oscs), + probability(per), + per, + ) +end + +# Add basic functions of iterators +Base.length(oscs::StratReprOpscenarios) = length(oscs.opscens) +function Base.eltype(_::Type{StratReprOpscenarios{SC}}) where {T,OP,SC<:OpScens{T,OP}} + return StratReprOpscenario{T,eltype(SC)} +end +function Base.iterate(oscs::StratReprOpscenarios, state = (nothing, 1)) + next = + isnothing(state[1]) ? iterate(oscs.opscens) : + iterate(oscs.opscens, state[1]) + isnothing(next) && return nothing + + return StratReprOpscenario(oscs, state[2], next[1]), (next[2], state[2] + 1) +end +function Base.getindex(oscs::StratReprOpscenarios, index::Int) + per = oscs.opscens[index] + return StratReprOpscenario(oscs, _opscen(per), per) +end +function Base.eachindex(oscs::StratReprOpscenarios) + return eachindex(oscs.opscens) +end +function Base.last(oscs::StratReprOpscenarios) + per = last(oscs.opscens) + return StratReprOpscenario(oscs, _opscen(per), per) +end From eca596ed0bb0bdb6eb54f215c3517fef191e43ba Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 08:30:14 +0200 Subject: [PATCH 11/20] Moved twoleveltree files to separate folder --- src/TimeStruct.jl | 8 ++++---- src/{twoleveltree.jl => strat_scenarios/core_types.jl} | 0 src/{tree_nodes.jl => strat_scenarios/tree_periods.jl} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename src/{twoleveltree.jl => strat_scenarios/core_types.jl} (100%) rename src/{tree_nodes.jl => strat_scenarios/tree_periods.jl} (100%) diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 5b611e1..947b33e 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -18,12 +18,12 @@ include(joinpath("representative", "strat_periods.jl")) include(joinpath("op_scenarios", "opscenarios.jl")) include(joinpath("op_scenarios", "rep_periods.jl")) include(joinpath("op_scenarios", "strat_periods.jl")) -include("utils.jl") -include("discount.jl") -include("tree_nodes.jl") -include("twoleveltree.jl") +include(joinpath("strat_scenarios", "tree_periods.jl")) +include(joinpath("strat_scenarios", "core_types.jl")) +include("utils.jl") +include("discount.jl") include("profiles.jl") export TimeStructure diff --git a/src/twoleveltree.jl b/src/strat_scenarios/core_types.jl similarity index 100% rename from src/twoleveltree.jl rename to src/strat_scenarios/core_types.jl diff --git a/src/tree_nodes.jl b/src/strat_scenarios/tree_periods.jl similarity index 100% rename from src/tree_nodes.jl rename to src/strat_scenarios/tree_periods.jl From 89cf829068887bd015113261b868cb720bb7bf10 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 10:08:40 +0200 Subject: [PATCH 12/20] Restructured tree structure --- src/TimeStruct.jl | 6 +- src/op_scenarios/tree_periods.jl | 273 +++++++++++++++++++++++ src/representative/tree_periods.jl | 113 ++++++++++ src/strat_scenarios/core_types.jl | 173 ++++----------- src/strat_scenarios/tree_periods.jl | 331 +--------------------------- src/utils.jl | 41 ++++ 6 files changed, 484 insertions(+), 453 deletions(-) create mode 100644 src/op_scenarios/tree_periods.jl create mode 100644 src/representative/tree_periods.jl diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 947b33e..917594a 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -13,14 +13,16 @@ include(joinpath("representative", "core_types.jl")) include(joinpath("op_scenarios", "core_types.jl")) include(joinpath("strategic", "core_types.jl")) include(joinpath("strategic", "strat_periods.jl")) +include(joinpath("strat_scenarios", "tree_periods.jl")) +include(joinpath("strat_scenarios", "core_types.jl")) include(joinpath("representative", "rep_periods.jl")) include(joinpath("representative", "strat_periods.jl")) +include(joinpath("representative", "tree_periods.jl")) include(joinpath("op_scenarios", "opscenarios.jl")) include(joinpath("op_scenarios", "rep_periods.jl")) include(joinpath("op_scenarios", "strat_periods.jl")) +include(joinpath("op_scenarios", "tree_periods.jl")) -include(joinpath("strat_scenarios", "tree_periods.jl")) -include(joinpath("strat_scenarios", "core_types.jl")) include("utils.jl") include("discount.jl") diff --git a/src/op_scenarios/tree_periods.jl b/src/op_scenarios/tree_periods.jl new file mode 100644 index 0000000..5ec40da --- /dev/null +++ b/src/op_scenarios/tree_periods.jl @@ -0,0 +1,273 @@ + +""" + struct StratNodeOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} + +A structure representing a single operational scenario for a strategic node supporting +iteration over its time periods. It is created through iterating through +[`StratNodeOpScens`](@ref). + +It is equivalent to a [`StratOperationalScenario`](@ref) of a [`TwoLevel`](@ref) time +structure when utilizing a [`TwolevelTree`]. +""" +struct StratNodeOperationalScenario{T,OP<:TimeStructure{T}} <: + AbstractOperationalScenario{T} + sp::Int + branch::Int + scen::Int + mult_sp::Float64 + mult_scen::Float64 + prob_branch::Float64 + prob_scen::Float64 + operational::OP +end + +_strat_per(osc::StratNodeOperationalScenario) = osc.sp +_branch(osc::StratNodeOperationalScenario) = osc.branch +_opscen(osc::StratNodeOperationalScenario) = osc.scen + +mult_strat(osc::StratNodeOperationalScenario) = osc.mult_sp +mult_scen(osc::StratNodeOperationalScenario) = osc.mult_scen +probability(osc::StratNodeOperationalScenario) = osc.prob_branch * prob_scen +probability_branch(osc::StratNodeOperationalScenario) = osc.prob_branch + +_oper_struct(osc::StratNodeOperationalScenario) = osc.operational + +# Provide a constructor to simplify the design +function TreePeriod( + osc::StratNodeOperationalScenario, + per::P, +) where {P<:Union{TimePeriod,AbstractOperationalScenario}} + mult = mult_strat(osc) * multiple(per) + return TreePeriod(_strat_per(osc), _branch(osc), probability_branch(osc), mult, per) +end + +function StrategicTreeIndexable(::Type{<:StratNodeOperationalScenario}) + return HasStratTreeIndex() +end +StrategicIndexable(::Type{<:StratNodeOperationalScenario}) = HasStratIndex() + +# Adding methods to existing Julia functions +function Base.show(io::IO, osc::StratNodeOperationalScenario) + return print(io, "sp$(_strat_per(osc))-br$(_branch(osc))-sc$(_opscen(osc))") +end +Base.eltype(_::StratNodeOperationalScenario) = TreePeriod + +""" + struct StratNodeOpScens <: AbstractTreeStructure + +Type for iterating through the individual operational scenarios of a [`StratNode`](@ref). +It is automatically created through the function [`opscenarios`](@ref). +""" +struct StratNodeOpScens <: AbstractTreeStructure + sp::Int + branch::Int + mult_sp::Float64 + prob_branch::Float64 + opscens::Any +end + +_strat_per(oscs::StratNodeOpScens) = oscs.sp +_branch(oscs::StratNodeOpScens) = oscs.branch + +mult_strat(oscs::StratNodeOpScens) = oscs.mult_sp +probability_branch(oscs::StratNodeOpScens) = oscs.prob_branch + +_oper_struct(oscs::StratNodeOpScens) = oscs.opscens + +""" +When the `TimeStructure` is a [`StratNode`](@ref), `opscenarios` returns the iterator +[`StratNodeOpScens`](@ref). +""" +function opscenarios(n::StratNode{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return StratNodeOpScens( + _strat_per(n), + _branch(n), + mult_strat(n), + probability_branch(n), + opscenarios(n.operational), + ) +end + +function strat_node_period(oscs::StratNodeOpScens, next, state) + return StratNodeOperationalScenario( + _strat_per(oscs), + _branch(oscs), + state, + mult_strat(oscs), + mult_scen(next), + probability_branch(oscs), + probability(next), + next, + ) +end + +Base.eltype(_::StratNodeOpScens) = StratNodeOperationalScenario + +""" + struct StratNodeReprOpScenario{T} <: AbstractOperationalScenario{T} + +A structure representing a single operational scenario for a representative period in A +[`TwoLevelTree`] structure supporting iteration over its time periods. +""" +struct StratNodeReprOpScenario{T,OP<:TimeStructure{T}} <: + AbstractOperationalScenario{T} + sp::Int + branch::Int + rp::Int + scen::Int + mult_sp::Float64 + mult_rp::Float64 + mult_scen::Float64 + prob_branch::Float64 + prob_scen::Float64 + operational::OP +end + +_strat_per(osc::StratNodeReprOpScenario) = osc.sp +_branch(osc::StratNodeReprOpScenario) = osc.branch +_rper(osc::StratNodeReprOpScenario) = osc.rp +_opscen(osc::StratNodeReprOpScenario) = osc.scen + +mult_strat(osc::StratNodeReprOpScenario) = osc.mult_sp +mult_repr(osc::StratNodeReprOpScenario) = osc.mult_rp +mult_scen(osc::StratNodeReprOpScenario) = osc.mult_scen +probability_branch(osc::StratNodeReprOpScenario) = osc.prob_branch +probability(osc::StratNodeReprOpScenario) = osc.prob_branch * osc.prob_scen + +_oper_struct(osc::StratNodeReprOpScenario) = osc.operational + +StrategicTreeIndexable(::Type{<:StratNodeReprOpScenario}) = HasStratTreeIndex() +StrategicIndexable(::Type{<:StratNodeReprOpScenario}) = HasStratIndex() +function RepresentativeIndexable(::Type{<:StratNodeReprOpScenario}) + return HasReprIndex() +end +ScenarioIndexable(::Type{<:StratNodeReprOpScenario}) = HasScenarioIndex() + +# Provide a constructor to simplify the design +function TreePeriod( + osc::StratNodeReprOpScenario, + per::P, +) where {P<:Union{TimePeriod,AbstractOperationalScenario}} + rper = ReprPeriod(_rper(osc), per, mult_repr(osc) * multiple(per)) + mult = mult_strat(osc) * mult_repr(osc) * multiple(per) + return TreePeriod(_strat_per(osc), _branch(osc), probability_branch(osc), mult, rper) +end + +# Adding methods to existing Julia functions +function Base.show(io::IO, osc::StratNodeReprOpScenario) + return print(io, "sp$(_strat_per(osc))-br$(_branch(osc))-rp$(_rper(osc))-sc$(_opscen(osc))") +end +Base.eltype(_::StratNodeReprOpScenario) = TreePeriod + + +""" + struct StratNodeReprOpScens <: AbstractTreeStructure + +Type for iterating through the individual operational scenarios of a +[`StratNodeReprPeriod`](@ref). It is automatically created through the function +[`opscenarios`](@ref). +""" +struct StratNodeReprOpScens <: AbstractTreeStructure + sp::Int + branch::Int + rp::Int + mult_sp::Float64 + mult_rp::Float64 + prob_branch::Float64 + opscens::Any +end + +_strat_per(oscs::StratNodeReprOpScens) = oscs.sp +_branch(oscs::StratNodeReprOpScens) = oscs.branch +_rper(oscs::StratNodeReprOpScens) = oscs.rp + +mult_strat(oscs::StratNodeReprOpScens) = oscs.mult_sp +mult_repr(oscs::StratNodeReprOpScens) = oscs.mult_rp +probability_branch(oscs::StratNodeReprOpScens) = oscs.prob_branch + +_oper_struct(oscs::StratNodeReprOpScens) = oscs.opscens + +""" +When the `TimeStructure` is a [`StratNodeReprPeriod`](@ref) with a [`RepresentativePeriod`](@ref), +`opscenarios` returns the iterator [`StratNodeReprOpScens`](@ref). +""" +function opscenarios( + rp::StratNodeReprPeriod{T,RepresentativePeriod{T,OP}}, +) where {T,OP} + return StratNodeReprOpScens( + _strat_per(rp), + _branch(rp), + _rper(rp), + mult_strat(rp), + mult_repr(rp), + probability_branch(rp), + opscenarios(rp.operational.operational), + ) +end + +""" +When the `TimeStructure` is a [`StratNodeReprPeriod`](@ref) with a [`SingleReprPeriod`](@ref), +`opscenarios` returns the iterator [`StratNodeOpScens`](@ref) as the overall time structure +does not include representative periods. +""" +function opscenarios( + rp::StratNodeReprPeriod{T,SingleReprPeriod{T,OP}}, +) where {T,OP} + return StratNodeOpScens( + _strat_per(rp), + _branch(rp), + mult_strat(rp), + probability_branch(rp), + opscenarios(rp.operational.ts), + ) +end + +function strat_node_period(oscs::StratNodeReprOpScens, next, state) + return StratNodeReprOpScenario( + _strat_per(oscs), + _branch(oscs), + _rper(oscs), + state, + mult_strat(oscs), + mult_repr(oscs), + mult_scen(next), + probability_branch(oscs), + probability(next), + next, + ) +end + +Base.eltype(_::StratNodeReprOpScens) = StratNodeReprOpScenario + +""" +When the `TimeStructure` is a [`StratNode`](@ref) with [`RepresentativePeriods`](@ref), +`opscenarios` returns an `Array` of all [`StratNodeReprOpScenario`](@ref)s. +""" +function opscenarios(n::StratNode{S,T,OP}) where {S,T,OP<:RepresentativePeriods} + return collect(Iterators.flatten(opscenarios(rp) for rp in repr_periods(n))) +end + +""" +When the `TimeStructure` is a [`TwoLevelTree`](@ref), `opscenarios` returns an `Array` of +all [`StratNodeOperationalScenario`](@ref)s or [`StratNodeReprOpScenario`](@ref)s types, +dependening on whether the [`TwoLevelTree`](@ref) includes [`RepresentativePeriods`](@ref) +or not. + +These are equivalent to a [`StratOperationalScenario`](@ref) and [`StratReprOpscenario`](@ref) +of a [`TwoLevel`](@ref) time structure. +""" +function opscenarios(ts::TwoLevelTree) + return collect( + Iterators.flatten(opscenarios(sp) for sp in strat_periods(ts)), + ) +end +function opscenarios( + ts::TwoLevelTree{T,StratNode{S,T,OP}}, +) where {S,T,OP<:RepresentativePeriods} + return collect( + Iterators.flatten( + opscenarios(rp) for sp in strat_periods(ts) for + rp in repr_periods(sp) + ), + ) +end diff --git a/src/representative/tree_periods.jl b/src/representative/tree_periods.jl new file mode 100644 index 0000000..a1b4c56 --- /dev/null +++ b/src/representative/tree_periods.jl @@ -0,0 +1,113 @@ + + +""" + struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} + +A structure representing a single representative period of a [`StratNode`](@ref) of a +[`TwolevelTree`]. It is created through iterating through [`StratNodeReprPeriods`](@ref). + +It is equivalent to a [`StratReprPeriod`] of a [`TwoLevel`](@ref) time structure when +utilizing a [`TwolevelTree`]. +""" +struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: + AbstractRepresentativePeriod{T} + sp::Int + branch::Int + rp::Int + mult_sp::Float64 + mult_rp::Float64 + prob_branch::Float64 + operational::OP +end + +_strat_per(rp::StratNodeReprPeriod) = rp.sp +_branch(rp::StratNodeReprPeriod) = rp.branch +_rper(rp::StratNodeReprPeriod) = rp.rp + +mult_strat(rp::StratNodeReprPeriod) = rp.mult_sp +mult_repr(rp::StratNodeReprPeriod) = rp.mult_rp +probability_branch(rp::StratNodeReprPeriod) = rp.prob_branch +probability(rp::StratNodeReprPeriod) = rp.prob_branch +function multiple(rp::StratNodeReprPeriod, t::OperationalPeriod) + return t.multiple / rp.mult_sp +end + +StrategicTreeIndexable(::Type{<:StratNodeReprPeriod}) = HasStratTreeIndex() +StrategicIndexable(::Type{<:StratNodeReprPeriod}) = HasStratIndex() + +# Provide a constructor to simplify the design +function TreePeriod( + rp::StratNodeReprPeriod, + per::P, +) where {P<:Union{TimePeriod,AbstractRepresentativePeriod}} + mult = mult_strat(rp) * multiple(per) + return TreePeriod(_strat_per(rp), _branch(rp), probability_branch(rp), mult, per) +end + +# Adding methods to existing Julia functions +function Base.show(io::IO, rp::StratNodeReprPeriod) + return print(io, "sp$(_strat_per(rp))-br$(_branch(rp))-rp$(_rper(rp))") +end +Base.eltype(_::StratNodeReprPeriod) = TreePeriod + +""" + struct StratNodeReprPeriods <: AbstractTreeStructure + +Type for iterating through the individual presentative periods of a [`StratNode`](@ref). +It is automatically created through the function [`repr_periods`](@ref). +""" +struct StratNodeReprPeriods <: AbstractTreeStructure + sp::Int + branch::Int + mult_sp::Float64 + prob_branch::Float64 + repr::Any +end + +_strat_per(rps::StratNodeReprPeriods) = rps.sp +_branch(rps::StratNodeReprPeriods) = rps.branch + +mult_strat(rps::StratNodeReprPeriods) = rps.mult_sp +probability_branch(rps::StratNodeReprPeriods) = rps.prob_branch + +_oper_struct(rps::StratNodeReprPeriods) = rps.repr + +""" +When the `TimeStructure` is a [`StratNode`](@ref), `repr_periods` returns the iterator +[`StratNodeReprPeriods`](@ref). +""" +function repr_periods(n::StratNode{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return StratNodeReprPeriods( + _strat_per(n), + _branch(n), + mult_strat(n), + probability_branch(n), + repr_periods(n.operational), + ) +end + +function strat_node_period(rps::StratNodeReprPeriods, next, state) + return StratNodeReprPeriod( + _strat_per(rps), + _branch(rps), + state, + mult_strat(rps), + mult_repr(next), + probability_branch(rps), + next, + ) +end + +Base.eltype(_::StratNodeReprPeriods) = StratNodeReprPeriod + +""" +When the `TimeStructure` is a [`TwoLevelTree`](@ref), `repr_periods` returns an `Array` of +all [`StratNodeReprPeriod`](@ref)s. + +These are equivalent to a [`StratReprPeriod`](@ref) of a [`TwoLevel`](@ref) time structure. +""" +function repr_periods(ts::TwoLevelTree) + return collect( + Iterators.flatten(repr_periods(sp) for sp in strat_periods(ts)), + ) +end diff --git a/src/strat_scenarios/core_types.jl b/src/strat_scenarios/core_types.jl index 61c8b6a..dc29a78 100644 --- a/src/strat_scenarios/core_types.jl +++ b/src/strat_scenarios/core_types.jl @@ -1,30 +1,25 @@ - """ - mutable struct TwoLevelTree{T} <: TimeStructure{T} + mutable struct TwoLevelTree{S,T,OP<:AbstractTreeNode{S,T}} <: TimeStructure{T} Time structure allowing for a tree structure for the strategic level. For each strategic node in the tree a separate time structure is used for operational decisions. Iterating the structure will go through all operational periods. """ -mutable struct TwoLevelTree{T,OP<:AbstractTreeNode{T}} <: TimeStructure{T} +mutable struct TwoLevelTree{S,T,OP<:AbstractTreeNode{S,T}} <: TimeStructure{T} len::Int root::Any nodes::Vector{OP} op_per_strat::Float64 end -function TwoLevelTree{T,OP}( +function TwoLevelTree{S,T,OP}( nodes::Vector{OP}, op_per_strat, -) where {T,OP<:AbstractTreeNode{T}} - return TwoLevelTree{T,OP}(0, nothing, nodes, op_per_strat) +) where {S,T,OP<:AbstractTreeNode{S,T}} + return TwoLevelTree{S,T,OP}(0, nothing, nodes, op_per_strat) end -function Base.length(itr::TwoLevelTree) - return sum(length(n.operational) for n in itr.nodes) -end -Base.eltype(::Type{TwoLevelTree{T,OP}}) where {T,OP} = TreePeriod function _multiple_adj(itr::TwoLevelTree, n) mult = @@ -32,18 +27,38 @@ function _multiple_adj(itr::TwoLevelTree, n) _total_duration(itr.nodes[n].operational) return stripunit(mult) end -strat_nodes(tree::TwoLevelTree) = tree.nodes +strat_nodes(ts::TwoLevelTree) = ts.nodes function children(n::StratNode, ts::TwoLevelTree) - return [c for c in ts.nodes if c.parent == n] + return [c for c in ts.nodes if _parent(c) == n] end -nchildren(n::StratNode, ts::TwoLevelTree) = count(c -> c.parent == n, ts.nodes) +nchildren(n::StratNode, ts::TwoLevelTree) = count(c -> _parent(c) == n, strat_nodes(ts)) -branches(tree::TwoLevelTree, sp) = count(n -> n.sp == sp, tree.nodes) +branches(ts::TwoLevelTree, sp::Int) = count(n -> _strat_per(n) == sp, strat_nodes(ts)) +leaves(ts::TwoLevelTree) = [n for n in strat_nodes(ts) if nchildren(n, ts) == 0] +nleaves(ts::TwoLevelTree) = count(n -> nchildren(n, ts) == 0, strat_nodes(ts)) +getleaf(ts::TwoLevelTree, leaf::Int) = leaves(ts)[leaf] -leaves(ts::TwoLevelTree) = [n for n in ts.nodes if nchildren(n, ts) == 0] -nleaves(ts::TwoLevelTree) = count(n -> nchildren(n, ts) == 0, ts.nodes) -getleaf(ts::TwoLevelTree, leaf) = leaves(ts)[leaf] +function Base.length(itr::TwoLevelTree) + return sum(length(n.operational) for n in itr.nodes) +end +Base.eltype(::Type{TwoLevelTree{S,T,OP}}) where {S,T,OP} = eltype(OP) +function Base.iterate(itr::TwoLevelTree, state = (1, nothing)) + i = state[1] + n = itr.nodes[i] + next = + isnothing(state[2]) ? iterate(n.operational) : + iterate(n.operational, state[2]) + if next === nothing + i = i + 1 + if i > length(itr.nodes) + return nothing + end + n = itr.nodes[i] + next = iterate(n.operational) + end + return TreePeriod(n, next[1]), (i, next[2]) +end """ struct TreePeriod{P} <: TimePeriod where {P<:TimePeriod} @@ -65,17 +80,19 @@ struct TreePeriod{P} <: TimePeriod where {P<:TimePeriod} period::P end +_strat_per(t::TreePeriod) = t.sp +_branch(t::TreePeriod) = t.branch + +_rper(t::TreePeriod) = _rper(t.period) +_opscen(t::TreePeriod) = _opscen(t.period) +_oper(t::TreePeriod) = _oper(t.period) + isfirst(t::TreePeriod) = isfirst(t.period) duration(t::TreePeriod) = duration(t.period) +multiple(t::TreePeriod) = t.multiple probability_branch(t::TreePeriod) = t.prob_branch probability(t::TreePeriod) = probability(t.period) * probability_branch(t) -multiple(t::TreePeriod) = t.multiple -_oper(t::TreePeriod) = _oper(t.period) -_opscen(t::TreePeriod) = _opscen(t.period) -_rper(t::TreePeriod) = _rper(t.period) -_branch(t::TreePeriod) = t.branch -_strat_per(t::TreePeriod) = t.sp function Base.show(io::IO, t::TreePeriod) return print(io, "sp$(t.sp)-br$(t.branch)-$(t.period)") @@ -84,58 +101,14 @@ function Base.isless(t1::TreePeriod, t2::TreePeriod) return t1.period < t2.period end -function Base.iterate(itr::TwoLevelTree, state = (1, nothing)) - i = state[1] - spn = itr.nodes[i] - next = - isnothing(state[2]) ? iterate(spn.operational) : - iterate(spn.operational, state[2]) - if next === nothing - i = i + 1 - if i > length(itr.nodes) - return nothing - end - spn = itr.nodes[i] - next = iterate(spn.operational) - end - per = next[1] - - mult = _multiple_adj(itr, i) * multiple(per) - return TreePeriod(spn.sp, spn.branch, probability_branch(spn), mult, per), - (i, next[2]) -end - # Convenient constructors for the individual types function TreePeriod( n::StratNode, per::P, ) where {P<:Union{TimePeriod,TimeStructure}} mult = n.mult_sp * multiple(per) - return TreePeriod(n.sp, n.branch, n.prob_branch, mult, per) -end -function TreePeriod( - osc::StratNodeOperationalScenario, - per::P, -) where {P<:Union{TimePeriod,AbstractOperationalScenario}} - mult = osc.mult_sp * multiple(per) - return TreePeriod(osc.sp, osc.branch, osc.prob_branch, mult, per) + return TreePeriod(_strat_per(n), _branch(n), probability_branch(n), mult, per) end -function TreePeriod( - rp::StratNodeReprPeriod, - per::P, -) where {P<:Union{TimePeriod,AbstractRepresentativePeriod}} - mult = rp.mult_sp * multiple(per) - return TreePeriod(rp.sp, rp.branch, rp.prob_branch, mult, per) -end -function TreePeriod( - osc::StratNodeReprOpscenario, - per::P, -) where {P<:Union{TimePeriod,AbstractOperationalScenario}} - rper = ReprPeriod(osc.rp, per, osc.mult_rp * multiple(per)) - mult = osc.mult_sp * osc.mult_rp * multiple(per) - return TreePeriod(osc.sp, osc.branch, osc.prob_branch, mult, rper) -end - """ struct StrategicScenario @@ -219,7 +192,7 @@ Iterative addition of nodes. """ # Add nodes iteratively in a depth first manner function add_node( - tree::TwoLevelTree{T,StratNode{S,T,OP}}, + tree::TwoLevelTree{S,T,StratNode{S,T,OP}}, parent, sp, duration::S, @@ -275,7 +248,7 @@ function regular_tree( ts::OP; op_per_strat::Real = 1.0, ) where {S,T,OP<:TimeStructure{T}} - tree = TwoLevelTree{T,StratNode{S,T,OP}}( + tree = TwoLevelTree{S,T,StratNode{S,T,OP}}( Vector{StratNode{S,T,OP}}(), op_per_strat, ) @@ -286,17 +259,17 @@ function regular_tree( end """ - struct StratTreeNodes{T, OP} <: AbstractTreeStructure + struct StratTreeNodes{S, T, OP} <: AbstractTreeStructure Type for iterating through the individual strategic nodes of a [`TwoLevelTree`](@ref). It is automatically created through the function [`strat_periods`](@ref), and hence, [`strategic_periods`](@ref). Iterating through `StratTreeNodes` using the `WithPrev` iterator changes the behaviour, -although the meaining remains unchanged. +although the meaning remains unchanged. """ -struct StratTreeNodes{T,OP} <: AbstractTreeStructure - ts::TwoLevelTree{T,OP} +struct StratTreeNodes{S,T,OP} <: AbstractTreeStructure + ts::TwoLevelTree{S,T,OP} end # Adding methods to existing Julia functions @@ -308,58 +281,10 @@ function Base.iterate(stps::StratTreeNodes, state = nothing) return stps.ts.nodes[next], next end -function Base.iterate(w::WithPrev{StratTreeNodes{T,OP}}) where {T,OP} - n = iterate(w.itr) - n === nothing && return n - return (nothing, n[1]), (n[1], n[2]) -end - -function Base.iterate(w::WithPrev{StratTreeNodes{T,OP}}, state) where {T,OP} - n = iterate(w.itr, state[2]) - n === nothing && return n - return (n[1].parent, n[1]), (n[1], n[2]) -end - -"""When the `TimeStructure` is a [`TwoLevelTree`](@ref), `strat_periods` returns a +""" +When the `TimeStructure` is a [`TwoLevelTree`](@ref), `strat_periods` returns a [`StratTreeNodes`](@ref) type, which, through iteration, provides [`StratNode`](@ref) types. These are equivalent to a [`StrategicPeriod`](@ref) of a [`TwoLevel`](@ref) time structure. """ strat_periods(ts::TwoLevelTree) = StratTreeNodes(ts) - -""" -When the `TimeStructure` is a [`TwoLevelTree`](@ref), `opscenarios` returns an `Array` of -all [`StratNodeOperationalScenario`](@ref)s or [`StratNodeReprOpscenario`](@ref)s types, -dependening on whether the [`TwoLevelTree`](@ref) includes [`RepresentativePeriods`](@ref) -or not. - -These are equivalent to a [`StratOperationalScenario`](@ref) of a [`TwoLevel`](@ref) time -structure. -""" -function opscenarios(ts::TwoLevelTree) - return collect( - Iterators.flatten(opscenarios(sp) for sp in strat_periods(ts)), - ) -end -function opscenarios( - ts::TwoLevelTree{T,StratNode{S,T,OP}}, -) where {S,T,OP<:RepresentativePeriods} - return collect( - Iterators.flatten( - opscenarios(rp) for sp in strat_periods(ts) for - rp in repr_periods(sp) - ), - ) -end - -""" -When the `TimeStructure` is a [`TwoLevelTree`](@ref), `repr_periods` returns an `Array` of -all [`StratNodeReprPeriod`]s. - -These are equivalent to a [`StratReprPeriod`] of a [`TwoLevel`](@ref) time structure. -""" -function repr_periods(ts::TwoLevelTree) - return collect( - Iterators.flatten(repr_periods(sp) for sp in strat_periods(ts)), - ) -end diff --git a/src/strat_scenarios/tree_periods.jl b/src/strat_scenarios/tree_periods.jl index e4a07e1..b5b3bc2 100644 --- a/src/strat_scenarios/tree_periods.jl +++ b/src/strat_scenarios/tree_periods.jl @@ -53,15 +53,18 @@ _strat_per(n::StratNode) = n.sp _branch(n::StratNode) = n.branch probability_branch(n::StratNode) = n.prob_branch +mult_strat(n::StratNode) = n.mult_sp duration_strat(n::StratNode) = n.duration multiple_strat(sp::StratNode, t) = multiple(t) / duration_strat(sp) +_parent(n::StratNode) = n.parent + isfirst(n::StratNode) = n.sp == 1 # Adding methods to existing Julia functions Base.show(io::IO, n::StratNode) = print(io, "sp$(n.sp)-br$(n.branch)") Base.length(n::StratNode) = length(n.operational) -Base.eltype(::Type{StratNode}) = TreePeriod +Base.eltype(::Type{StratNode{S,T,OP}}) where {S,T,OP} = TreePeriod{eltype(OP)} function Base.iterate(n::StratNode, state = nothing) next = isnothing(state) ? iterate(n.operational) : @@ -70,329 +73,3 @@ function Base.iterate(n::StratNode, state = nothing) return TreePeriod(n, next[1]), next[2] end - -""" - struct StratNodeOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} - -A structure representing a single operational scenario for a strategic node supporting -iteration over its time periods. It is created through iterating through -[`StratNodeOpScens`](@ref). - -It is equivalent to a [`StratOperationalScenario`](@ref) of a [`TwoLevel`](@ref) time -structure when utilizing a [`TwolevelTree`]. -""" -struct StratNodeOperationalScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} - sp::Int - branch::Int - scen::Int - mult_sp::Float64 - mult_scen::Float64 - prob_branch::Float64 - prob_scen::Float64 - operational::OP -end - -_opscen(osc::StratNodeOperationalScenario) = osc.scen -_branch(osc::StratNodeOperationalScenario) = osc.branch -_strat_per(osc::StratNodeOperationalScenario) = osc.sp - -probability(osc::StratNodeOperationalScenario) = osc.prob_branch * prob_scen -probability_branch(osc::StratNodeOperationalScenario) = osc.prob_branch -mult_scen(osc::StratNodeOperationalScenario) = osc.mult_scen - -function StrategicTreeIndexable(::Type{<:StratNodeOperationalScenario}) - return HasStratTreeIndex() -end -StrategicIndexable(::Type{<:StratNodeOperationalScenario}) = HasStratIndex() - -# Adding methods to existing Julia functions -function Base.show(io::IO, osc::StratNodeOperationalScenario) - return print(io, "sp$(osc.sp)-br$(osc.branch)-sc$(osc.scen)") -end -Base.eltype(_::StratNodeOperationalScenario) = TreePeriod - -""" - struct StratNodeOpScens <: AbstractTreeStructure - -Type for iterating through the individual operational scenarios of a [`StratNode`](@ref). -It is automatically created through the function [`opscenarios`](@ref). -""" -struct StratNodeOpScens <: AbstractTreeStructure - sp::Int - branch::Int - mult_sp::Float64 - prob_branch::Float64 - opscens::Any -end - -""" - opscenarios(sp::StratNode{S,T,OP}) - -When the `TimeStructure` is a [`StratNode`](@ref), `opscenarios` returns a -[`StratNodeOpScens`](@ref) used for iterating through the individual operational scenarios -""" -function opscenarios(n::StratNode{S,T,OP}) where {S,T,OP<:TimeStructure{T}} - return StratNodeOpScens( - _strat_per(n), - _branch(n), - n.mult_sp, - probability_branch(n), - opscenarios(n.operational), - ) -end -function opscenarios(n::StratNode{S,T,OP}) where {S,T,OP<:RepresentativePeriods} - return collect(Iterators.flatten(opscenarios(rp) for rp in repr_periods(n))) -end - -_oper_struct(oscs::StratNodeOpScens) = oscs.opscens -function strat_node_period(oscs::StratNodeOpScens, next, state) - return StratNodeOperationalScenario( - oscs.sp, - oscs.branch, - state, - oscs.mult_sp, - mult_scen(next), - oscs.prob_branch, - probability(next), - next, - ) -end - -Base.eltype(_::StratNodeOpScens) = StratNodeOperationalScenario - -""" - struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} - -A structure representing a single representative period of a [`StratNode`](@ref) of a -[`TwolevelTree`]. It is created through iterating through [`StratNodeReprPeriods`](@ref). - -It is equivalent to a [`StratReprPeriod`] of a [`TwoLevel`](@ref) time structure when -utilizing a [`TwolevelTree`]. -""" -struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} - sp::Int - branch::Int - rp::Int - mult_sp::Float64 - mult_rp::Float64 - prob_branch::Float64 - operational::OP -end - -_rper(rp::StratNodeReprPeriod) = rp.rp -_branch(rp::StratNodeReprPeriod) = rp.branch -_strat_per(rp::StratNodeReprPeriod) = rp.sp - -probability(rp::StratNodeReprPeriod) = rp.prob_branch -probability_branch(rp::StratNodeReprPeriod) = rp.prob_branch -function multiple(rp::StratNodeReprPeriod, t::OperationalPeriod) - return t.multiple / rp.mult_sp -end - -StrategicTreeIndexable(::Type{<:StratNodeReprPeriod}) = HasStratTreeIndex() -StrategicIndexable(::Type{<:StratNodeReprPeriod}) = HasStratIndex() - -# Adding methods to existing Julia functions -function Base.show(io::IO, rp::StratNodeReprPeriod) - return print(io, "sp$(rp.sp)-br$(rp.branch)-rp$(rp.rp)") -end -Base.eltype(_::StratNodeReprPeriod) = TreePeriod - -""" - struct StratNodeReprPeriods <: AbstractTreeStructure - -Type for iterating through the individual presentative periods of a [`StratNode`](@ref). -It is automatically created through the function [`repr_periods`](@ref). -""" -struct StratNodeReprPeriods <: AbstractTreeStructure - sp::Int - branch::Int - mult_sp::Float64 - prob_branch::Float64 - repr::Any -end - -""" - repr_periods(sp::StratNode{S,T,OP}) - -Iterator that iterates over operational scenarios for a specific strategic node in the tree. -""" -function repr_periods(n::StratNode{S,T,OP}) where {S,T,OP<:TimeStructure{T}} - return StratNodeReprPeriods( - _strat_per(n), - _branch(n), - n.mult_sp, - probability_branch(n), - repr_periods(n.operational), - ) -end - -_oper_struct(rps::StratNodeReprPeriods) = rps.repr -function strat_node_period(rps::StratNodeReprPeriods, next, state) - return StratNodeReprPeriod( - rps.sp, - rps.branch, - state, - rps.mult_sp, - mult_repr(next), - rps.prob_branch, - next, - ) -end - -Base.eltype(_::StratNodeReprPeriods) = StratNodeReprPeriod - -""" - struct StratNodeReprOpscenario{T} <: AbstractOperationalScenario{T} - -A structure representing a single operational scenario for a representative period in A -[`TwoLevelTree`] structure supporting iteration over its time periods. -""" -struct StratNodeReprOpscenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} - sp::Int - branch::Int - rp::Int - opscen::Int - mult_sp::Float64 - mult_rp::Float64 - prob_branch::Float64 - prob_scen::Float64 - operational::OP -end - -_opscen(osc::StratNodeReprOpscenario) = osc.opscen -_rper(osc::StratNodeReprOpscenario) = osc.rp -_branch(osc::StratNodeReprOpscenario) = osc.branch -_strat_per(osc::StratNodeReprOpscenario) = osc.sp - -probability(osc::StratNodeReprOpscenario) = osc.prob_branch * osc.prob_scen -probability_branch(osc::StratNodeReprOpscenario) = osc.prob_branch - -StrategicTreeIndexable(::Type{<:StratNodeReprOpscenario}) = HasStratTreeIndex() -StrategicIndexable(::Type{<:StratNodeReprOpscenario}) = HasStratIndex() -function RepresentativeIndexable(::Type{<:StratNodeReprOpscenario}) - return HasReprIndex() -end -ScenarioIndexable(::Type{<:StratNodeReprOpscenario}) = HasScenarioIndex() - -# Adding methods to existing Julia functions -function Base.show(io::IO, osc::StratNodeReprOpscenario) - return print(io, "sp$(osc.sp)-br$(osc.branch)-rp$(osc.rp)-sc$(osc.opscen)") -end -Base.eltype(_::StratNodeReprOpscenario) = TreePeriod - -""" - struct StratNodeReprOpscenarios <: AbstractTreeStructure - -Type for iterating through the individual operational scenarios of a -[`StratNodeReprPeriod`](@ref). It is automatically created through the function -[`opscenarios`](@ref). -""" -struct StratNodeReprOpscenarios <: AbstractTreeStructure - sp::Int - branch::Int - rp::Int - mult_sp::Float64 - mult_rp::Float64 - prob_branch::Float64 - opscens::Any -end - -function StratNodeReprOpscenarios( - rp::StratNodeReprPeriod{T,OP}, - opscens, -) where {T,OP<:TimeStructure{T}} - return StratNodeReprOpscenarios( - _strat_per(rp), - _branch(rp), - _rper(rp), - rp.mult_sp, - rp.mult_rp, - probability_branch(rp), - opscens, - ) -end - -""" - opscenarios(sp::StratNodeReprPeriod{T,RepresentativePeriod{T,OP}}) - -Iterator that iterates over operational scenarios for a specific representative period of -a strategic node in the tree. -""" -function opscenarios( - rp::StratNodeReprPeriod{T,RepresentativePeriod{T,OP}}, -) where {T,OP} - if _strat_per(rp) == 1 && _rper(rp) == 1 - end - return StratNodeReprOpscenarios( - _strat_per(rp), - _branch(rp), - _rper(rp), - rp.mult_sp, - rp.mult_rp, - probability_branch(rp), - opscenarios(rp.operational.operational), - ) -end -function opscenarios( - rp::StratNodeReprPeriod{T,SingleReprPeriod{T,OP}}, -) where {T,OP} - if _strat_per(rp) == 1 && _rper(rp) == 1 - end - return StratNodeOpScens( - _strat_per(rp), - _branch(rp), - rp.mult_sp, - probability_branch(rp), - opscenarios(rp.operational.ts), - ) -end - -_oper_struct(oscs::StratNodeReprOpscenarios) = oscs.opscens -function strat_node_period(oscs::StratNodeReprOpscenarios, next, state) - return StratNodeReprOpscenario( - oscs.sp, - oscs.branch, - oscs.rp, - state, - oscs.mult_sp, - oscs.mult_rp, - oscs.prob_branch, - probability(next), - next, - ) -end - -Base.eltype(_::StratNodeReprOpscenarios) = StratNodeReprOpscenario - -# All introduced subtypes require the same procedures for the iteration and indexing. -# Hence, all introduced types use the same functions. -TreeStructure = Union{ - StratNodeOperationalScenario, - StratNodeReprPeriod, - StratNodeReprOpscenario, -} -Base.length(ts::TreeStructure) = length(ts.operational) -function Base.last(ts::TreeStructure) - per = last(ts.operational) - return TreePeriod(ts, per) -end - -function Base.getindex(ts::TreeStructure, index) - per = ts.operational[index] - return TreePeriod(ts, per) -end -function Base.eachindex(ts::TreeStructure) - return eachindex(ts.operational) -end -function Base.iterate(ts::TreeStructure, state = nothing) - next = - isnothing(state) ? iterate(ts.operational) : - iterate(ts.operational, state) - isnothing(next) && return nothing - - return TreePeriod(ts, next[1]), next[2] -end diff --git a/src/utils.jl b/src/utils.jl index 1a37bfa..87c55a6 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -25,6 +25,18 @@ function Base.iterate(w::WithPrev, state) return (isfirst(n[1]) ? nothing : state[1], n[1]), (n[1], n[2]) end +function Base.iterate(w::WithPrev{StratTreeNodes{T,OP}}) where {T,OP} + n = iterate(w.itr) + n === nothing && return n + return (nothing, n[1]), (n[1], n[2]) +end + +function Base.iterate(w::WithPrev{StratTreeNodes{T,OP}}, state) where {T,OP} + n = iterate(w.itr, state[2]) + n === nothing && return n + return (n[1].parent, n[1]), (n[1], n[2]) +end + struct Chunk{I} itr::I ns::Int @@ -126,3 +138,32 @@ function start_oper_time(t::TimePeriod, ts::TimeStructure) end function expand_dataframe!(df, periods) end + +# All introduced subtypes require the same procedures for the iteration and indexing. +# Hence, all introduced types use the same functions. +TreeStructure = Union{ + StratNodeOperationalScenario, + StratNodeReprPeriod, + StratNodeReprOpScenario, +} +Base.length(ts::TreeStructure) = length(ts.operational) +function Base.last(ts::TreeStructure) + per = last(ts.operational) + return TreePeriod(ts, per) +end + +function Base.getindex(ts::TreeStructure, index) + per = ts.operational[index] + return TreePeriod(ts, per) +end +function Base.eachindex(ts::TreeStructure) + return eachindex(ts.operational) +end +function Base.iterate(ts::TreeStructure, state = nothing) + next = + isnothing(state) ? iterate(ts.operational) : + iterate(ts.operational, state) + isnothing(next) && return nothing + + return TreePeriod(ts, next[1]), next[2] +end From 8c6f1ddd48e1a18b9f6a03f49736143d94f9a98a Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 11:16:38 +0200 Subject: [PATCH 13/20] Multiple minor structural changes --- src/calendar.jl | 3 +- src/op_scenarios/opscenarios.jl | 13 ++++--- src/op_scenarios/rep_periods.jl | 15 ++++---- src/op_scenarios/strat_periods.jl | 45 ++++++++++++------------ src/op_scenarios/tree_periods.jl | 1 - src/representative/core_types.jl | 11 +++--- src/representative/rep_periods.jl | 53 ++++++++++++----------------- src/representative/strat_periods.jl | 46 +++++++++++++------------ src/representative/tree_periods.jl | 2 -- src/simple.jl | 2 +- src/strategic/core_types.jl | 12 ++++--- src/strategic/strat_periods.jl | 18 +++++----- 12 files changed, 104 insertions(+), 117 deletions(-) diff --git a/src/calendar.jl b/src/calendar.jl index f9e662a..cd6d6af 100644 --- a/src/calendar.jl +++ b/src/calendar.jl @@ -1,4 +1,3 @@ - """ struct CalendarTimes{T} <: TimeStructure{T} @@ -70,6 +69,7 @@ _total_duration(ts::CalendarTimes) = sum(duration(t) for t in ts) # Add basic functions of iterators Base.length(ts::CalendarTimes) = ts.length +Base.eltype(::Type{CalendarTimes{T}}) where {T} = CalendarPeriod{T} function Base.iterate(ts::CalendarTimes) return CalendarPeriod(ts.start_date, ts.start_date + ts.period, 1), (1, ts.start_date) @@ -80,7 +80,6 @@ function Base.iterate(ts::CalendarTimes, state) return CalendarPeriod(start_time, start_time + ts.period, state[1] + 1), (state[1] + 1, start_time) end -Base.eltype(::Type{CalendarTimes{T}}) where {T} = CalendarPeriod{T} function Base.getindex(ts::CalendarTimes, index) start_time = ts.start_date + (index - 1) * ts.period return CalendarPeriod(start_time, start_time + ts.period, index) diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 2f1e649..2e735f7 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -1,4 +1,3 @@ - """ AbstractOperationalScenario{T} <: TimeStructure{T} @@ -166,7 +165,7 @@ Type for iterating through the individual operational scenarios of a function [`opscenarios`](@ref). """ struct OpScens{T,OP} - ts::OperationalScenarios{T,OP} + opscens::OperationalScenarios{T,OP} end """ @@ -179,14 +178,14 @@ opscenarios(ts::OperationalScenarios) = OpScens(ts) function OperationalScenario(oscs::OpScens, per::Int) return OperationalScenario( per, - oscs.ts.probability[per], - _multiple_adj(oscs.ts, per), - oscs.ts.scenarios[per], + oscs.opscens.probability[per], + _multiple_adj(oscs.opscens, per), + oscs.opscens.scenarios[per], ) end # Add basic functions of iterators -Base.length(oscs::OpScens) = oscs.ts.len +Base.length(oscs::OpScens) = oscs.opscens.len function Base.eltype(_::Type{OpScens{T,OP}}) where {T,OP<:TimeStructure{T}} return OperationalScenario{T,OP} end @@ -200,6 +199,6 @@ function Base.getindex(oscs::OpScens, index::Int) return OperationalScenario(oscs, index) end function Base.eachindex(oscs::OpScens) - return eachindex(oscs.ts.scenarios) + return eachindex(oscs.opscens.scenarios) end Base.last(oscs::OpScens) = OperationalScenario(oscs, length(oscs)) diff --git a/src/op_scenarios/rep_periods.jl b/src/op_scenarios/rep_periods.jl index cbcd38d..ff2534d 100644 --- a/src/op_scenarios/rep_periods.jl +++ b/src/op_scenarios/rep_periods.jl @@ -70,15 +70,14 @@ function [`opscenarios`](@ref). struct RepOpScens{OP} rp::Int mult::Float64 - op_scens::OP + opscens::OP end _rper(oscs::RepOpScens) = oscs.rp """ - opscenarios(rep::RepresentativePeriod) - -Iterator that iterates over operational scenarios in a `RepresentativePeriod` time structure. +When the `TimeStructure` is a [`RepresentativePeriod`](@ref) with [`OperationalScenarios`](@ref), +`opscenarios` returns the iterator [`RepOpScens`](@ref). """ function opscenarios( rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}, @@ -91,14 +90,14 @@ function ReprOperationalScenario(oscs::RepOpScens, state::Int) return ReprOperationalScenario( _rper(oscs), state, - oscs.op_scens.probability[state], + oscs.opscens.probability[state], oscs.mult, - _multiple_adj(oscs.op_scens, state), - oscs.op_scens.scenarios[state] + _multiple_adj(oscs.opscens, state), + oscs.opscens.scenarios[state] ) end -Base.length(oscs::RepOpScens) = length(oscs.op_scens.scenarios) +Base.length(oscs::RepOpScens) = length(oscs.opscens.scenarios) function Base.eltype(_::RepOpScens{SC}) where {T,OP,SC<:OperationalScenarios{T,OP}} return ReprOperationalScenario{T,OP} end diff --git a/src/op_scenarios/strat_periods.jl b/src/op_scenarios/strat_periods.jl index e4e4412..82938bb 100644 --- a/src/op_scenarios/strat_periods.jl +++ b/src/op_scenarios/strat_periods.jl @@ -82,17 +82,6 @@ When the `TimeStructure` is a [`StrategicPeriod`](@ref), `opscenarios` returns t function opscenarios(sp::StrategicPeriod{S,T,OP}) where {S,T,OP} return StratOpScens(_strat_per(sp), mult_strat(sp), opscenarios(sp.operational)) end - -""" -When the `TimeStructure` is a [`TwoLevel`](@ref), `opscenarios` returns a vector of -[`StratOperationalScenario`](@ref)s. -""" -function opscenarios(ts::TwoLevel{S,T,OP}) where {S,T,OP} - return collect( - Iterators.flatten(opscenarios(sp) for sp in strategic_periods(ts)), - ) -end - # Provide a constructor to simplify the design function StratOperationalScenario(oscs::StratOpScens, scen::Int, per) return StratOperationalScenario( @@ -234,18 +223,6 @@ function opscenarios( ) end -""" -When the `TimeStructure` is a [`TwoLevel`](@ref) with [`RepresentativePeriods`](@ref), -`opscenarios` returns a vector of [`StratReprOpscenario`](@ref)s. -""" -function opscenarios( - ts::TwoLevel{S1,T,RepresentativePeriods{S2,T,OP}}, -) where {S1,S2,T,OP} - return collect( - Iterators.flatten(opscenarios(rp) for sp in strategic_periods(ts) for rp in repr_periods(sp)), - ) -end - # Provide a constructor to simplify the design function StratReprOpscenario(oscs::StratReprOpscenarios, scen, per) return StratReprOpscenario( @@ -283,3 +260,25 @@ function Base.last(oscs::StratReprOpscenarios) per = last(oscs.opscens) return StratReprOpscenario(oscs, _opscen(per), per) end + +""" +When the `TimeStructure` is a [`TwoLevel`](@ref), `opscenarios` returns a vector of +[`StratOperationalScenario`](@ref)s. +""" +function opscenarios(ts::TwoLevel{S,T,OP}) where {S,T,OP} + return collect( + Iterators.flatten(opscenarios(sp) for sp in strategic_periods(ts)), + ) +end + +""" +When the `TimeStructure` is a [`TwoLevel`](@ref) with [`RepresentativePeriods`](@ref), +`opscenarios` returns a vector of [`StratReprOpscenario`](@ref)s. +""" +function opscenarios( + ts::TwoLevel{S1,T,RepresentativePeriods{S2,T,OP}}, +) where {S1,S2,T,OP} + return collect( + Iterators.flatten(opscenarios(rp) for sp in strategic_periods(ts) for rp in repr_periods(sp)), + ) +end diff --git a/src/op_scenarios/tree_periods.jl b/src/op_scenarios/tree_periods.jl index 5ec40da..1d79463 100644 --- a/src/op_scenarios/tree_periods.jl +++ b/src/op_scenarios/tree_periods.jl @@ -1,4 +1,3 @@ - """ struct StratNodeOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} diff --git a/src/representative/core_types.jl b/src/representative/core_types.jl index 6a52bf7..b0e8c16 100644 --- a/src/representative/core_types.jl +++ b/src/representative/core_types.jl @@ -64,7 +64,7 @@ struct RepresentativePeriods{S<:Duration,T,OP<:TimeStructure{T}} <: "The length of `rep_periods` cannot be less than the field `len` of `RepresentativePeriods`.", ), ) - elseif sum(period_share) > 1 || sum(period_share) < 1 + elseif sum(period_share) > 1+1e-6 || sum(period_share) < 1-1e-6 @warn( "The sum of the `period_share` vector is given by $(sum(period_share)). " * "This can lead to unexpected behaviour." @@ -139,6 +139,9 @@ end function Base.length(rpers::RepresentativePeriods) return sum(length(rp) for rp in rpers.rep_periods) end +function Base.eltype(::Type{RepresentativePeriods{S,T,OP}}) where {S,T,OP} + return ReprPeriod{eltype(OP)} +end function Base.iterate(rpers::RepresentativePeriods, state = (nothing, 1)) rp = state[2] next = @@ -153,9 +156,6 @@ function Base.iterate(rpers::RepresentativePeriods, state = (nothing, 1)) end return ReprPeriod(rpers, next[1], rp), (next[2], rp) end -function Base.eltype(::Type{RepresentativePeriods{S,T,OP}}) where {S,T,OP} - return ReprPeriod{eltype(OP)} -end function Base.last(rpers::RepresentativePeriods) per = last(rpers.rep_periods[rpers.len]) return ReprPeriod(rpers, per, rpers.len) @@ -183,11 +183,12 @@ duration(t::ReprPeriod) = duration(t.period) multiple(t::ReprPeriod) = t.mult probability(t::ReprPeriod) = probability(t.period) -Base.show(io::IO, rp::ReprPeriod) = print(io, "rp$(rp.rp)-$(rp.period)") +Base.show(io::IO, t::ReprPeriod) = print(io, "rp$(t.rp)-$(t.period)") function Base.isless(t1::ReprPeriod, t2::ReprPeriod) return t1.rp < t2.rp || (t1.rp == t2.rp && t1.period < t2.period) end +# Convenience constructor for the type function ReprPeriod(rpers::RepresentativePeriods, per::TimePeriod, rp::Int) mult = _multiple_adj(rpers, rp) * multiple(per) return ReprPeriod(rp, per, mult) diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl index 7d247c6..22b6661 100644 --- a/src/representative/rep_periods.jl +++ b/src/representative/rep_periods.jl @@ -1,16 +1,16 @@ - """ AbstractRepresentativePeriod{T} <: TimeStructure{T} Abstract type used for time structures that represent a representative period. These periods are obtained when iterating through the representative periods of a time -structure declared using the function [`repr_period`](@ref.) +structure declared by the function [`repr_period`](@ref.) """ abstract type AbstractRepresentativePeriod{T} <: TimeStructure{T} end function _rper(rp::AbstractRepresentativePeriod) return error("_rper() not implemented for $(typeof(rp))") end + isfirst(rp::AbstractRepresentativePeriod) = _rper(rp) == 1 mult_repr(rp::AbstractRepresentativePeriod) = 1 @@ -66,39 +66,28 @@ struct SingleReprPeriodWrapper{T,OP<:TimeStructure{T}} <: TimeStructure{T} ts::OP end -""" - repr_periods(ts::TimeStructure) - -This function returns a type for iterating through the individual representative -periods of a `TimeStructure`. The type of the iterator is dependent on the type of the -input `TimeStructure`. - -When the `TimeStructure` is a `TimeStructure`, `repr_periods` returns a -[`SingleReprPeriodWrapper`](@ref). This coresponds to the default behavior. -""" -repr_periods(ts::TimeStructure) = SingleReprPeriodWrapper(ts) - # Add basic functions of iterators Base.length(rpers::SingleReprPeriodWrapper) = 1 - +function Base.eltype(::Type{SingleReprPeriodWrapper{T,OP}}) where {T,OP} + return SingleReprPeriod{T,OP} +end function Base.iterate(rpers::SingleReprPeriodWrapper, state = nothing) !isnothing(state) && return nothing return SingleReprPeriod(rpers.ts), 1 end -function Base.eltype(::Type{SingleReprPeriodWrapper{T,OP}}) where {T,OP} - return SingleReprPeriod{T,OP} -end Base.last(rpers::SingleReprPeriodWrapper) = SingleReprPeriod(rpers.ts) """ -When the `TimeStructure` is a [`TwoLevel`](@ref), `repr_periods` returns an `Array` of -all [`StratReprPeriod`](@ref)s. + repr_periods(ts::TimeStructure) + +This function returns a type for iterating through the individual representative +periods of a `TimeStructure`. The type of the iterator is dependent on the type of the +input `TimeStructure`. + +When the `TimeStructure` is a `TimeStructure`, `repr_periods` returns a +[`SingleReprPeriodWrapper`](@ref). This corresponds to the default behavior. """ -function repr_periods(ts::TwoLevel) - return collect( - Iterators.flatten(repr_periods(sp) for sp in strategic_periods(ts)), - ) -end +repr_periods(ts::TimeStructure) = SingleReprPeriodWrapper(ts) """ RepresentativePeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} @@ -130,6 +119,9 @@ end # Add basic functions of iterators Base.length(rp::RepresentativePeriod) = length(rp.operational) +function Base.eltype(_::Type{RepresentativePeriod{T,OP}}) where {T,OP} + return ReprPeriod{eltype(OP)} +end function Base.iterate(rp::RepresentativePeriod, state = nothing) next = isnothing(state) ? iterate(rp.operational) : @@ -138,9 +130,6 @@ function Base.iterate(rp::RepresentativePeriod, state = nothing) return ReprPeriod(rp, next[1]), next[2] end -function Base.eltype(_::Type{RepresentativePeriod{T,OP}}) where {T,OP} - return ReprPeriod{eltype(OP)} -end function Base.getindex(rp::RepresentativePeriod, index::Int) per = rp.operational[index] return ReprPeriod(rp, per) @@ -154,7 +143,7 @@ function Base.last(rp::RepresentativePeriod) end """ - ReprPeriods{T,OP} + ReprPeriods{S,T,OP} Type for iterating through the individual representative periods of a [`RepresentativePeriods`](@ref) time structure. It is automatically created through the @@ -181,15 +170,15 @@ end # Add basic functions of iterators Base.length(rpers::ReprPeriods) = rpers.ts.len +function Base.eltype(_::ReprPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return RepresentativePeriod{T,OP} +end function Base.iterate(rpers::ReprPeriods, state = nothing) per = isnothing(state) ? 1 : state + 1 per > length(rpers) && return nothing return RepresentativePeriod(rpers, per), per end -function Base.eltype(_::ReprPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} - return RepresentativePeriod{T,OP} -end function Base.getindex(rpers::ReprPeriods, index::Int) return RepresentativePeriod(rpers, index) end diff --git a/src/representative/strat_periods.jl b/src/representative/strat_periods.jl index b4156ee..c00efb4 100644 --- a/src/representative/strat_periods.jl +++ b/src/representative/strat_periods.jl @@ -1,4 +1,3 @@ - """ StratReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} @@ -19,22 +18,23 @@ _strat_per(rp::StratReprPeriod) = rp.sp multiple(rp::StratReprPeriod, t::OperationalPeriod) = t.multiple / rp.mult_sp mult_repr(rp::StratReprPeriod) = rp.mult_rp +mult_strat(rp::StratReprPeriod) = rp.mult_sp StrategicIndexable(::Type{<:StratReprPeriod}) = HasStratIndex() -function Base.show(io::IO, rp::StratReprPeriod) - return print(io, "sp$(_strat_per(rp))-rp$(_rper(rp))") -end -Base.eltype(_::Type{StratReprPeriod{T,OP}}) where {T,OP} = OperationalPeriod -Base.length(rp::StratReprPeriod) = length(rp.operational) - # Provide a constructor to simplify the design function OperationalPeriod(rp::StratReprPeriod, per) - mult = rp.mult_sp * multiple(per) - return OperationalPeriod(rp.sp, per, mult) + mult = mult_strat(rp) * multiple(per) + return OperationalPeriod(_strat_per(rp), per, mult) +end + +function Base.show(io::IO, rp::StratReprPeriod) + return print(io, "sp$(_strat_per(rp))-rp$(_rper(rp))") end -# Iterate the time periods of a StratReprPeriod +# Add basic functions of iterators +Base.length(rp::StratReprPeriod) = length(rp.operational) +Base.eltype(_::Type{StratReprPeriod{T,OP}}) where {T,OP} = OperationalPeriod function Base.iterate(rp::StratReprPeriod, state = nothing) next = isnothing(state) ? iterate(rp.operational) : @@ -68,28 +68,21 @@ struct StratReprPeriods{OP} repr::OP end -function StratReprPeriods( - sp::StrategicPeriod{S,T,OP}, - repr, -) where {S,T,OP<:TimeStructure{T}} - return StratReprPeriods(_strat_per(sp), mult_strat(sp), repr) -end - """ When the `TimeStructure` is a [`StrategicPeriod`](@ref), `repr_periods` returns the iterator [`StratReprPeriods`](@ref). """ -function repr_periods(sp::StrategicPeriod) - return StratReprPeriods(sp, repr_periods(sp.operational)) +function repr_periods(sp::StrategicPeriod{S,T,OP}) where {S,T,OP} + return StratReprPeriods(_strat_per(sp), mult_strat(sp), repr_periods(sp.operational)) end -Base.length(rpers::StratReprPeriods) = length(rpers.repr) # Provide a constructor to simplify the design function StratReprPeriod(rpers::StratReprPeriods, state, per) return StratReprPeriod(rpers.sp, state, rpers.mult_sp, mult_repr(per), per) end -# Iterate the time periods of a StratReprPeriods +# Add basic functions of iterators +Base.length(rpers::StratReprPeriods) = length(rpers.repr) function Base.iterate(rpers::StratReprPeriods, state = (nothing, 1)) next = isnothing(state[1]) ? iterate(rpers.repr) : @@ -102,7 +95,7 @@ function Base.getindex(rpers::StratReprPeriods, index::Int) return StratReprPeriod(rpers, index) end function Base.eachindex(rpers::StratReprPeriods) - return eachindex(rpers.rep_periods) + return eachindex(rpers.repr) end function Base.last(rpers::StratReprPeriods) per = last(rpers.repr) @@ -114,3 +107,12 @@ When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `repr_periods` re correct behavior based on the substructure. """ repr_periods(ts::SingleStrategicPeriod) = repr_periods(ts.ts) +""" +When the `TimeStructure` is a [`TwoLevel`](@ref), `repr_periods` returns an `Array` of +all [`StratReprPeriod`](@ref)s. +""" +function repr_periods(ts::TwoLevel) + return collect( + Iterators.flatten(repr_periods(sp) for sp in strategic_periods(ts)), + ) +end diff --git a/src/representative/tree_periods.jl b/src/representative/tree_periods.jl index a1b4c56..9cdcf9a 100644 --- a/src/representative/tree_periods.jl +++ b/src/representative/tree_periods.jl @@ -1,5 +1,3 @@ - - """ struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} diff --git a/src/simple.jl b/src/simple.jl index ea8ded9..ea5171b 100644 --- a/src/simple.jl +++ b/src/simple.jl @@ -40,6 +40,7 @@ _total_duration(st::SimpleTimes) = sum(st.duration) # Add basic functions of iterators Base.length(st::SimpleTimes) = st.len +Base.eltype(::Type{SimpleTimes{T}}) where {T} = SimplePeriod{T} function Base.iterate(itr::SimpleTimes{T}, state = nothing) where {T} next = isnothing(state) ? 1 : state + 1 next > itr.len && return nothing @@ -55,7 +56,6 @@ end function Base.last(ts::SimpleTimes{T}) where {T} return SimplePeriod{T}(ts.len, ts.duration[ts.len]) end -Base.eltype(::Type{SimpleTimes{T}}) where {T} = SimplePeriod{T} """ struct SimplePeriod{T<:Number} <: TimePeriod diff --git a/src/strategic/core_types.jl b/src/strategic/core_types.jl index 2e63ad7..cdc8d89 100644 --- a/src/strategic/core_types.jl +++ b/src/strategic/core_types.jl @@ -176,6 +176,7 @@ end function Base.length(ts::TwoLevel) return sum(length(op) for op in ts.operational) end +Base.eltype(::Type{TwoLevel{S,T,OP}}) where {S,T,OP} = OperationalPeriod function Base.iterate(ts::TwoLevel, state = (nothing, 1)) sp = state[2] next = @@ -190,7 +191,6 @@ function Base.iterate(ts::TwoLevel, state = (nothing, 1)) end return OperationalPeriod(ts, next[1], sp), (next[2], sp) end -Base.eltype(::Type{TwoLevel{S,T,OP}}) where {S,T,OP} = OperationalPeriod function Base.last(ts::TwoLevel) per = last(ts.operational[ts.len]) return OperationalPeriod(ts, per, ts.len) @@ -208,10 +208,6 @@ struct OperationalPeriod <: TimePeriod period::TimePeriod multiple::Float64 end -function OperationalPeriod(ts::TwoLevel, per::TimePeriod, sp::Int) - mult = _multiple_adj(ts, sp) * multiple(per) - return OperationalPeriod(sp, per, mult) -end _oper(t::OperationalPeriod) = _oper(t.period) _opscen(t::OperationalPeriod) = _opscen(t.period) @@ -229,3 +225,9 @@ end function Base.isless(t1::OperationalPeriod, t2::OperationalPeriod) return t1.sp < t2.sp || (t1.sp == t2.sp && t1.period < t2.period) end + +# Convenience constructor for the type +function OperationalPeriod(ts::TwoLevel, per::TimePeriod, sp::Int) + mult = _multiple_adj(ts, sp) * multiple(per) + return OperationalPeriod(sp, per, mult) +end diff --git a/src/strategic/strat_periods.jl b/src/strategic/strat_periods.jl index 78686bf..e437c1b 100644 --- a/src/strategic/strat_periods.jl +++ b/src/strategic/strat_periods.jl @@ -1,16 +1,16 @@ - """ AbstractStrategicPeriod{S,T} <: TimeStructure{T} Abstract type used for time structures that represent a strategic period. These periods are obtained when iterating through the strategic periods of a time -structure declared using the function [`strat_periods`](@ref.) +structure declared by the function [`strat_periods`](@ref.) """ abstract type AbstractStrategicPeriod{S,T} <: TimeStructure{T} end function _strat_per(sp::AbstractStrategicPeriod) return error("_strat_per() not implemented for $(typeof(sp))") end + isfirst(sp::AbstractStrategicPeriod) = _strat_per(sp) == 1 mult_strat(sp::AbstractStrategicPeriod) = 1 function duration_strat(sp::AbstractStrategicPeriod) @@ -67,11 +67,11 @@ duration_strat(sp::SingleStrategicPeriod) = _total_duration(sp.ts) # Add basic functions of iterators Base.length(sp::SingleStrategicPeriod) = length(sp.ts) +Base.eltype(::Type{SingleStrategicPeriod{T,SP}}) where {T,SP} = eltype(SP) function Base.iterate(sp::SingleStrategicPeriod, state = nothing) next = isnothing(state) ? iterate(sp.ts) : iterate(sp.ts, state) return next end -Base.eltype(::Type{SingleStrategicPeriod{T,SP}}) where {T,SP} = eltype(SP) Base.last(sp::SingleStrategicPeriod) = last(sp.ts) """ @@ -142,6 +142,9 @@ end # Add basic functions of iterators Base.length(sp::StrategicPeriod) = length(sp.operational) +function Base.eltype(_::Type{StrategicPeriod{S,T,OP}}) where {S,T,OP} + return OperationalPeriod +end function Base.iterate(sp::StrategicPeriod, state = nothing) next = isnothing(state) ? iterate(sp.operational) : @@ -150,9 +153,6 @@ function Base.iterate(sp::StrategicPeriod, state = nothing) return OperationalPeriod(sp, next[1]), next[2] end -function Base.eltype(_::Type{StrategicPeriod{S,T,OP}}) where {S,T,OP} - return OperationalPeriod -end function Base.getindex(sp::StrategicPeriod, index::Int) per = sp.operational[index] return OperationalPeriod(sp, per) @@ -220,15 +220,15 @@ end # Add basic functions of iterators Base.length(sps::StratPeriods) = sps.ts.len +function Base.eltype(_::StratPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} + return StrategicPeriod{S,T,OP} +end function Base.iterate(sps::StratPeriods, state = nothing) sp = isnothing(state) ? 1 : state + 1 sp > length(sps) && return nothing return StrategicPeriod(sps, sp), sp end -function Base.eltype(_::StratPeriods{S,T,OP}) where {S,T,OP<:TimeStructure{T}} - return StrategicPeriod{S,T,OP} -end function Base.getindex(sps::StratPeriods, index::Int) return StrategicPeriod(sps, index) end From 51bc39059cec2686ed522e6377dda469f7d0c4ea Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 11:29:56 +0200 Subject: [PATCH 14/20] First take on docs --- docs/make.jl | 2 +- docs/src/index.md | 8 ++---- docs/src/manual/basic.md | 50 +++++++++++++++++++--------------- docs/src/manual/discount.md | 14 ++++++---- docs/src/manual/iteration.md | 22 +++++++++------ docs/src/manual/multi.md | 25 +++++++++-------- docs/src/manual/profiles.md | 11 ++++---- docs/src/reference/api.md | 5 ---- docs/src/reference/internal.md | 9 ++---- 9 files changed, 76 insertions(+), 70 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 3d61d26..2d232e0 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -28,7 +28,7 @@ Documenter.makedocs( assets = String[], ), doctest = false, - #modules = [TimeStruct], + modules = [TimeStruct], pages = pages, ) diff --git a/docs/src/index.md b/docs/src/index.md index bbd9dd8..c150252 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -2,7 +2,6 @@ Welcome to the documentation of the TimeStruct package! - ## What is TimeStruct? TimeStruct is a Julia package that supports the efficient development of optimization models with multi-horizon time modelling. The package is designed to be used in combination with the JuMP package for optimization modeling in Julia. @@ -24,6 +23,7 @@ The main concept is a [`TimeStructure`](@ref) which is an abstract type that ena ## How to get started The package is registered in the general registry and can be installed in standard fashion + ```julia ] add TimeStruct ``` @@ -31,11 +31,12 @@ The package is registered in the general registry and can be installed in standa This documentation consists of a manual explaining concepts and giving examples as well as a complete API reference. - ## Cite + If you find TimeStruct useful in your work, we kindly request that you cite the following: ``` + @misc{TimeStruct.jl, author = {Flatberg, Truls and Hellemo, Lars}, title = {{TimeStruct.jl: Flexible time structures in optimization modelling}}, @@ -47,9 +48,6 @@ following: } ``` - ## Acknowledgements This material is based upon work supported by the Research Council of Norway through the projects ZeroKyst (328721), MaritimeNH3 (328679) and CleanExport (308811). - - diff --git a/docs/src/manual/basic.md b/docs/src/manual/basic.md index 56bc2f4..62c9858 100644 --- a/docs/src/manual/basic.md +++ b/docs/src/manual/basic.md @@ -6,7 +6,7 @@ using TimeStruct ## SimpleTimes -The basic time structure is [`SimpleTimes`](@ref) which represents a continuous period of time divided into individual time periods of varying duration. +The basic time structure is [`SimpleTimes`](@ref) which represents a continuous period of time divided into individual time periods of varying duration. The length of each time period is obtained by the [`duration(t)`](@ref) function. ```@repl ts @@ -16,21 +16,23 @@ durations = [duration(t) for t in periods] ![Illustration of SimpleTimes](./../figures/simple.png) -## Calendar based +## Calendar based -For some applications it is required to relate the time periods to actual calendar dates. +For some applications it is required to relate the time periods to actual calendar dates. This is supported by the time structure [`CalendarTimes`](@ref) that alllows for creation and iteration of a calendar based sequence of periods in combination with calendar arithmetic. The following example shows the creation of a time structure with 12 months starting from the first of January 2024. The duration of each time period is given in hours by default, but it is possible to specify the time units to use by providing the period type to use: + ```@repl ts using Dates year = CalendarTimes(DateTime(2024, 1, 1), 12, Month(1)); duration(first(year); dfunc = Dates.Day) ``` -You can also make the time structure for a specific time zone as shown in the following +You can also make the time structure for a specific time zone as shown in the following example with 3 days in the end of March with a transition to summer time on the second day: + ```@repl ts using TimeZones periods = CalendarTimes(DateTime(2023, 3, 25), tz"Europe/Berlin", 3, Day(1)); @@ -38,24 +40,27 @@ duration.(periods) ``` ## Representative periods -In some cases, a fine-scale representation for the operations of the infrastructure of the whole time horizon, is not feasible. A possible strategy is then to select one or more representative periods and use them to evaluate operational cost and feasibility. The time structure [`RepresentativePeriods`](@ref) consists of an ordered sequence of representative periods that represents a longer period of time. Each -representative period covers a specified share of the whole time period. + +In some cases, a fine-scale representation for the operations of the infrastructure of the whole time horizon, is not feasible. A possible strategy is then to select one or more representative periods and use them to evaluate operational cost and feasibility. The time structure [`RepresentativePeriods`](@ref) consists of an ordered sequence of representative periods that represents a longer period of time. Each +representative period covers a specified share of the whole time period. The following example shows an example with a year with daily resolution represented by two weeks -with a share of 0.7 and 0.3 respectively. +with a share of 0.7 and 0.3 respectively. ```@repl rp using JuMP, TimeStruct periods = RepresentativePeriods( - 2, - 365, - [0.7, 0.3], + 2, + 365, + [0.7, 0.3], [SimpleTimes(7,1), SimpleTimes(7,1)] ); ``` -The time periods can be iterated both for the whole time structure and individually by each representative period using the [`repr_periods`](@ref) function. This is illustrated here + +The time periods can be iterated both for the whole time structure and individually by each representative period using the [`repr_periods`](@ref) function. This is illustrated here when setting up a JuMP model with a separate constraint for each representative period: + ```@repl rp m = Model(); @variable(m, prod[periods] >= 0); @@ -66,42 +71,43 @@ end @constraint(m, sum(prod[t] * multiple(t) for t in periods) <= 1); ``` + For each time period the [`multiple`](@ref) function returns how many times the given period should be counted when aggregating to the whole [`RepresentativePeriods`](@ref) structure. This will take into account both the duration and share of each representative period, thus -we have that: +we have that: + ```@repl rp sum(duration(t) * multiple(t) for t in periods) duration(periods) ``` + ## Operational scenarios -Operations often face uncertain operating conditions. In energy systems modeling, a typical example is the availability of wind and solar power. -One method for accounting for this uncertainty is to have multiple operational scenarios that are used to evaluate the cost and feasibility of +Operations often face uncertain operating conditions. In energy systems modeling, a typical example is the availability of wind and solar power. +One method for accounting for this uncertainty is to have multiple operational scenarios that are used to evaluate the cost and feasibility of operations, where each scenario has a given probability of occurring. -The time structure [`OperationalScenarios`](@ref) represents an unordered collection of -operational scenarios where each scenario has a separate time structure and an associated +The time structure [`OperationalScenarios`](@ref) represents an unordered collection of +operational scenarios where each scenario has a separate time structure and an associated probability. ```@repl os using TimeStruct, JuMP scenarios = OperationalScenarios( - 3, - [SimpleTimes(5,1), SimpleTimes(7,2), SimpleTimes(10,1)], + 3, + [SimpleTimes(5,1), SimpleTimes(7,2), SimpleTimes(10,1)], [0.3, 0.2, 0.5] ); ``` ![Illustration of OperationalScenarios](./../figures/scenario.png) - Similar to representative periods, each period has a [`multiple`](@ref) that is defined relative to the maximum duration for all scenarios. In addition, each time period has a [`probability`](@ref)equal to the probability of its scenario. Thus we have that: + ```@repl os -sum(duration(t) * probability(t) * multiple(t) for t in scenarios) +sum(duration(t) * probability(t) * multiple(t) for t in scenarios) duration(scenarios) ``` - - diff --git a/docs/src/manual/discount.md b/docs/src/manual/discount.md index e8bd398..c3de796 100644 --- a/docs/src/manual/discount.md +++ b/docs/src/manual/discount.md @@ -2,27 +2,29 @@ For multi-year investment optimization models it is common pratice to use an objective that is discounted to get the net present value of the investment. -Since investment decisions usually are done on a strategic level, discount -factors are also calculated based on strategic periods. +Since investment decisions usually are done on a strategic level, discount +factors are also calculated based on strategic periods. The discount factor for a time period `t` is found by the [`discount`](@ref) function. There are two strategies for calculating the discount factor, either all discounting is calculated based on the start of the strategic period -or it is based on finding an approximation of the average value over the +or it is based on finding an approximation of the average value over the strategic period. The following example shows how these two types will -differ for a planning period of 50 years, consisting of 5 periods of +differ for a planning period of 50 years, consisting of 5 periods of 10 years: + ```@repl ts using TimeStruct ts = TwoLevel(5, 10, SimpleTimes(1,1)); df_start = [discount(t, ts, 0.05; type = "start") for t in ts] df_avg = [discount(t, ts, 0.05; type = "avg") for t in ts] ``` -While it is often normal to assume investments at the start of each + +While it is often normal to assume investments at the start of each strategic period, it can be more correct to average the discount factor for operational costs that are accrued throughout the strategic period. To help setting up the objective function in a typical optimization problem, there is a utility function [`objective_weight`](@ref) that returns the weight to give a time period in the objective, considering both -discount factor, probability and possible multiplicity. +discount factor, probability and possible multiplicity. diff --git a/docs/src/manual/iteration.md b/docs/src/manual/iteration.md index 1f38de8..30130b3 100644 --- a/docs/src/manual/iteration.md +++ b/docs/src/manual/iteration.md @@ -3,6 +3,7 @@ ## Basic iteration All time structures are iterable over their operational time periods + ```@repl ts using TimeStruct @@ -18,8 +19,9 @@ end 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`](@ref) it is possible to return both the previous and +[`withprev`](@ref) it is possible to return both the previous and current time period as a tuple when iterating: + ```@repl ts using TimeStruct periods = SimpleTimes(5, 1); @@ -37,15 +39,17 @@ there are several iterator wrappers that allows this kind of iteration pattern. The [`chunk`](@ref) function iterates through a time structure returning -subsequences of length at most `n` starting at each time period. +subsequences of length at most `n` starting at each time period. + ```@repl ts periods = SimpleTimes(5,1) collect(collect(ts) for ts in chunk(periods, 3)) ``` 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: +uptime. The following example shows how this can be implemented as part of +a JuMP model: + ```@ex using JuMP, TimeStruct @@ -57,10 +61,12 @@ m = Model() for ts in chunk(periods, 3) @constraint(m, sum(startup[t] for t in ts) <= 1) end + ``` Similarly, if modelling shutdown decision with a minimum uptime, -it is possible to reverse the original time periods and then +it is possible to reverse the original time periods and then chunk: + ```@ex m = Model() @variable(m, shutdown[periods], Bin) @@ -74,14 +80,14 @@ end Not all time structures can be reversed. Currently, it is only supported for operational time structures and operational scenarios. - ## 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`](@ref) function iterates through a time structure returning -subsequences of duration at least `dur` starting at each time period. +subsequences of duration at least `dur` starting at each time period. + ```@repl ts periods = SimpleTimes(5,[1, 2, 1, 1.5, 0.5, 2]) collect(collect(ts) for ts in chunk_duration(periods, 3)) @@ -91,7 +97,7 @@ collect(collect(ts) for ts in chunk_duration(periods, 3)) It is possible to use indices for operational time structures, either directly using [`SimpleTimes`](@ref) or [`CalendarTimes`](@ref) or by accessing an -operational scenario. +operational scenario. ```@repl ts periods = TwoLevel(3, 100, SimpleTimes(10,1)); diff --git a/docs/src/manual/multi.md b/docs/src/manual/multi.md index d2d3c75..3e5ee14 100644 --- a/docs/src/manual/multi.md +++ b/docs/src/manual/multi.md @@ -3,21 +3,23 @@ ## TwoLevel The main motivation for the `TimeStruct` package is to support -multi-horizon optimization models. The time structure [`TwoLevel`](@ref) allows for a two level -approach, combining an ordered sequence of strategic periods with given duration and an associated operational time structure. +multi-horizon optimization models. The time structure [`TwoLevel`](@ref) allows for a two level +approach, combining an ordered sequence of strategic periods with given duration and an associated operational time structure. + ```@repl ts using TimeStruct periods = TwoLevel( - [SimpleTimes(5,1), SimpleTimes(5,1), SimpleTimes(5,1)], + [SimpleTimes(5,1), SimpleTimes(5,1), SimpleTimes(5,1)], ); ``` ![Illustration of TwoLevel](./../figures/twolevel.png) -The following example shows a typical usage of a [`TwoLevel`](@ref) strucure with investment -decisions on a strategic level and operational decision variables. It is possible to iterate -through each strategic period using the [`strat_periods`](@ref)function. -```@repl ts +The following example shows a typical usage of a [`TwoLevel`](@ref) strucure with investment +decisions on a strategic level and operational decision variables. It is possible to iterate +through each strategic period using the [`strat_periods`](@ref)function. + +```@repl ts using JuMP m = Model(); @variable(m, invest[strat_periods(periods)] >= 0); @@ -38,7 +40,7 @@ scen = OperationalScenarios([oper, oper, oper], [0.4, 0.5, 0.1]); repr = RepresentativePeriods(2, 5, [0.5, 0.5], [oper, oper]); repr_scen = RepresentativePeriods(2, 5, [0.5, 0.5], [scen, scen]); -periods = TwoLevel([scen, repr, repr_scen]); +periods = TwoLevel([scen, repr, repr_scen]); ``` ![Complex TwoLevel](./../figures/two_complex.png) @@ -55,13 +57,14 @@ that holds the number of operational periods per strategic period. A typical use case is an investment problem where one uses years to measure duration at the strategic level and hours/days on the operational level. -Below is an example with 3 strategic periods of duration 5, 5, and 10 years -respectively, while the operational time structure is given by +Below is an example with 3 strategic periods of duration 5, 5, and 10 years +respectively, while the operational time structure is given by representative periods with duration in days. The `op_per_strat` is then set to 365. + ```@repl ts week = SimpleTimes(7,1); repr = RepresentativePeriods(2, 365, [0.6, 0.4], [week, week]); periods = TwoLevel(3, [5, 5, 10], [repr, repr, repr], 365.0); ``` -## TwoLevelTree \ No newline at end of file +## TwoLevelTree diff --git a/docs/src/manual/profiles.md b/docs/src/manual/profiles.md index fc2b25d..73932bf 100644 --- a/docs/src/manual/profiles.md +++ b/docs/src/manual/profiles.md @@ -2,16 +2,16 @@ To provide data for different time structures there is a flexible system of different time profiles that can be indexed by time periods. -- [`FixedProfile`](@ref): Time profile with the same value for all time periods -- [`OperationalProfile`](@ref): Time profile with values varying with operational time periods +- [`FixedProfile`](@ref): Time profile with the same value for all time periods +- [`OperationalProfile`](@ref): Time profile with values varying with operational time periods - [`RepresentativeProfile`](@ref): Holds a separate time profile for each representative period - [`ScenarioProfile`](@ref): Holds a separate time profile for each operational scenario - [`StrategicProfile`](@ref) : Holds a separate time profile for each strategic period -The following code example shows how these profile types can be combined in a flexible +The following code example shows how these profile types can be combined in a flexible manner to produce different overall profiles. -```julia +```julia rep_periods = RepresentativePeriods(2, 365, [0.6, 0.4], [SimpleTimes(7,1), SimpleTimes(7,1)]) periods = TwoLevel(2, 365, rep_periods) @@ -19,7 +19,7 @@ cost = StrategicProfile( [ RepresentativeProfile( [ - OperationalProfile([3, 3, 4, 3, 4, 6, 5]), + OperationalProfile([3, 3, 4, 3, 4, 6, 5]), FixedProfile(5) ] ), @@ -27,5 +27,6 @@ cost = StrategicProfile( ] ) ``` + Illustration of profile values for the various time periods as defined in the profile example ![Time profile values](./../figures/profiles.png) diff --git a/docs/src/reference/api.md b/docs/src/reference/api.md index f74e7eb..f4b2a50 100644 --- a/docs/src/reference/api.md +++ b/docs/src/reference/api.md @@ -1,7 +1,3 @@ - -```@meta -CurrentModule = TimeStruct -``` # Available time structures ```@docs @@ -82,7 +78,6 @@ chunk chunk_duration ``` - # Time profiles ```@docs diff --git a/docs/src/reference/internal.md b/docs/src/reference/internal.md index 310adc0..b7080f3 100644 --- a/docs/src/reference/internal.md +++ b/docs/src/reference/internal.md @@ -1,9 +1,5 @@ # Internal types -```@meta -CurrentModule = TimeStruct -``` - ## Strategic period types ([`TwoLevelTree`](@ref)) ### Single types @@ -19,7 +15,6 @@ StrategicScenario ```@docs AbstractTreeStructure StratTreeNodes - ``` ## Strategic period types ([`TwoLevel`](@ref)) @@ -60,7 +55,7 @@ StratNodeReprPeriods OperationalScenario StratOperationalScenario StratNodeOperationalScenario -StratNodeReprOpscenario +StratNodeReprOpScenario ``` ### Iterator types @@ -68,7 +63,7 @@ StratNodeReprOpscenario ```@docs StrategicScenarios StratNodeOpScens -StratNodeReprOpscenarios +StratNodeReprOpScens ``` ## Operational period types From 9025f4f188921233274f104562f761af736893a4 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 12:32:14 +0200 Subject: [PATCH 15/20] Updated docs based on docstrings of type/function --- docs/src/manual/multi.md | 4 +- docs/src/reference/api.md | 4 ++ docs/src/reference/internal.md | 65 +++++++++++++++++++---------- src/op_scenarios/opscenarios.jl | 2 +- src/op_scenarios/rep_periods.jl | 2 +- src/op_scenarios/tree_periods.jl | 4 +- src/representative/rep_periods.jl | 2 +- src/representative/tree_periods.jl | 6 +-- src/strat_scenarios/core_types.jl | 2 +- src/strat_scenarios/tree_periods.jl | 8 ++-- src/strategic/strat_periods.jl | 2 +- 11 files changed, 62 insertions(+), 39 deletions(-) diff --git a/docs/src/manual/multi.md b/docs/src/manual/multi.md index 3e5ee14..e748a40 100644 --- a/docs/src/manual/multi.md +++ b/docs/src/manual/multi.md @@ -1,6 +1,6 @@ # Multi-horizon time structures -## TwoLevel +## TwoLevel structure The main motivation for the `TimeStruct` package is to support multi-horizon optimization models. The time structure [`TwoLevel`](@ref) allows for a two level @@ -67,4 +67,4 @@ repr = RepresentativePeriods(2, 365, [0.6, 0.4], [week, week]); periods = TwoLevel(3, [5, 5, 10], [repr, repr, repr], 365.0); ``` -## TwoLevelTree +## TwoLevelTree structure diff --git a/docs/src/reference/api.md b/docs/src/reference/api.md index f4b2a50..aa82cac 100644 --- a/docs/src/reference/api.md +++ b/docs/src/reference/api.md @@ -43,6 +43,10 @@ isfirst multiple ``` +```@docs +multiple_strat +``` + ```@docs probability ``` diff --git a/docs/src/reference/internal.md b/docs/src/reference/internal.md index b7080f3..9678a03 100644 --- a/docs/src/reference/internal.md +++ b/docs/src/reference/internal.md @@ -5,16 +5,17 @@ ### Single types ```@docs -AbstractTreeNode -StratNode -StrategicScenario +TimeStruct.AbstractTreeNode +TimeStruct.StratNode +TimeStruct.StrategicScenario ``` ### Iterator types ```@docs -AbstractTreeStructure -StratTreeNodes +TimeStruct.AbstractTreeStructure +TimeStruct.StratTreeNodes +TimeStruct.StrategicScenarios ``` ## Strategic period types ([`TwoLevel`](@ref)) @@ -22,13 +23,16 @@ StratTreeNodes ### Single types ```@docs -AbstractStrategicPeriod -StrategicPeriod +TimeStruct.AbstractStrategicPeriod +TimeStruct.SingleStrategicPeriod +TimeStruct.StrategicPeriod ``` ### Iterator types ```@docs +TimeStruct.SingleStrategicPeriodWrapper +TimeStruct.StratPeriods ``` ## Representative period types @@ -36,15 +40,20 @@ StrategicPeriod ### Single types ```@docs -AbstractRepresentativePeriod -RepresentativePeriod -StratNodeReprPeriod +TimeStruct.AbstractRepresentativePeriod +TimeStruct.SingleReprPeriod +TimeStruct.RepresentativePeriod +TimeStruct.StratReprPeriod +TimeStruct.StratNodeReprPeriod ``` ### Iterator types ```@docs -StratNodeReprPeriods +TimeStruct.SingleReprPeriodWrapper +TimeStruct.ReprPeriods +TimeStruct.StratReprPeriods +TimeStruct.StratNodeReprPeriods ``` ## Operational scenarios types @@ -52,26 +61,36 @@ StratNodeReprPeriods ### Single types ```@docs -OperationalScenario -StratOperationalScenario -StratNodeOperationalScenario -StratNodeReprOpScenario +TimeStruct.AbstractOperationalScenario +TimeStruct.SingleScenario +TimeStruct.OperationalScenario +TimeStruct.ReprOperationalScenario +TimeStruct.StratOperationalScenario +TimeStruct.StratReprOpscenario +TimeStruct.StratNodeOperationalScenario +TimeStruct.StratNodeReprOpScenario ``` ### Iterator types ```@docs -StrategicScenarios -StratNodeOpScens -StratNodeReprOpScens +TimeStruct.SingleScenarioWrapper +TimeStruct.OpScens +TimeStruct.RepOpScens +TimeStruct.StratOpScens +TimeStruct.StratReprOpscenarios +TimeStruct.StratNodeOpScens +TimeStruct.StratNodeReprOpScens ``` ## Operational period types ```@docs -TimePeriod -SimplePeriod -CalendarPeriod -OperationalPeriod -TreePeriod +TimeStruct.TimePeriod +TimeStruct.SimplePeriod +TimeStruct.CalendarPeriod +TimeStruct.ScenarioPeriod +TimeStruct.ReprPeriod +TimeStruct.OperationalPeriod +TimeStruct.TreePeriod ``` diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 2e735f7..4b97923 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -3,7 +3,7 @@ Abstract type used for time structures that represent an operational scenario. These periods are obtained when iterating through the operational scenarios of a time -structure declared by the function [`opscenarios`](@ref.) +structure declared by the function [`opscenarios`](@ref). """ abstract type AbstractOperationalScenario{T} <: TimeStructure{T} end diff --git a/src/op_scenarios/rep_periods.jl b/src/op_scenarios/rep_periods.jl index ff2534d..8424d34 100644 --- a/src/op_scenarios/rep_periods.jl +++ b/src/op_scenarios/rep_periods.jl @@ -109,7 +109,7 @@ function Base.iterate(oscs::RepOpScens, state = nothing) end """ -When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `repr_periods` returns the +When the `TimeStructure` is a [`SingleReprPeriod`](@ref), `opscenarios` returns the correct behavior based on the substructure. """ opscenarios(ts::SingleReprPeriod) = opscenarios(ts.ts) diff --git a/src/op_scenarios/tree_periods.jl b/src/op_scenarios/tree_periods.jl index 1d79463..32c2fa6 100644 --- a/src/op_scenarios/tree_periods.jl +++ b/src/op_scenarios/tree_periods.jl @@ -6,7 +6,7 @@ iteration over its time periods. It is created through iterating through [`StratNodeOpScens`](@ref). It is equivalent to a [`StratOperationalScenario`](@ref) of a [`TwoLevel`](@ref) time -structure when utilizing a [`TwolevelTree`]. +structure when utilizing a [`TwoLevelTree`](@ref). """ struct StratNodeOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} @@ -106,7 +106,7 @@ Base.eltype(_::StratNodeOpScens) = StratNodeOperationalScenario struct StratNodeReprOpScenario{T} <: AbstractOperationalScenario{T} A structure representing a single operational scenario for a representative period in A -[`TwoLevelTree`] structure supporting iteration over its time periods. +[`TwoLevelTree`](@ref) structure supporting iteration over its time periods. """ struct StratNodeReprOpScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl index 22b6661..2819cdf 100644 --- a/src/representative/rep_periods.jl +++ b/src/representative/rep_periods.jl @@ -3,7 +3,7 @@ Abstract type used for time structures that represent a representative period. These periods are obtained when iterating through the representative periods of a time -structure declared by the function [`repr_period`](@ref.) +structure declared by the function [`repr_periods`](@ref). """ abstract type AbstractRepresentativePeriod{T} <: TimeStructure{T} end diff --git a/src/representative/tree_periods.jl b/src/representative/tree_periods.jl index 9cdcf9a..cd1b51b 100644 --- a/src/representative/tree_periods.jl +++ b/src/representative/tree_periods.jl @@ -2,10 +2,10 @@ struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} A structure representing a single representative period of a [`StratNode`](@ref) of a -[`TwolevelTree`]. It is created through iterating through [`StratNodeReprPeriods`](@ref). +[`TwoLevelTree`](@ref). It is created through iterating through [`StratNodeReprPeriods`](@ref). -It is equivalent to a [`StratReprPeriod`] of a [`TwoLevel`](@ref) time structure when -utilizing a [`TwolevelTree`]. +It is equivalent to a [`StratReprPeriod`](@ref) of a [`TwoLevel`](@ref) time structure when +utilizing a [`TwoLevelTree`](@ref). """ struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} diff --git a/src/strat_scenarios/core_types.jl b/src/strat_scenarios/core_types.jl index dc29a78..2c40543 100644 --- a/src/strat_scenarios/core_types.jl +++ b/src/strat_scenarios/core_types.jl @@ -135,7 +135,7 @@ end struct StrategicScenarios Type for iteration through the individual strategic scenarios represented as -[`StrategicScenario`]. +[`StrategicScenario`](@ref). """ struct StrategicScenarios ts::TwoLevelTree diff --git a/src/strat_scenarios/tree_periods.jl b/src/strat_scenarios/tree_periods.jl index b5b3bc2..9baffff 100644 --- a/src/strat_scenarios/tree_periods.jl +++ b/src/strat_scenarios/tree_periods.jl @@ -1,14 +1,14 @@ """ AbstractTreeNode{S,T} <: AbstractStrategicPeriod{S,T} -Abstract base type for all tree nodes within a [`TwoLevelTree`] type. +Abstract base type for all tree nodes within a [`TwoLevelTree`](@ref) type. """ abstract type AbstractTreeNode{S,T} <: AbstractStrategicPeriod{S,T} end """ AbstractTreeStructure -Abstract base type for all tree timestructures within a [`TwoLevelTree`] type. +Abstract base type for all tree timestructures within a [`TwoLevelTree`](@ref) type. """ abstract type AbstractTreeStructure end @@ -33,11 +33,11 @@ StrategicTreeIndexable(::Type{<:TimePeriod}) = HasStratTreeIndex() """ struct StratNode{S, T, OP<:TimeStructure{T}} <: AbstractTreeNode{S,T} -A structure representing a single strategic node of a [`TwolevelTree`]. It is created +A structure representing a single strategic node of a [`TwoLevelTree`](@ref). It is created through iterating through [`StratTreeNodes`](@ref). It is equivalent to a [`StrategicPeriod`](@ref) of a [`TwoLevel`](@ref) time structure when -utilizing a [`TwolevelTree`]. +utilizing a [`TwoLevelTree`](@ref). """ struct StratNode{S,T,OP<:TimeStructure{T}} <: AbstractTreeNode{S,T} sp::Int diff --git a/src/strategic/strat_periods.jl b/src/strategic/strat_periods.jl index e437c1b..16d2675 100644 --- a/src/strategic/strat_periods.jl +++ b/src/strategic/strat_periods.jl @@ -3,7 +3,7 @@ Abstract type used for time structures that represent a strategic period. These periods are obtained when iterating through the strategic periods of a time -structure declared by the function [`strat_periods`](@ref.) +structure declared by the function [`strat_periods`](@ref). """ abstract type AbstractStrategicPeriod{S,T} <: TimeStructure{T} end From 4b3d367939355094f07e5c5bf7a4c173f3a987d2 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 14:12:25 +0200 Subject: [PATCH 16/20] Updates in the individual types --- src/op_scenarios/opscenarios.jl | 25 +++++----- src/op_scenarios/rep_periods.jl | 77 +++++++++++++++++++------------ src/op_scenarios/strat_periods.jl | 57 ++++++++++++++--------- 3 files changed, 92 insertions(+), 67 deletions(-) diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 4b97923..14e9eb5 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -89,11 +89,6 @@ When the `TimeStructure` is a `TimeStructure`, `opscenarios` returns a [`SingleScenarioWrapper`](@ref). This corresponds to the default behavior. """ opscenarios(ts::TimeStructure) = SingleScenarioWrapper(ts) -""" -When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `opscenarios` returns the -correct behavior based on the substructure. -""" -opscenarios(ts::SingleStrategicPeriod) = opscenarios(ts.ts) # Add basic functions of iterators Base.length(ssw::SingleScenarioWrapper) = 1 @@ -114,8 +109,8 @@ time periods. It is created when iterating through [`OpScens`](@ref). struct OperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} scen::Int - probability::Float64 mult_sc::Float64 + probability::Float64 operational::OP end @@ -137,7 +132,9 @@ end # Add basic functions of iterators Base.length(osc::OperationalScenario) = length(osc.operational) -Base.eltype(::Type{OperationalScenario}) = ScenarioPeriod +function Base.eltype(_::Type{OperationalScenario{T,OP}}) where {T,OP} + return ScenarioPeriod{eltype(OP)} +end function Base.iterate(osc::OperationalScenario, state = nothing) next = isnothing(state) ? iterate(osc.operational) : @@ -165,27 +162,27 @@ Type for iterating through the individual operational scenarios of a function [`opscenarios`](@ref). """ struct OpScens{T,OP} - opscens::OperationalScenarios{T,OP} + ts::OperationalScenarios{T,OP} end """ When the `TimeStructure` is an [`OperationalScenarios`](@ref), `opscenarios` returns the iterator [`OpScens`](@ref). """ -opscenarios(ts::OperationalScenarios) = OpScens(ts) +opscenarios(oscs::OperationalScenarios) = OpScens(oscs) # Provide a constructor to simplify the design function OperationalScenario(oscs::OpScens, per::Int) return OperationalScenario( per, - oscs.opscens.probability[per], - _multiple_adj(oscs.opscens, per), - oscs.opscens.scenarios[per], + _multiple_adj(oscs.ts, per), + oscs.ts.probability[per], + oscs.ts.scenarios[per], ) end # Add basic functions of iterators -Base.length(oscs::OpScens) = oscs.opscens.len +Base.length(oscs::OpScens) = oscs.ts.len function Base.eltype(_::Type{OpScens{T,OP}}) where {T,OP<:TimeStructure{T}} return OperationalScenario{T,OP} end @@ -199,6 +196,6 @@ function Base.getindex(oscs::OpScens, index::Int) return OperationalScenario(oscs, index) end function Base.eachindex(oscs::OpScens) - return eachindex(oscs.opscens.scenarios) + return eachindex(oscs.ts.scenarios) end Base.last(oscs::OpScens) = OperationalScenario(oscs, length(oscs)) diff --git a/src/op_scenarios/rep_periods.jl b/src/op_scenarios/rep_periods.jl index 8424d34..412fcb9 100644 --- a/src/op_scenarios/rep_periods.jl +++ b/src/op_scenarios/rep_periods.jl @@ -6,39 +6,37 @@ time periods. It is created when iterating through [`RepOpScens`](@ref). """ struct ReprOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} - rp::Int - scen::Int - probability::Float64 - multiple_repr::Float64 - multiple_scen::Float64 - operational::OP + rp::Int + scen::Int + mult_rp::Float64 + mult_scen::Float64 + probability::Float64 + operational::OP end _opscen(osc::ReprOperationalScenario) = osc.scen _rper(osc::ReprOperationalScenario) = osc.rp probability(osc::ReprOperationalScenario) = osc.probability -mult_scen(osc::ReprOperationalScenario) = osc.multiple_scen -mult_repr(osc::ReprOperationalScenario) = osc.multiple_repr +mult_scen(osc::ReprOperationalScenario) = osc.mult_scen +mult_repr(osc::ReprOperationalScenario) = osc.mult_rp RepresentativeIndexable(::Type{<:ReprOperationalScenario}) = HasReprIndex() # Provide a constructor to simplify the design function ReprPeriod(osc::ReprOperationalScenario, per) - mult_scp = mult_scen(osc) * multiple(per) - mult_rp = mult_repr(osc) * mult_scp - scp = ScenarioPeriod(_opscen(osc), probability(osc), mult_scp, per) - return ReprPeriod(_rper(osc), scp, mult_rp) + mult = mult_repr(osc) * multiple(per) + return ReprPeriod(_rper(osc), per, mult) end function Base.show(io::IO, osc::ReprOperationalScenario) - return print(io, "rp$(osc.rp)-sc$(osc.scen)") + return print(io, "rp$(_rper(osc))-sc$(_opscen(osc))") end # Add basic functions of iterators Base.length(osc::ReprOperationalScenario) = length(osc.operational) -function Base.eltype(osc::ReprOperationalScenario{T,OP}) where {T,OP} - return ReprPeriod{ScenarioPeriod{eltype(OP)}} +function Base.eltype(_::ReprOperationalScenario{T,OP}) where {T,OP} + return ReprPeriod{eltype(OP)} end function Base.iterate(osc::ReprOperationalScenario, state = nothing) next = @@ -69,12 +67,16 @@ function [`opscenarios`](@ref). """ struct RepOpScens{OP} rp::Int - mult::Float64 + mult_rp::Float64 opscens::OP end _rper(oscs::RepOpScens) = oscs.rp +mult_repr(oscs::RepOpScens) = oscs.mult_rp + +_oper_it(oscs::RepOpScens) = oscs.opscens + """ When the `TimeStructure` is a [`RepresentativePeriod`](@ref) with [`OperationalScenarios`](@ref), `opscenarios` returns the iterator [`RepOpScens`](@ref). @@ -82,30 +84,45 @@ When the `TimeStructure` is a [`RepresentativePeriod`](@ref) with [`OperationalS function opscenarios( rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}, ) where {T,OP} - return RepOpScens(_rper(rep), mult_repr(rep), rep.operational) + return RepOpScens(_rper(rep), mult_repr(rep), opscenarios(rep.operational)) end # Provide a constructor to simplify the design -function ReprOperationalScenario(oscs::RepOpScens, state::Int) +function ReprOperationalScenario(oscs::RepOpScens, scen::Int, per) return ReprOperationalScenario( _rper(oscs), - state, - oscs.opscens.probability[state], - oscs.mult, - _multiple_adj(oscs.opscens, state), - oscs.opscens.scenarios[state] + scen, + mult_repr(oscs), + _multiple_adj(_oper_it(_oper_it(oscs)), scen), + probability(per), + per, ) end -Base.length(oscs::RepOpScens) = length(oscs.opscens.scenarios) -function Base.eltype(_::RepOpScens{SC}) where {T,OP,SC<:OperationalScenarios{T,OP}} - return ReprOperationalScenario{T,OP} +# Add basic functions of iterators +Base.length(oscs::RepOpScens) = length(_oper_it(_oper_it(oscs)).scenarios) +function Base.eltype(_::Type{RepOpScens{SC}}) where {T,OP,SC<:OpScens{T,OP}} + return ReprOperationalScenario{T,eltype(SC)} end -function Base.iterate(oscs::RepOpScens, state = nothing) - scen = isnothing(state) ? 1 : state + 1 - scen > length(oscs) && return nothing +function Base.iterate(oscs::RepOpScens, state = (nothing, 1)) + next = + isnothing(state[1]) ? iterate(_oper_it(oscs)) : + iterate(_oper_it(oscs), state[1]) + isnothing(next) && return nothing - return ReprOperationalScenario(oscs, scen), scen + scen = state[2] + return ReprOperationalScenario(oscs, _opscen(next[1]), next[1]), (next[2], scen + 1) +end +function Base.getindex(oscs::RepOpScens, index::Int) + per = _oper_it(oscs)[index] + return ReprOperationalScenario(oscs, _opscen(per), per) +end +function Base.eachindex(oscs::RepOpScens) + return eachindex(_oper_it(oscs)) +end +function Base.last(oscs::RepOpScens) + per = last(_oper_it(oscs)) + return ReprOperationalScenario(oscs, _opscen(per), per) end """ diff --git a/src/op_scenarios/strat_periods.jl b/src/op_scenarios/strat_periods.jl index 82938bb..a9c9171 100644 --- a/src/op_scenarios/strat_periods.jl +++ b/src/op_scenarios/strat_periods.jl @@ -13,12 +13,12 @@ struct StratOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalSc operational::OP end -_opscen(osc::StratOperationalScenario) = osc.scen _strat_per(osc::StratOperationalScenario) = osc.sp +_opscen(osc::StratOperationalScenario) = osc.scen -probability(osc::StratOperationalScenario) = osc.probability -mult_scen(osc::StratOperationalScenario) = osc.mult_scen mult_strat(osc::StratOperationalScenario) = osc.mult_sp +mult_scen(osc::StratOperationalScenario) = osc.mult_scen +probability(osc::StratOperationalScenario) = osc.probability StrategicIndexable(::Type{<:StratOperationalScenario}) = HasStratIndex() ScenarioIndexable(::Type{<:StratOperationalScenario}) = HasScenarioIndex() @@ -75,6 +75,8 @@ _strat_per(oscs::StratOpScens) = oscs.sp mult_strat(oscs::StratOpScens) = oscs.mult_sp +_oper_it(oscs::StratOpScens) = oscs.opscens + """ When the `TimeStructure` is a [`StrategicPeriod`](@ref), `opscenarios` returns the iterator [`StratOpScens`](@ref). @@ -90,26 +92,26 @@ function StratOperationalScenario(oscs::StratOpScens, scen::Int, per) mult_strat(oscs), mult_scen(per), probability(per), - per + per, ) end -Base.length(oscs::StratOpScens) = length(oscs.opscens) +Base.length(oscs::StratOpScens) = length(_oper_it(oscs)) function Base.iterate(oscs::StratOpScens, state = (nothing, 1)) next = - isnothing(state[1]) ? iterate(oscs.opscens) : - iterate(oscs.opscens, state[1]) + isnothing(state[1]) ? iterate(_oper_it(oscs)) : + iterate(_oper_it(oscs), state[1]) isnothing(next) && return nothing scen = state[2] return StratOperationalScenario(oscs, _opscen(next[1]), next[1]), (next[2], scen + 1) end function Base.getindex(oscs::StratOpScens, index::Int) - per = oscs.opscens[index] + per = _oper_it(oscs)[index] return StratOperationalScenario(oscs, index, per) end function Base.eachindex(oscs::StratOpScens) - return eachindex(oscs.opscens) + return eachindex(_oper_it(oscs)) end function Base.last(oscs::StratOpScens) per = last(oscs.repr) @@ -192,20 +194,25 @@ Type for iterating through the individual operational scenarios of a automatically created through the function [`opscenarios`](@ref). """ struct StratReprOpscenarios{OP} - srp::StratReprPeriod + sp::Int + rp::Int + mult_sp::Float64 + mult_rp::Float64 opscens::OP end -_rper(oscs::StratReprOpscenarios) = _rper(oscs.srp) -_strat_per(oscs::StratReprOpscenarios) = _strat_per(oscs.srp) +_strat_per(oscs::StratReprOpscenarios) = oscs.sp +_rper(oscs::StratReprOpscenarios) = oscs.rp -mult_repr(oscs::StratReprOpscenarios) = mult_repr(oscs.srp) -mult_strat(oscs::StratReprOpscenarios) = mult_strat(oscs.srp) +mult_strat(oscs::StratReprOpscenarios) = oscs.mult_sp +mult_repr(oscs::StratReprOpscenarios) = oscs.mult_rp + +_oper_it(oscs::StratReprOpscenarios) = oscs.opscens function opscenarios( - srp::StratReprPeriod{T,RepresentativePeriod{T,OP}}, + rp::StratReprPeriod{T,RepresentativePeriod{T,OP}}, ) where {T,OP} - return StratReprOpscenarios(srp, opscenarios(srp.operational.operational)) + return StratReprOpscenarios(_strat_per(rp), _rper(rp), mult_strat(rp), mult_repr(rp), opscenarios(rp.operational.operational)) end function opscenarios(rp::StratReprPeriod) return StratOpScens(_strat_per(rp), mult_strat(rp), opscenarios(rp.operational)) @@ -237,30 +244,35 @@ function StratReprOpscenario(oscs::StratReprOpscenarios, scen, per) end # Add basic functions of iterators -Base.length(oscs::StratReprOpscenarios) = length(oscs.opscens) +Base.length(oscs::StratReprOpscenarios) = length(_oper_it(oscs)) function Base.eltype(_::Type{StratReprOpscenarios{SC}}) where {T,OP,SC<:OpScens{T,OP}} return StratReprOpscenario{T,eltype(SC)} end function Base.iterate(oscs::StratReprOpscenarios, state = (nothing, 1)) next = - isnothing(state[1]) ? iterate(oscs.opscens) : - iterate(oscs.opscens, state[1]) + isnothing(state[1]) ? iterate(_oper_it(oscs)) : + iterate(_oper_it(oscs), state[1]) isnothing(next) && return nothing return StratReprOpscenario(oscs, state[2], next[1]), (next[2], state[2] + 1) end function Base.getindex(oscs::StratReprOpscenarios, index::Int) - per = oscs.opscens[index] + per = _oper_it(oscs)[index] return StratReprOpscenario(oscs, _opscen(per), per) end function Base.eachindex(oscs::StratReprOpscenarios) - return eachindex(oscs.opscens) + return eachindex(_oper_it(oscs)) end function Base.last(oscs::StratReprOpscenarios) - per = last(oscs.opscens) + per = last(_oper_it(oscs)) return StratReprOpscenario(oscs, _opscen(per), per) end +""" +When the `TimeStructure` is a [`SingleStrategicPeriod`](@ref), `opscenarios` returns the +correct behavior based on the substructure. +""" +opscenarios(ts::SingleStrategicPeriod) = opscenarios(ts.ts) """ When the `TimeStructure` is a [`TwoLevel`](@ref), `opscenarios` returns a vector of [`StratOperationalScenario`](@ref)s. @@ -270,7 +282,6 @@ function opscenarios(ts::TwoLevel{S,T,OP}) where {S,T,OP} Iterators.flatten(opscenarios(sp) for sp in strategic_periods(ts)), ) end - """ When the `TimeStructure` is a [`TwoLevel`](@ref) with [`RepresentativePeriods`](@ref), `opscenarios` returns a vector of [`StratReprOpscenario`](@ref)s. From 08ef00428ccf319456c0e4dcde18dbbbaee904e0 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 14:15:15 +0200 Subject: [PATCH 17/20] Switched Formatter to 92 --- .JuliaFormatter.toml | 2 +- docs/make.jl | 7 +- ext/TimeStructUnitfulExt.jl | 11 +- src/TimeStruct.jl | 4 +- src/calendar.jl | 9 +- src/discount.jl | 26 +-- src/op_scenarios/core_types.jl | 11 +- src/op_scenarios/opscenarios.jl | 8 +- src/op_scenarios/rep_periods.jl | 19 +- src/op_scenarios/strat_periods.jl | 47 ++--- src/op_scenarios/tree_periods.jl | 24 +-- src/profiles.jl | 42 +--- src/representative/core_types.jl | 13 +- src/representative/rep_periods.jl | 15 +- src/representative/strat_periods.jl | 15 +- src/representative/tree_periods.jl | 7 +- src/strat_scenarios/core_types.jl | 29 +-- src/strat_scenarios/tree_periods.jl | 4 +- src/strategic/core_types.jl | 16 +- src/strategic/strat_periods.jl | 17 +- src/utils.jl | 21 +- test/runtests.jl | 290 ++++++++-------------------- 22 files changed, 174 insertions(+), 463 deletions(-) diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml index a700a07..8206146 100644 --- a/.JuliaFormatter.toml +++ b/.JuliaFormatter.toml @@ -3,6 +3,6 @@ always_for_in = true always_use_return = true -margin = 80 +margin = 92 remove_extra_newlines = true short_to_long_function_def = true diff --git a/docs/make.jl b/docs/make.jl index 2d232e0..ddac0a1 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -13,12 +13,7 @@ pages = [ "Internal reference" => "reference/internal.md", ] -DocMeta.setdocmeta!( - TimeStruct, - :DocTestSetup, - :(using TimeStruct); - recursive = true, -) +DocMeta.setdocmeta!(TimeStruct, :DocTestSetup, :(using TimeStruct); recursive = true) Documenter.makedocs( sitename = "TimeStruct", diff --git a/ext/TimeStructUnitfulExt.jl b/ext/TimeStructUnitfulExt.jl index b9ce70e..2d178b2 100644 --- a/ext/TimeStructUnitfulExt.jl +++ b/ext/TimeStructUnitfulExt.jl @@ -3,10 +3,7 @@ module TimeStructUnitfulExt using Unitful using TimeStruct -function TimeStruct.SimpleTimes( - dur::Vector{T}, - u::Unitful.Units, -) where {T<:Real} +function TimeStruct.SimpleTimes(dur::Vector{T}, u::Unitful.Units) where {T<:Real} return TimeStruct.SimpleTimes(length(dur), Unitful.Quantity.(dur, u)) end @@ -15,11 +12,7 @@ function TimeStruct.TwoLevel( u::Unitful.Units, oper::TimeStructure{<:Unitful.Quantity{V,Unitful.𝐓}}, ) where {V} - return TimeStruct.TwoLevel( - Unitful.Quantity.(duration, u), - oper; - op_per_strat = 1.0, - ) + return TimeStruct.TwoLevel(Unitful.Quantity.(duration, u), oper; op_per_strat = 1.0) end function TimeStruct.stripunit(val::Unitful.Quantity) diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 917594a..b852f30 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -3,8 +3,7 @@ module TimeStruct import Dates import TimeZones -import .Base: - first, last, isempty, length, size, eltype, IteratorSize, IteratorEltype +import .Base: first, last, isempty, length, size, eltype, IteratorSize, IteratorEltype include("structures.jl") include("simple.jl") @@ -23,7 +22,6 @@ include(joinpath("op_scenarios", "rep_periods.jl")) include(joinpath("op_scenarios", "strat_periods.jl")) include(joinpath("op_scenarios", "tree_periods.jl")) - include("utils.jl") include("discount.jl") include("profiles.jl") diff --git a/src/calendar.jl b/src/calendar.jl index cd6d6af..28c6447 100644 --- a/src/calendar.jl +++ b/src/calendar.jl @@ -42,11 +42,7 @@ function CalendarTimes( length::Integer, period::Dates.Period, ) - return CalendarTimes( - TimeZones.ZonedDateTime(start_date, zone), - length, - period, - ) + return CalendarTimes(TimeZones.ZonedDateTime(start_date, zone), length, period) end function CalendarTimes( start_date::Union{Dates.Date,Dates.DateTime}, @@ -71,8 +67,7 @@ _total_duration(ts::CalendarTimes) = sum(duration(t) for t in ts) Base.length(ts::CalendarTimes) = ts.length Base.eltype(::Type{CalendarTimes{T}}) where {T} = CalendarPeriod{T} function Base.iterate(ts::CalendarTimes) - return CalendarPeriod(ts.start_date, ts.start_date + ts.period, 1), - (1, ts.start_date) + return CalendarPeriod(ts.start_date, ts.start_date + ts.period, 1), (1, ts.start_date) end function Base.iterate(ts::CalendarTimes, state) state[1] == ts.length && return nothing diff --git a/src/discount.jl b/src/discount.jl index 21336be..9270d83 100644 --- a/src/discount.jl +++ b/src/discount.jl @@ -19,15 +19,10 @@ function _start_strat(t::OperationalPeriod, ts::TwoLevel{S,T}) where {S,T} return zero(S) end - return sum( - duration_strat(spp) for spp in strat_periods(ts) if _strat_per(spp) < sp - ) + return sum(duration_strat(spp) for spp in strat_periods(ts) if _strat_per(spp) < sp) end -function _start_strat( - sp::AbstractStrategicPeriod, - ts::TwoLevel{S,T}, -) where {S,T} +function _start_strat(sp::AbstractStrategicPeriod, ts::TwoLevel{S,T}) where {S,T} if _strat_per(sp) == 1 return zero(S) end @@ -69,12 +64,7 @@ function discount( return multiplier end -function discount( - disc::Discounter, - t::TimePeriod; - type = "start", - timeunit_to_year = 1.0, -) +function discount(disc::Discounter, t::TimePeriod; type = "start", timeunit_to_year = 1.0) return discount(t, disc.ts, disc.discount_rate; type, timeunit_to_year) end @@ -82,8 +72,8 @@ function discount_avg(discount_rate, start_year, duration_years) if discount_rate > 0 δ = 1 / (1 + discount_rate) m = - (δ^start_year - δ^(start_year + duration_years)) / - log(1 + discount_rate) / duration_years + (δ^start_year - δ^(start_year + duration_years)) / log(1 + discount_rate) / + duration_years return m else return 1.0 @@ -151,11 +141,7 @@ function objective_weight( return discount(sp, ts, discount_rate; type, timeunit_to_year) end -function objective_weight( - sp::AbstractStrategicPeriod, - disc::Discounter; - type = "start", -) +function objective_weight(sp::AbstractStrategicPeriod, disc::Discounter; type = "start") return objective_weight( sp, disc.ts, diff --git a/src/op_scenarios/core_types.jl b/src/op_scenarios/core_types.jl index 97f974a..5fb22d2 100644 --- a/src/op_scenarios/core_types.jl +++ b/src/op_scenarios/core_types.jl @@ -62,18 +62,11 @@ end function OperationalScenarios(len::Integer, oper::TimeStructure{T}) where {T} return OperationalScenarios(len, fill(oper, len), fill(1.0 / len, len)) end -function OperationalScenarios( - oper::Vector{<:TimeStructure{T}}, - prob::Vector, -) where {T} +function OperationalScenarios(oper::Vector{<:TimeStructure{T}}, prob::Vector) where {T} return OperationalScenarios(length(oper), oper, prob) end function OperationalScenarios(oper::Vector{<:TimeStructure{T}}) where {T} - return OperationalScenarios( - length(oper), - oper, - fill(1.0 / length(oper), length(oper)), - ) + return OperationalScenarios(length(oper), oper, fill(1.0 / length(oper), length(oper))) end function _total_duration(oscs::OperationalScenarios) diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 14e9eb5..f2407a7 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -106,8 +106,7 @@ end A type representing a single operational scenario supporting iteration over its time periods. It is created when iterating through [`OpScens`](@ref). """ -struct OperationalScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} +struct OperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} scen::Int mult_sc::Float64 probability::Float64 @@ -136,9 +135,7 @@ function Base.eltype(_::Type{OperationalScenario{T,OP}}) where {T,OP} return ScenarioPeriod{eltype(OP)} end function Base.iterate(osc::OperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(osc.operational) : - iterate(osc.operational, state) + next = isnothing(state) ? iterate(osc.operational) : iterate(osc.operational, state) next === nothing && return nothing return ScenarioPeriod(osc, next[1]), next[2] end @@ -153,7 +150,6 @@ function Base.eachindex(osc::OperationalScenario) return eachindex(osc.operational) end - """ OpScens{T,OP} diff --git a/src/op_scenarios/rep_periods.jl b/src/op_scenarios/rep_periods.jl index 412fcb9..08390e9 100644 --- a/src/op_scenarios/rep_periods.jl +++ b/src/op_scenarios/rep_periods.jl @@ -4,8 +4,7 @@ A type representing a single operational scenarios supporting iteration over its time periods. It is created when iterating through [`RepOpScens`](@ref). """ -struct ReprOperationalScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} +struct ReprOperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} rp::Int scen::Int mult_rp::Float64 @@ -39,9 +38,7 @@ function Base.eltype(_::ReprOperationalScenario{T,OP}) where {T,OP} return ReprPeriod{eltype(OP)} end function Base.iterate(osc::ReprOperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(osc.operational) : - iterate(osc.operational, state) + next = isnothing(state) ? iterate(osc.operational) : iterate(osc.operational, state) next === nothing && return nothing return ReprPeriod(osc, next[1]), next[2] @@ -81,9 +78,7 @@ _oper_it(oscs::RepOpScens) = oscs.opscens When the `TimeStructure` is a [`RepresentativePeriod`](@ref) with [`OperationalScenarios`](@ref), `opscenarios` returns the iterator [`RepOpScens`](@ref). """ -function opscenarios( - rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}, -) where {T,OP} +function opscenarios(rep::RepresentativePeriod{T,OperationalScenarios{T,OP}}) where {T,OP} return RepOpScens(_rper(rep), mult_repr(rep), opscenarios(rep.operational)) end @@ -105,9 +100,7 @@ function Base.eltype(_::Type{RepOpScens{SC}}) where {T,OP,SC<:OpScens{T,OP}} return ReprOperationalScenario{T,eltype(SC)} end function Base.iterate(oscs::RepOpScens, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(_oper_it(oscs)) : - iterate(_oper_it(oscs), state[1]) + next = isnothing(state[1]) ? iterate(_oper_it(oscs)) : iterate(_oper_it(oscs), state[1]) isnothing(next) && return nothing scen = state[2] @@ -135,7 +128,5 @@ When the `TimeStructure` is a [`RepresentativePeriods`](@ref), `opscenarios` ret `Array` of all [`ReprOperationalScenario`](@ref)s. """ function opscenarios(ts::RepresentativePeriods) - return collect( - Iterators.flatten(opscenarios(rp) for rp in repr_periods(ts)), - ) + return collect(Iterators.flatten(opscenarios(rp) for rp in repr_periods(ts))) end diff --git a/src/op_scenarios/strat_periods.jl b/src/op_scenarios/strat_periods.jl index a9c9171..cdfa4af 100644 --- a/src/op_scenarios/strat_periods.jl +++ b/src/op_scenarios/strat_periods.jl @@ -39,9 +39,7 @@ function Base.eltype(::Type{StratOperationalScenario{T,OP}}) where {T,OP} return OperationalPeriod end function Base.iterate(osc::StratOperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(osc.operational) : - iterate(osc.operational, state) + next = isnothing(state) ? iterate(osc.operational) : iterate(osc.operational, state) isnothing(next) && return nothing return OperationalPeriod(osc, next[1]), next[2] @@ -98,9 +96,7 @@ end Base.length(oscs::StratOpScens) = length(_oper_it(oscs)) function Base.iterate(oscs::StratOpScens, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(_oper_it(oscs)) : - iterate(_oper_it(oscs), state[1]) + next = isnothing(state[1]) ? iterate(_oper_it(oscs)) : iterate(_oper_it(oscs), state[1]) isnothing(next) && return nothing scen = state[2] @@ -124,8 +120,7 @@ end A type representing a single representative period supporting iteration over its time periods. It is created when iterating through [`StratReprPeriods`](@ref). """ -struct StratReprOpscenario{T, OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} +struct StratReprOpscenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} sp::Int rp::Int opscen::Int @@ -167,9 +162,7 @@ function Base.eltype(::Type{StratReprOpscenario{T,OP}}) where {T,OP} return OperationalPeriod end function Base.iterate(osc::StratReprOpscenario, state = nothing) - next = - isnothing(state) ? iterate(osc.operational) : - iterate(osc.operational, state) + next = isnothing(state) ? iterate(osc.operational) : iterate(osc.operational, state) isnothing(next) && return nothing return OperationalPeriod(osc, next[1]), next[2] @@ -209,10 +202,14 @@ mult_repr(oscs::StratReprOpscenarios) = oscs.mult_rp _oper_it(oscs::StratReprOpscenarios) = oscs.opscens -function opscenarios( - rp::StratReprPeriod{T,RepresentativePeriod{T,OP}}, -) where {T,OP} - return StratReprOpscenarios(_strat_per(rp), _rper(rp), mult_strat(rp), mult_repr(rp), opscenarios(rp.operational.operational)) +function opscenarios(rp::StratReprPeriod{T,RepresentativePeriod{T,OP}}) where {T,OP} + return StratReprOpscenarios( + _strat_per(rp), + _rper(rp), + mult_strat(rp), + mult_repr(rp), + opscenarios(rp.operational.operational), + ) end function opscenarios(rp::StratReprPeriod) return StratOpScens(_strat_per(rp), mult_strat(rp), opscenarios(rp.operational)) @@ -225,9 +222,7 @@ When the `TimeStructure` is a [`StrategicPeriod`](@ref) with [`RepresentativePer function opscenarios( sp::StrategicPeriod{S1,T,RepresentativePeriods{S2,T,OP}}, ) where {S1,S2,T,OP} - return collect( - Iterators.flatten(opscenarios(rp) for rp in repr_periods(sp)), - ) + return collect(Iterators.flatten(opscenarios(rp) for rp in repr_periods(sp))) end # Provide a constructor to simplify the design @@ -249,9 +244,7 @@ function Base.eltype(_::Type{StratReprOpscenarios{SC}}) where {T,OP,SC<:OpScens{ return StratReprOpscenario{T,eltype(SC)} end function Base.iterate(oscs::StratReprOpscenarios, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(_oper_it(oscs)) : - iterate(_oper_it(oscs), state[1]) + next = isnothing(state[1]) ? iterate(_oper_it(oscs)) : iterate(_oper_it(oscs), state[1]) isnothing(next) && return nothing return StratReprOpscenario(oscs, state[2], next[1]), (next[2], state[2] + 1) @@ -278,18 +271,16 @@ When the `TimeStructure` is a [`TwoLevel`](@ref), `opscenarios` returns a vector [`StratOperationalScenario`](@ref)s. """ function opscenarios(ts::TwoLevel{S,T,OP}) where {S,T,OP} - return collect( - Iterators.flatten(opscenarios(sp) for sp in strategic_periods(ts)), - ) + return collect(Iterators.flatten(opscenarios(sp) for sp in strategic_periods(ts))) end """ When the `TimeStructure` is a [`TwoLevel`](@ref) with [`RepresentativePeriods`](@ref), `opscenarios` returns a vector of [`StratReprOpscenario`](@ref)s. """ -function opscenarios( - ts::TwoLevel{S1,T,RepresentativePeriods{S2,T,OP}}, -) where {S1,S2,T,OP} +function opscenarios(ts::TwoLevel{S1,T,RepresentativePeriods{S2,T,OP}}) where {S1,S2,T,OP} return collect( - Iterators.flatten(opscenarios(rp) for sp in strategic_periods(ts) for rp in repr_periods(sp)), + Iterators.flatten( + opscenarios(rp) for sp in strategic_periods(ts) for rp in repr_periods(sp) + ), ) end diff --git a/src/op_scenarios/tree_periods.jl b/src/op_scenarios/tree_periods.jl index 32c2fa6..9a20e88 100644 --- a/src/op_scenarios/tree_periods.jl +++ b/src/op_scenarios/tree_periods.jl @@ -108,8 +108,7 @@ Base.eltype(_::StratNodeOpScens) = StratNodeOperationalScenario A structure representing a single operational scenario for a representative period in A [`TwoLevelTree`](@ref) structure supporting iteration over its time periods. """ -struct StratNodeReprOpScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} +struct StratNodeReprOpScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} sp::Int branch::Int rp::Int @@ -154,11 +153,13 @@ end # Adding methods to existing Julia functions function Base.show(io::IO, osc::StratNodeReprOpScenario) - return print(io, "sp$(_strat_per(osc))-br$(_branch(osc))-rp$(_rper(osc))-sc$(_opscen(osc))") + return print( + io, + "sp$(_strat_per(osc))-br$(_branch(osc))-rp$(_rper(osc))-sc$(_opscen(osc))", + ) end Base.eltype(_::StratNodeReprOpScenario) = TreePeriod - """ struct StratNodeReprOpScens <: AbstractTreeStructure @@ -190,9 +191,7 @@ _oper_struct(oscs::StratNodeReprOpScens) = oscs.opscens When the `TimeStructure` is a [`StratNodeReprPeriod`](@ref) with a [`RepresentativePeriod`](@ref), `opscenarios` returns the iterator [`StratNodeReprOpScens`](@ref). """ -function opscenarios( - rp::StratNodeReprPeriod{T,RepresentativePeriod{T,OP}}, -) where {T,OP} +function opscenarios(rp::StratNodeReprPeriod{T,RepresentativePeriod{T,OP}}) where {T,OP} return StratNodeReprOpScens( _strat_per(rp), _branch(rp), @@ -209,9 +208,7 @@ When the `TimeStructure` is a [`StratNodeReprPeriod`](@ref) with a [`SingleReprP `opscenarios` returns the iterator [`StratNodeOpScens`](@ref) as the overall time structure does not include representative periods. """ -function opscenarios( - rp::StratNodeReprPeriod{T,SingleReprPeriod{T,OP}}, -) where {T,OP} +function opscenarios(rp::StratNodeReprPeriod{T,SingleReprPeriod{T,OP}}) where {T,OP} return StratNodeOpScens( _strat_per(rp), _branch(rp), @@ -256,17 +253,14 @@ These are equivalent to a [`StratOperationalScenario`](@ref) and [`StratReprOpsc of a [`TwoLevel`](@ref) time structure. """ function opscenarios(ts::TwoLevelTree) - return collect( - Iterators.flatten(opscenarios(sp) for sp in strat_periods(ts)), - ) + return collect(Iterators.flatten(opscenarios(sp) for sp in strat_periods(ts))) end function opscenarios( ts::TwoLevelTree{T,StratNode{S,T,OP}}, ) where {S,T,OP<:RepresentativePeriods} return collect( Iterators.flatten( - opscenarios(rp) for sp in strat_periods(ts) for - rp in repr_periods(sp) + opscenarios(rp) for sp in strat_periods(ts) for rp in repr_periods(sp) ), ) end diff --git a/src/profiles.jl b/src/profiles.jl index f5ae669..9a82015 100644 --- a/src/profiles.jl +++ b/src/profiles.jl @@ -14,10 +14,7 @@ struct FixedProfile{T<:Number} <: TimeProfile{T} val::T end -function Base.getindex( - fp::FixedProfile, - _::T, -) where {T<:Union{TimePeriod,TimeStructure}} +function Base.getindex(fp::FixedProfile, _::T) where {T<:Union{TimePeriod,TimeStructure}} return fp.val end @@ -68,14 +65,11 @@ function StrategicProfile(vals::Vector{<:Number}) end function _value_lookup(::HasStratIndex, sp::StrategicProfile, period) - return sp.vals[_strat_per(period) > length(sp.vals) ? end : - _strat_per(period)][period] + return sp.vals[_strat_per(period) > length(sp.vals) ? end : _strat_per(period)][period] end function _value_lookup(::NoStratIndex, sp::StrategicProfile, period) - return error( - "Type $(typeof(period)) can not be used as index for a strategic profile", - ) + return error("Type $(typeof(period)) can not be used as index for a strategic profile") end function Base.getindex( @@ -113,9 +107,7 @@ function _value_lookup(::HasScenarioIndex, sp::ScenarioProfile, period) end function _value_lookup(::NoScenarioIndex, sp::ScenarioProfile, period) - return error( - "Type $(typeof(period)) can not be used as index for a scenario profile", - ) + return error("Type $(typeof(period)) can not be used as index for a scenario profile") end function Base.getindex( @@ -190,28 +182,16 @@ struct StrategicStochasticProfile{T<:Number,P<:TimeProfile{T}} <: TimeProfile{T} vals::Vector{<:Vector{P}} end function StrategicStochasticProfile(vals::Vector{<:Vector{<:Number}}) - return StrategicStochasticProfile([ - [FixedProfile(v_2) for v_2 in v_1] for v_1 in vals - ]) + return StrategicStochasticProfile([[FixedProfile(v_2) for v_2 in v_1] for v_1 in vals]) end -function _value_lookup( - ::HasStratTreeIndex, - ssp::StrategicStochasticProfile, - period, -) - sp_prof = ssp.vals[_strat_per(period) > length(ssp.vals) ? end : - _strat_per(period)] - branch_prof = - sp_prof[_branch(period) > length(sp_prof) ? end : _branch(period)] +function _value_lookup(::HasStratTreeIndex, ssp::StrategicStochasticProfile, period) + sp_prof = ssp.vals[_strat_per(period) > length(ssp.vals) ? end : _strat_per(period)] + branch_prof = sp_prof[_branch(period) > length(sp_prof) ? end : _branch(period)] return branch_prof[period] end -function _value_lookup( - ::NoStratTreeIndex, - ssp::StrategicStochasticProfile, - period, -) +function _value_lookup(::NoStratTreeIndex, ssp::StrategicStochasticProfile, period) return error( "Type $(typeof(period)) can not be used as index for a strategic stochastic profile", ) @@ -234,9 +214,7 @@ function Base.getindex( end function Base.getindex(TP::TimeProfile, inds::Any) - return error( - "Type $(typeof(inds)) can not be used as index for a $(typeof(TP))", - ) + return error("Type $(typeof(inds)) can not be used as index for a $(typeof(TP))") end import Base: +, -, *, / diff --git a/src/representative/core_types.jl b/src/representative/core_types.jl index b0e8c16..77fa15a 100644 --- a/src/representative/core_types.jl +++ b/src/representative/core_types.jl @@ -40,8 +40,7 @@ RepresentativePeriods(2, 8760, SimpleTimes(24, 1)) RepresentativePeriods(8760, [SimpleTimes(24, 1), SimpleTimes(24,1)]) ``` """ -struct RepresentativePeriods{S<:Duration,T,OP<:TimeStructure{T}} <: - TimeStructure{T} +struct RepresentativePeriods{S<:Duration,T,OP<:TimeStructure{T}} <: TimeStructure{T} len::Int duration::S period_share::Vector{Float64} @@ -64,7 +63,7 @@ struct RepresentativePeriods{S<:Duration,T,OP<:TimeStructure{T}} <: "The length of `rep_periods` cannot be less than the field `len` of `RepresentativePeriods`.", ), ) - elseif sum(period_share) > 1+1e-6 || sum(period_share) < 1-1e-6 + elseif sum(period_share) > 1 + 1e-6 || sum(period_share) < 1 - 1e-6 @warn( "The sum of the `period_share` vector is given by $(sum(period_share)). " * "This can lead to unexpected behaviour." @@ -95,12 +94,7 @@ function RepresentativePeriods( period_share::Vector{<:Real}, rep_periods::Vector{<:TimeStructure{T}}, ) where {S<:Duration,T} - return RepresentativePeriods( - length(rep_periods), - duration, - period_share, - rep_periods, - ) + return RepresentativePeriods(length(rep_periods), duration, period_share, rep_periods) end function RepresentativePeriods( duration::S, @@ -161,7 +155,6 @@ function Base.last(rpers::RepresentativePeriods) return ReprPeriod(rpers, per, rpers.len) end - """ ReprPeriod{P} <: TimePeriod where {P<:TimePeriod} diff --git a/src/representative/rep_periods.jl b/src/representative/rep_periods.jl index 2819cdf..e7c7e91 100644 --- a/src/representative/rep_periods.jl +++ b/src/representative/rep_periods.jl @@ -14,10 +14,7 @@ end isfirst(rp::AbstractRepresentativePeriod) = _rper(rp) == 1 mult_repr(rp::AbstractRepresentativePeriod) = 1 -function Base.isless( - rp1::AbstractRepresentativePeriod, - rp2::AbstractRepresentativePeriod, -) +function Base.isless(rp1::AbstractRepresentativePeriod, rp2::AbstractRepresentativePeriod) return _rper(rp1) < _rper(rp2) end @@ -36,8 +33,7 @@ RepresentativeIndexable(::Type{<:TimePeriod}) = HasReprIndex() A type representing a single representative period supporting iteration over its time periods. It is created when iterating through [`SingleReprPeriodWrapper`](@ref). """ -struct SingleReprPeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} +struct SingleReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} ts::OP end @@ -95,8 +91,7 @@ repr_periods(ts::TimeStructure) = SingleReprPeriodWrapper(ts) A type representing a single representative period supporting iteration over its time periods. It is created when iterating through [`ReprPeriods`](@ref). """ -struct RepresentativePeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} +struct RepresentativePeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} rp::Int mult_rp::Float64 operational::OP @@ -123,9 +118,7 @@ function Base.eltype(_::Type{RepresentativePeriod{T,OP}}) where {T,OP} return ReprPeriod{eltype(OP)} end function Base.iterate(rp::RepresentativePeriod, state = nothing) - next = - isnothing(state) ? iterate(rp.operational) : - iterate(rp.operational, state) + next = isnothing(state) ? iterate(rp.operational) : iterate(rp.operational, state) isnothing(next) && return nothing return ReprPeriod(rp, next[1]), next[2] diff --git a/src/representative/strat_periods.jl b/src/representative/strat_periods.jl index c00efb4..d6dae00 100644 --- a/src/representative/strat_periods.jl +++ b/src/representative/strat_periods.jl @@ -4,8 +4,7 @@ A type representing a single representative period supporting iteration over its time periods. It is created when iterating through [`StratReprPeriods`](@ref). """ -struct StratReprPeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} +struct StratReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} sp::Int rp::Int mult_sp::Float64 @@ -36,9 +35,7 @@ end Base.length(rp::StratReprPeriod) = length(rp.operational) Base.eltype(_::Type{StratReprPeriod{T,OP}}) where {T,OP} = OperationalPeriod function Base.iterate(rp::StratReprPeriod, state = nothing) - next = - isnothing(state) ? iterate(rp.operational) : - iterate(rp.operational, state) + next = isnothing(state) ? iterate(rp.operational) : iterate(rp.operational, state) isnothing(next) && return nothing return OperationalPeriod(rp, next[1]), next[2] @@ -84,9 +81,7 @@ end # Add basic functions of iterators Base.length(rpers::StratReprPeriods) = length(rpers.repr) function Base.iterate(rpers::StratReprPeriods, state = (nothing, 1)) - next = - isnothing(state[1]) ? iterate(rpers.repr) : - iterate(rpers.repr, state[1]) + next = isnothing(state[1]) ? iterate(rpers.repr) : iterate(rpers.repr, state[1]) isnothing(next) && return nothing return StratReprPeriod(rpers, state[2], next[1]), (next[2], state[2] + 1) @@ -112,7 +107,5 @@ When the `TimeStructure` is a [`TwoLevel`](@ref), `repr_periods` returns an `Arr all [`StratReprPeriod`](@ref)s. """ function repr_periods(ts::TwoLevel) - return collect( - Iterators.flatten(repr_periods(sp) for sp in strategic_periods(ts)), - ) + return collect(Iterators.flatten(repr_periods(sp) for sp in strategic_periods(ts))) end diff --git a/src/representative/tree_periods.jl b/src/representative/tree_periods.jl index cd1b51b..21e13a9 100644 --- a/src/representative/tree_periods.jl +++ b/src/representative/tree_periods.jl @@ -7,8 +7,7 @@ A structure representing a single representative period of a [`StratNode`](@ref) It is equivalent to a [`StratReprPeriod`](@ref) of a [`TwoLevel`](@ref) time structure when utilizing a [`TwoLevelTree`](@ref). """ -struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: - AbstractRepresentativePeriod{T} +struct StratNodeReprPeriod{T,OP<:TimeStructure{T}} <: AbstractRepresentativePeriod{T} sp::Int branch::Int rp::Int @@ -105,7 +104,5 @@ all [`StratNodeReprPeriod`](@ref)s. These are equivalent to a [`StratReprPeriod`](@ref) of a [`TwoLevel`](@ref) time structure. """ function repr_periods(ts::TwoLevelTree) - return collect( - Iterators.flatten(repr_periods(sp) for sp in strat_periods(ts)), - ) + return collect(Iterators.flatten(repr_periods(sp) for sp in strat_periods(ts))) end diff --git a/src/strat_scenarios/core_types.jl b/src/strat_scenarios/core_types.jl index 2c40543..f61fd34 100644 --- a/src/strat_scenarios/core_types.jl +++ b/src/strat_scenarios/core_types.jl @@ -20,11 +20,9 @@ function TwoLevelTree{S,T,OP}( return TwoLevelTree{S,T,OP}(0, nothing, nodes, op_per_strat) end - function _multiple_adj(itr::TwoLevelTree, n) mult = - itr.nodes[n].duration * itr.op_per_strat / - _total_duration(itr.nodes[n].operational) + itr.nodes[n].duration * itr.op_per_strat / _total_duration(itr.nodes[n].operational) return stripunit(mult) end strat_nodes(ts::TwoLevelTree) = ts.nodes @@ -46,9 +44,7 @@ Base.eltype(::Type{TwoLevelTree{S,T,OP}}) where {S,T,OP} = eltype(OP) function Base.iterate(itr::TwoLevelTree, state = (1, nothing)) i = state[1] n = itr.nodes[i] - next = - isnothing(state[2]) ? iterate(n.operational) : - iterate(n.operational, state[2]) + next = isnothing(state[2]) ? iterate(n.operational) : iterate(n.operational, state[2]) if next === nothing i = i + 1 if i > length(itr.nodes) @@ -93,7 +89,6 @@ multiple(t::TreePeriod) = t.multiple probability_branch(t::TreePeriod) = t.prob_branch probability(t::TreePeriod) = probability(t.period) * probability_branch(t) - function Base.show(io::IO, t::TreePeriod) return print(io, "sp$(t.sp)-br$(t.branch)-$(t.period)") end @@ -102,10 +97,7 @@ function Base.isless(t1::TreePeriod, t2::TreePeriod) end # Convenient constructors for the individual types -function TreePeriod( - n::StratNode, - per::P, -) where {P<:Union{TimePeriod,TimeStructure}} +function TreePeriod(n::StratNode, per::P) where {P<:Union{TimePeriod,TimeStructure}} mult = n.mult_sp * multiple(per) return TreePeriod(_strat_per(n), _branch(n), probability_branch(n), mult, per) end @@ -219,15 +211,7 @@ function add_node( if sp < tree.len for i in 1:branching[sp] # TODO: consider branching probability as input, but use uniform for now - add_node( - tree, - node, - sp + 1, - duration, - 1.0 / branching[sp], - branching, - oper, - ) + add_node(tree, node, sp + 1, duration, 1.0 / branching[sp], branching, oper) end end end @@ -248,10 +232,7 @@ function regular_tree( ts::OP; op_per_strat::Real = 1.0, ) where {S,T,OP<:TimeStructure{T}} - tree = TwoLevelTree{S,T,StratNode{S,T,OP}}( - Vector{StratNode{S,T,OP}}(), - op_per_strat, - ) + tree = TwoLevelTree{S,T,StratNode{S,T,OP}}(Vector{StratNode{S,T,OP}}(), op_per_strat) tree.len = length(branching) + 1 add_node(tree, nothing, 1, duration, 1.0, branching, ts) diff --git a/src/strat_scenarios/tree_periods.jl b/src/strat_scenarios/tree_periods.jl index 9baffff..aad0915 100644 --- a/src/strat_scenarios/tree_periods.jl +++ b/src/strat_scenarios/tree_periods.jl @@ -66,9 +66,7 @@ Base.show(io::IO, n::StratNode) = print(io, "sp$(n.sp)-br$(n.branch)") Base.length(n::StratNode) = length(n.operational) Base.eltype(::Type{StratNode{S,T,OP}}) where {S,T,OP} = TreePeriod{eltype(OP)} function Base.iterate(n::StratNode, state = nothing) - next = - isnothing(state) ? iterate(n.operational) : - iterate(n.operational, state) + next = isnothing(state) ? iterate(n.operational) : iterate(n.operational, state) next === nothing && return nothing return TreePeriod(n, next[1]), next[2] diff --git a/src/strategic/core_types.jl b/src/strategic/core_types.jl index cdc8d89..a53f03f 100644 --- a/src/strategic/core_types.jl +++ b/src/strategic/core_types.jl @@ -109,20 +109,13 @@ function TwoLevel( return TwoLevel(len, fill(duration, len), oper, op_per_strat) end -function TwoLevel( - len::Integer, - oper::TimeStructure{T}; - op_per_strat = 1.0, -) where {T} +function TwoLevel(len::Integer, oper::TimeStructure{T}; op_per_strat = 1.0) where {T} oper = fill(oper, len) dur = [_total_duration(op) / op_per_strat for op in oper] return TwoLevel(len, dur, oper, op_per_strat) end -function TwoLevel( - oper::Vector{<:TimeStructure{T}}; - op_per_strat = 1.0, -) where {T} +function TwoLevel(oper::Vector{<:TimeStructure{T}}; op_per_strat = 1.0) where {T} len = length(oper) dur = [_total_duration(op) / op_per_strat for op in oper] return TwoLevel(len, dur, oper, op_per_strat) @@ -166,9 +159,7 @@ end _total_duration(ts::TwoLevel) = sum(ts.duration) function _multiple_adj(ts::TwoLevel, sp) - mult = - ts.duration[sp] * ts.op_per_strat / - _total_duration(ts.operational[sp]) + mult = ts.duration[sp] * ts.op_per_strat / _total_duration(ts.operational[sp]) return stripunit(mult) end @@ -196,7 +187,6 @@ function Base.last(ts::TwoLevel) return OperationalPeriod(ts, per, ts.len) end - """ struct OperationalPeriod <: TimePeriod diff --git a/src/strategic/strat_periods.jl b/src/strategic/strat_periods.jl index 16d2675..ea43cec 100644 --- a/src/strategic/strat_periods.jl +++ b/src/strategic/strat_periods.jl @@ -30,10 +30,7 @@ StrategicIndexable(::Type) = NoStratIndex() StrategicIndexable(::Type{<:AbstractStrategicPeriod}) = HasStratIndex() StrategicIndexable(::Type{<:TimePeriod}) = HasStratIndex() -function start_time( - sp::AbstractStrategicPeriod{S,T}, - ts::TimeStructure{T}, -) where {S,T} +function start_time(sp::AbstractStrategicPeriod{S,T}, ts::TimeStructure{T}) where {S,T} return isfirst(sp) ? zero(S) : sum(duration_strat(spp) for spp in strategic_periods(ts) if spp < sp) end @@ -43,20 +40,16 @@ function end_time(sp::AbstractStrategicPeriod, ts::TimeStructure) end function remaining(sp::AbstractStrategicPeriod, ts::TimeStructure) - return sum( - duration_strat(spp) for spp in strategic_periods(ts) if spp >= sp - ) + return sum(duration_strat(spp) for spp in strategic_periods(ts) if spp >= sp) end - """ SingleStrategicPeriodWrapper{T,SP<:TimeStructure{T}} <: AbstractStrategicPeriod{T,T} A type representing a single strategic period supporting iteration over its time periods. It is created when iterating through [`SingleStrategicPeriodWrapper`](@ref). """ -struct SingleStrategicPeriod{T,SP<:TimeStructure{T}} <: - AbstractStrategicPeriod{T,T} +struct SingleStrategicPeriod{T,SP<:TimeStructure{T}} <: AbstractStrategicPeriod{T,T} ts::SP end @@ -146,9 +139,7 @@ function Base.eltype(_::Type{StrategicPeriod{S,T,OP}}) where {S,T,OP} return OperationalPeriod end function Base.iterate(sp::StrategicPeriod, state = nothing) - next = - isnothing(state) ? iterate(sp.operational) : - iterate(sp.operational, state) + next = isnothing(state) ? iterate(sp.operational) : iterate(sp.operational, state) next === nothing && return nothing return OperationalPeriod(sp, next[1]), next[2] diff --git a/src/utils.jl b/src/utils.jl index 87c55a6..4b9438f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -63,10 +63,7 @@ function Base.iterate(w::Chunk, state = nothing) if w.cyclic itr = Iterators.cycle(w.itr) end - next = Iterators.take( - isnothing(state) ? itr : Iterators.rest(itr, state), - w.ns, - ) + next = Iterators.take(isnothing(state) ? itr : Iterators.rest(itr, state), w.ns) return next, n[2] end @@ -114,10 +111,7 @@ function Base.iterate(w::ChunkDuration, state = nothing) if w.cyclic itr = Iterators.cycle(w.itr) end - next = take_duration( - isnothing(state) ? itr : Iterators.rest(itr, state...), - w.duration, - ) + next = take_duration(isnothing(state) ? itr : Iterators.rest(itr, state...), w.duration) return next, n[2] end @@ -141,11 +135,8 @@ function expand_dataframe!(df, periods) end # All introduced subtypes require the same procedures for the iteration and indexing. # Hence, all introduced types use the same functions. -TreeStructure = Union{ - StratNodeOperationalScenario, - StratNodeReprPeriod, - StratNodeReprOpScenario, -} +TreeStructure = + Union{StratNodeOperationalScenario,StratNodeReprPeriod,StratNodeReprOpScenario} Base.length(ts::TreeStructure) = length(ts.operational) function Base.last(ts::TreeStructure) per = last(ts.operational) @@ -160,9 +151,7 @@ function Base.eachindex(ts::TreeStructure) return eachindex(ts.operational) end function Base.iterate(ts::TreeStructure, state = nothing) - next = - isnothing(state) ? iterate(ts.operational) : - iterate(ts.operational, state) + next = isnothing(state) ? iterate(ts.operational) : iterate(ts.operational, state) isnothing(next) && return nothing return TreePeriod(ts, next[1]), next[2] diff --git a/test/runtests.jl b/test/runtests.jl index aa0c735..8ac9c4a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -52,26 +52,21 @@ end year = CalendarTimes(DateTime(2024, 1, 1), 12, Month(1)) @test length(year) == 12 - @test first(year) == TimeStruct.CalendarPeriod( - DateTime(2024, 1, 1), - DateTime(2024, 2, 1), - 1, - ) + @test first(year) == + TimeStruct.CalendarPeriod(DateTime(2024, 1, 1), DateTime(2024, 2, 1), 1) @test TimeStruct._total_duration(year) == 366 * 24 months = collect(year) @test duration(months[2]) == 29 * 24 # 10 weeks with reduced length due to DST - periods = - CalendarTimes(DateTime(2023, 3, 1), tz"Europe/Berlin", 10, Week(1)) + periods = CalendarTimes(DateTime(2023, 3, 1), tz"Europe/Berlin", 10, Week(1)) dur = [duration(w) for w in periods] @test TimeStruct._total_duration(periods) == 10 * 168 - 1 @test dur[4] == 167 # 10 weeks not affected by DST - periods = - CalendarTimes(DateTime(2023, 4, 1), tz"Europe/Berlin", 10, Week(1)) + periods = CalendarTimes(DateTime(2023, 4, 1), tz"Europe/Berlin", 10, Week(1)) dur = [duration(w) for w in periods] @test TimeStruct._total_duration(periods) == 10 * 168 @test dur[4] == 168 @@ -108,8 +103,7 @@ end scen_coll = collect(scens) @test length(scen_coll) == 5 - @test typeof(scen_coll[3]) == - TimeStruct.OperationalScenario{Int,SimpleTimes{Int}} + @test typeof(scen_coll[3]) == TimeStruct.OperationalScenario{Int,SimpleTimes{Int}} @test probability(scen_coll[3]) == 0.2 @test length(scen_coll[3]) == 10 @@ -122,8 +116,7 @@ end week = SimpleTimes(168, 1) ts = OperationalScenarios(3, [day, day, week], [0.1, 0.2, 0.7]) - @test first(ts) == - TimeStruct.ScenarioPeriod(1, 0.1, 7.0, TimeStruct.SimplePeriod(1, 1)) + @test first(ts) == TimeStruct.ScenarioPeriod(1, 0.1, 7.0, TimeStruct.SimplePeriod(1, 1)) @test length(ts) == 216 pers = [] for sc in opscenarios(ts) @@ -140,8 +133,7 @@ end # resolution and the same probability of occuring ts = OperationalScenarios([day, week]) - @test first(ts) == - TimeStruct.ScenarioPeriod(1, 0.5, 7.0, TimeStruct.SimplePeriod(1, 1)) + @test first(ts) == TimeStruct.ScenarioPeriod(1, 0.5, 7.0, TimeStruct.SimplePeriod(1, 1)) @test length(ts) == 192 scens = opscenarios(ts) @@ -150,8 +142,7 @@ end scen_coll = collect(scens) @test length(scen_coll) == 2 - @test typeof(scen_coll[2]) == - TimeStruct.OperationalScenario{Int,SimpleTimes{Int}} + @test typeof(scen_coll[2]) == TimeStruct.OperationalScenario{Int,SimpleTimes{Int}} @test probability(scen_coll[2]) == 0.5 @test repr(scen_coll[1]) == "sc-1" @@ -177,12 +168,8 @@ end end @testitem "RepresentativePeriods" begin - rep = RepresentativePeriods( - 2, - 8760, - [0.4, 0.6], - [SimpleTimes(24, 1), SimpleTimes(24, 1)], - ) + rep = + RepresentativePeriods(2, 8760, [0.4, 0.6], [SimpleTimes(24, 1), SimpleTimes(24, 1)]) # Test of direct functions of `RepresentativePeriods` rps = collect(rep) @@ -248,18 +235,14 @@ end ts_5 = RepresentativePeriods(8760, [0.5, 0.5], day) fields = fieldnames(typeof(ts_1)) - @test sum( - getfield(ts_1, field) == getfield(ts_2, field) for field in fields - ) == length(fields) - @test sum( - getfield(ts_1, field) == getfield(ts_3, field) for field in fields - ) == length(fields) - @test sum( - getfield(ts_1, field) == getfield(ts_4, field) for field in fields - ) == length(fields) - @test sum( - getfield(ts_1, field) == getfield(ts_5, field) for field in fields - ) == length(fields) + @test sum(getfield(ts_1, field) == getfield(ts_2, field) for field in fields) == + length(fields) + @test sum(getfield(ts_1, field) == getfield(ts_3, field) for field in fields) == + length(fields) + @test sum(getfield(ts_1, field) == getfield(ts_4, field) for field in fields) == + length(fields) + @test sum(getfield(ts_1, field) == getfield(ts_5, field) for field in fields) == + length(fields) end @testitem "RepresentativePeriods with units" begin @@ -287,19 +270,16 @@ end @test sum(probability(t) * multiple(t) * duration(t) for t in rep) ≈ 28 @test sum( - probability(t) * multiple(t) * duration(t) for rp in repr_periods(rep) - for t in rp + probability(t) * multiple(t) * duration(t) for rp in repr_periods(rep) for t in rp ) ≈ 28 @test sum( - probability(t) * multiple(t) * duration(t) for rp in repr_periods(rep) - for sc in opscenarios(rp) for t in sc + probability(t) * multiple(t) * duration(t) for rp in repr_periods(rep) for + sc in opscenarios(rp) for t in sc ) ≈ 28 pers = collect(rep) pers_rep = collect(t for rp in repr_periods(rep) for t in rp) - pers_scen = collect( - t for rp in repr_periods(rep) for sc in opscenarios(rp) for t in sc - ) + pers_scen = collect(t for rp in repr_periods(rep) for sc in opscenarios(rp) for t in sc) @test pers == pers_rep @test pers == pers_scen @@ -312,10 +292,8 @@ end pers = collect(rep_simple) pers_rep = collect(t for rp in repr_periods(rep_simple) for t in rp) - pers_scen = collect( - t for rp in repr_periods(rep_simple) for sc in opscenarios(rp) for - t in sc - ) + pers_scen = + collect(t for rp in repr_periods(rep_simple) for sc in opscenarios(rp) for t in sc) @test pers == pers_rep @test pers == pers_scen @@ -326,19 +304,16 @@ end # Test with only OperationalScenarios pers = collect(scenarios) pers_rep = collect(t for rp in repr_periods(scenarios) for t in rp) - pers_scen = collect( - t for rp in repr_periods(scenarios) for sc in opscenarios(rp) for - t in sc - ) + pers_scen = + collect(t for rp in repr_periods(scenarios) for sc in opscenarios(rp) for t in sc) # Test with just SimpleTimes simple = SimpleTimes(10, 1) pers = collect(simple) pers_rep = collect(t for rp in repr_periods(simple) for t in rp) - pers_scen = collect( - t for rp in repr_periods(simple) for sc in opscenarios(rp) for t in sc - ) + pers_scen = + collect(t for rp in repr_periods(simple) for sc in opscenarios(rp) for t in sc) @test pers == pers_rep @test pers == pers_scen @@ -355,12 +330,11 @@ end @test sum(probability(t) * duration(t) * multiple(t) for t in rep) ≈ 70u"d" @test sum( - probability(t) * duration(t) * multiple(t) for rp in repr_periods(rep) - for t in rp + probability(t) * duration(t) * multiple(t) for rp in repr_periods(rep) for t in rp ) ≈ 70u"d" @test sum( - probability(t) * duration(t) * multiple(t) for rp in repr_periods(rep) - for sc in opscenarios(rp) for t in sc + probability(t) * duration(t) * multiple(t) for rp in repr_periods(rep) for + sc in opscenarios(rp) for t in sc ) ≈ 70u"d" end @@ -462,9 +436,7 @@ end @testitem "TwoLevel with CalendarTimes" begin using Dates - years = TwoLevel([ - CalendarTimes(DateTime(y, 1, 1), 12, Month(1)) for y in 2023:2032 - ]) + years = TwoLevel([CalendarTimes(DateTime(y, 1, 1), 12, Month(1)) for y in 2023:2032]) @test length(years) == length(collect(years)) @@ -479,10 +451,7 @@ end @test duration(m) == 30 * 24 @test TimeStruct.start_date(m.period) == DateTime(2023, 4, 1) - scens = OperationalScenarios( - 4, - CalendarTimes(DateTime(2023, 9, 1), 10, Hour(1)), - ) + scens = OperationalScenarios(4, CalendarTimes(DateTime(2023, 9, 1), 10, Hour(1))) ts = TwoLevel(5, 1, scens; op_per_strat = 8760) pers = collect(ts) @@ -535,8 +504,7 @@ end end @test issetequal(pers, pers_sp) - @test sum(length(opscenarios(sp)) for sp in strat_periods(seasonal_year)) == - 12 + @test sum(length(opscenarios(sp)) for sp in strat_periods(seasonal_year)) == 12 @test length(pers) == length(seasonal_year) @test typeof(first(pers)) == typeof(first(seasonal_year)) scen = first(opscenarios(first(strat_periods(seasonal_year)))) @@ -569,10 +537,7 @@ end per = first(scen) @test typeof(per) <: TimeStruct.SimplePeriod{Float64} - ts = OperationalScenarios( - [SimpleTimes(5, 1.5), SimpleTimes(10, 1.0)], - [0.9, 0.1], - ) + ts = OperationalScenarios([SimpleTimes(5, 1.5), SimpleTimes(10, 1.0)], [0.9, 0.1]) pers = [t for sp in strat_periods(ts) for sc in opscenarios(sp) for t in sc] pers_ts = [t for t in ts] @test pers == pers_ts @@ -588,12 +553,7 @@ end ts = TwoLevel( 3, 5 * 8760, - RepresentativePeriods( - 2, - 8760, - [0.7, 0.3], - [opscen_summer, opscen_winter], - ); + RepresentativePeriods(2, 8760, [0.7, 0.3], [opscen_summer, opscen_winter]); op_per_strat = 1.0, ) @@ -606,14 +566,10 @@ end pers_rp = collect(t for rp in repr_periods(ts) for t in rp) @test issetequal(pers, pers_rp) - pers_rp_os = collect( - t for rp in repr_periods(ts) for sc in opscenarios(rp) for t in sc - ) + pers_rp_os = collect(t for rp in repr_periods(ts) for sc in opscenarios(rp) for t in sc) @test issetequal(pers, pers_rp_os) - pers_os = collect( - t for sp in strat_periods(ts) for sc in opscenarios(sp) for t in sc - ) + pers_os = collect(t for sp in strat_periods(ts) for sc in opscenarios(sp) for t in sc) end @testitem "SimpleTimes as TwoLevel" begin @@ -656,78 +612,55 @@ end pers_sp = collect(t for sp in strat_periods(periods) for t in sp) @test issetequal(pers, pers_sp) - pers_rp_sc = collect( - t for rp in repr_periods(periods) for sc in opscenarios(rp) for - t in sc - ) + pers_rp_sc = + collect(t for rp in repr_periods(periods) for sc in opscenarios(rp) for t in sc) @test issetequal(pers, pers_rp_sc) pers_sp_rp = collect( - t for sp in strat_periods(periods) for rp in repr_periods(sp) - for t in rp + t for sp in strat_periods(periods) for rp in repr_periods(sp) for t in rp ) @test issetequal(pers, pers_sp_rp) pers_sp_sc = collect( - t for sp in strat_periods(periods) for sc in opscenarios(sp) for - t in sc + t for sp in strat_periods(periods) for sc in opscenarios(sp) for t in sc ) @test issetequal(pers, pers_sp_sc) pers_sp_rp_sc = collect( - t for sp in strat_periods(periods) for rp in repr_periods(sp) - for sc in opscenarios(rp) for t in sc + t for sp in strat_periods(periods) for rp in repr_periods(sp) for + sc in opscenarios(rp) for t in sc ) @test issetequal(pers, pers_sp_rp_sc) repr_pers = collect(rp for rp in repr_periods(periods)) - repr_pers_sp = collect( - rp for sp in strat_periods(periods) for rp in repr_periods(sp) - ) + repr_pers_sp = + collect(rp for sp in strat_periods(periods) for rp in repr_periods(sp)) @test issetequal(repr_pers, repr_pers_sp) opscens = collect(sc for sc in opscenarios(periods)) - opscens_sp = collect( - sc for sp in strat_periods(periods) for sc in opscenarios(sp) - ) + opscens_sp = collect(sc for sp in strat_periods(periods) for sc in opscenarios(sp)) @test issetequal(opscens, opscens_sp) opscens_rp_sp = collect( - sc for sp in strat_periods(periods) for rp in repr_periods(sp) - for sc in opscenarios(rp) + sc for sp in strat_periods(periods) for rp in repr_periods(sp) for + sc in opscenarios(rp) ) @test issetequal(opscens, opscens_rp_sp) end test_invariants(SimpleTimes(10, 1)) - test_invariants( - CalendarTimes(Dates.DateTime(2023, 1, 1), 12, Dates.Month(1)), - ) + test_invariants(CalendarTimes(Dates.DateTime(2023, 1, 1), 12, Dates.Month(1))) test_invariants(OperationalScenarios(3, SimpleTimes(10, 1))) test_invariants( - RepresentativePeriods( - 2, - 10, - [0.2, 0.8], - [SimpleTimes(10, 1), SimpleTimes(5, 1)], - ), - ) - opscen = OperationalScenarios( - 2, - [SimpleTimes(10, 1), SimpleTimes(5, 3)], - [0.4, 0.6], + RepresentativePeriods(2, 10, [0.2, 0.8], [SimpleTimes(10, 1), SimpleTimes(5, 1)]), ) + opscen = OperationalScenarios(2, [SimpleTimes(10, 1), SimpleTimes(5, 3)], [0.4, 0.6]) repr_op = RepresentativePeriods(2, 10, [0.2, 0.8], [opscen, opscen]) test_invariants(repr_op) test_invariants(TwoLevel(5, 10, SimpleTimes(10, 1))) test_invariants(TwoLevel(5, 30, opscen)) - repr = RepresentativePeriods( - 2, - 20, - [0.2, 0.8], - [SimpleTimes(5, 1), SimpleTimes(5, 1)], - ) + repr = RepresentativePeriods(2, 20, [0.2, 0.8], [SimpleTimes(5, 1), SimpleTimes(5, 1)]) two_level = TwoLevel(100, [repr, repr, repr]; op_per_strat = 1.0) test_invariants(two_level) @@ -742,10 +675,8 @@ end function test_last(periods) @test last(periods) == last(collect(periods)) - @test last(strategic_periods(periods)) == - last(collect(strategic_periods(periods))) - @test last(repr_periods(periods)) == - last(collect(repr_periods(periods))) + @test last(strategic_periods(periods)) == last(collect(strategic_periods(periods))) + @test last(repr_periods(periods)) == last(collect(repr_periods(periods))) for sp in strategic_periods(periods) @test last(sp) == last(collect(sp)) @@ -776,19 +707,11 @@ end periods = CalendarTimes(Dates.DateTime(2023, 1, 1), 12, Dates.Month(1)) test_last(periods) - periods = RepresentativePeriods( - 2, - 20, - [0.2, 0.8], - [SimpleTimes(5, 1), SimpleTimes(5, 1)], - ) + periods = + RepresentativePeriods(2, 20, [0.2, 0.8], [SimpleTimes(5, 1), SimpleTimes(5, 1)]) test_last(periods) - opscen = OperationalScenarios( - 2, - [SimpleTimes(10, 1), SimpleTimes(5, 3)], - [0.4, 0.6], - ) + opscen = OperationalScenarios(2, [SimpleTimes(10, 1), SimpleTimes(5, 3)], [0.4, 0.6]) test_last(opscen) periods = TwoLevel(2, 1, SimpleTimes(4, 1)) @@ -940,11 +863,9 @@ end ops_inv[5] = [t for sc in opscenarios(ts) for t in sc] ops_inv[6] = [t for sp in sps for sc in opscenarios(sp) for t in sc] - ops_inv[7] = - [t for rp in repr_periods(ts) for sc in opscenarios(rp) for t in sc] + ops_inv[7] = [t for rp in repr_periods(ts) for sc in opscenarios(rp) for t in sc] ops_inv[8] = [ - t for sp in sps for rp in repr_periods(sp) for - sc in opscenarios(rp) for t in sc + t for sp in sps for rp in repr_periods(sp) for sc in opscenarios(rp) for t in sc ] for ops in ops_inv @@ -961,12 +882,9 @@ end oscs_inv[1] = [osc for osc in opscenarios(ts)] oscs_inv[2] = [osc for sp in sps for osc in opscenarios(sp)] - oscs_inv[3] = - [osc for rp in repr_periods(ts) for osc in opscenarios(rp)] - oscs_inv[4] = [ - osc for sp in sps for rp in repr_periods(sp) for - osc in opscenarios(rp) - ] + oscs_inv[3] = [osc for rp in repr_periods(ts) for osc in opscenarios(rp)] + oscs_inv[4] = + [osc for sp in sps for rp in repr_periods(sp) for osc in opscenarios(rp)] for oscs in oscs_inv @test length(oscs) == n_sc @@ -1013,9 +931,7 @@ end nodes = strat_nodes(regtree) for sp in 1:3 - @test sum( - TimeStruct.probability_branch(n) for n in nodes if n.sp == sp - ) ≈ 1.0 + @test sum(TimeStruct.probability_branch(n) for n in nodes if n.sp == sp) ≈ 1.0 end node = nodes[2] @test length(node) == 5 @@ -1049,11 +965,7 @@ end end @testitem "TwoLevelTree with OperationalScenarios" setup = [TwoLevelTreeTest] begin - regtree = TimeStruct.regular_tree( - 5, - [3, 2], - OperationalScenarios(3, SimpleTimes(5, 1)), - ) + regtree = TimeStruct.regular_tree(5, [3, 2], OperationalScenarios(3, SimpleTimes(5, 1))) n_sp = 10 n_sc = n_sp * 3 n_op = n_sc * 5 @@ -1061,11 +973,8 @@ end end @testitem "TwoLevelTree with RepresentativePeriods" setup = [TwoLevelTreeTest] begin - regtree = TimeStruct.regular_tree( - 5, - [3, 2], - RepresentativePeriods(2, 1, SimpleTimes(5, 1)), - ) + regtree = + TimeStruct.regular_tree(5, [3, 2], RepresentativePeriods(2, 1, SimpleTimes(5, 1))) n_sp = 10 n_rp = n_sp * 2 n_op = n_rp * 5 @@ -1087,11 +996,7 @@ end end @testitem "Strategic scenarios with operational scenarios" begin - regtree = TimeStruct.regular_tree( - 5, - [3, 2], - OperationalScenarios(3, SimpleTimes(5, 1)), - ) + regtree = TimeStruct.regular_tree(5, [3, 2], OperationalScenarios(3, SimpleTimes(5, 1))) @test length(TimeStruct.strategic_scenarios(regtree)) == 6 @@ -1100,8 +1005,7 @@ end for (prev_sp, sp) in withprev(sc) if !isnothing(prev_sp) - @test TimeStruct._strat_per(prev_sp) + 1 == - TimeStruct._strat_per(sp) + @test TimeStruct._strat_per(prev_sp) + 1 == TimeStruct._strat_per(sp) end end end @@ -1113,8 +1017,7 @@ end scens = TimeStruct.strategic_scenarios(two_level) @test length(scens) == 1 sps = collect( - sp for sc in TimeStruct.strategic_scenarios(two_level) for - sp in strat_periods(sc) + sp for sc in TimeStruct.strategic_scenarios(two_level) for sp in strat_periods(sc) ) @test length(sps) == 5 end @@ -1141,12 +1044,7 @@ end vals = collect(profile[sp] for sp in strat_periods(ts)) @test vals == [1, 2, 3] - repr = RepresentativePeriods( - 2, - 5, - [0.6, 0.4], - [SimpleTimes(5, 1), SimpleTimes(5, 1)], - ) + repr = RepresentativePeriods(2, 5, [0.6, 0.4], [SimpleTimes(5, 1), SimpleTimes(5, 1)]) ts = TwoLevel(3, 5, repr) vals = collect(profile[sp] for sp in strat_periods(ts)) @@ -1154,11 +1052,7 @@ end end @testitem "Profiles and representative periods" begin - profile = RepresentativeProfile([ - FixedProfile(1), - FixedProfile(2), - FixedProfile(3), - ]) + profile = RepresentativeProfile([FixedProfile(1), FixedProfile(2), FixedProfile(3)]) simple = SimpleTimes(10, 1) rp = first(repr_periods(simple)) @@ -1169,12 +1063,7 @@ end vals = collect(profile[rp] for rp in repr_periods(ts)) @test vals == [1, 1, 1] - repr = RepresentativePeriods( - 2, - 5, - [0.6, 0.4], - [SimpleTimes(5, 1), SimpleTimes(5, 1)], - ) + repr = RepresentativePeriods(2, 5, [0.6, 0.4], [SimpleTimes(5, 1), SimpleTimes(5, 1)]) ts = TwoLevel(3, 5, repr) vals = collect(profile[rp] for rp in repr_periods(ts)) @@ -1189,8 +1078,7 @@ end end @testitem "Profiles and operational scenarios" begin - profile = - ScenarioProfile([FixedProfile(1), FixedProfile(2), FixedProfile(3)]) + profile = ScenarioProfile([FixedProfile(1), FixedProfile(2), FixedProfile(3)]) simple = SimpleTimes(10, 1) sc = first(opscenarios(simple)) @@ -1219,10 +1107,8 @@ end uniform_week = TwoLevel(7, 24, uniform_day) @test first(withprev(uniform_day))[1] === nothing - @test collect(withprev(uniform_week))[25] == ( - nothing, - TimeStruct.OperationalPeriod(2, TimeStruct.SimplePeriod(1, 1), 1.0), - ) + @test collect(withprev(uniform_week))[25] == + (nothing, TimeStruct.OperationalPeriod(2, TimeStruct.SimplePeriod(1, 1), 1.0)) periods = SimpleTimes(10, 1) @@ -1232,8 +1118,7 @@ end @test length(per_next[10]) == 1 @test per_next[1] == collect(Iterators.take(periods, 5)) - per_prev = - collect(collect(ts) for ts in chunk(Iterators.reverse(periods), 5)) + per_prev = collect(collect(ts) for ts in chunk(Iterators.reverse(periods), 5)) @test length(per_prev[1]) == 5 @test length(per_next[7]) == 4 @test length(per_next[10]) == 1 @@ -1282,8 +1167,7 @@ end @test discount(disc, t) == δ^(i - 1) end - @test sum(objective_weight(t, disc) for t in uniform_years) ≈ 8.435 atol = - 1e-3 + @test sum(objective_weight(t, disc) for t in uniform_years) ≈ 8.435 atol = 1e-3 uniform_day = SimpleTimes(24, 1) periods = TwoLevel(10, 8760, uniform_day) @@ -1296,9 +1180,8 @@ end uniform_day = SimpleTimes(24, 1u"hr") periods_unit = TwoLevel(10, 365.125u"d", uniform_day) - @test sum( - objective_weight(sp, disc) for sp in strat_periods(periods_unit) - ) ≈ 8.435 atol = 1e-3 + @test sum(objective_weight(sp, disc) for sp in strat_periods(periods_unit)) ≈ 8.435 atol = + 1e-3 end @testitem "Start and end times" begin @@ -1317,8 +1200,7 @@ end end_t = collect(end_oper_time(t, periods_unit) for t in periods_unit) @test start_t[26] == end_t[25] - tsc = - TwoLevel(3, 168u"hr", OperationalScenarios(3, SimpleTimes(7, 24u"hr"))) + tsc = TwoLevel(3, 168u"hr", OperationalScenarios(3, SimpleTimes(7, 24u"hr"))) start_t = collect(start_oper_time(t, tsc) for t in tsc) end_t = collect(end_oper_time(t, tsc) for t in tsc) @@ -1339,10 +1221,7 @@ end @test hasproperty(df1, :end_time) twolevel = TwoLevel(5, 24, OperationalScenarios(3, SimpleTimes(6, 4))) - df2 = DataFrame( - period = [t for t in twolevel], - value = rand(length(twolevel)), - ) + df2 = DataFrame(period = [t for t in twolevel], value = rand(length(twolevel))) TimeStruct.expand_dataframe!(df2, twolevel) @test hasproperty(df2, :strategic_period) @test hasproperty(df2, :op_scenario) @@ -1351,10 +1230,7 @@ end @test hasproperty(df2, :start_oper_time) @test hasproperty(df2, :end_oper_time) - scen = OperationalScenarios( - 5, - SimpleTimes(4, [2.0u"hr", 3.5u"hr", 10u"hr", 20u"hr"]), - ) + scen = OperationalScenarios(5, SimpleTimes(4, [2.0u"hr", 3.5u"hr", 10u"hr", 20u"hr"])) df3 = DataFrame(per = [t for t in scen], value = rand(length(scen))) TimeStruct.expand_dataframe!(df3, scen) @test hasproperty(df3, :op_scenario) From a0df5205a36bd7a48b6f309b2d484de5ea6b83ab Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 14:19:50 +0200 Subject: [PATCH 18/20] Fixed the error --- src/op_scenarios/opscenarios.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index f2407a7..33dcf75 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -106,7 +106,8 @@ end A type representing a single operational scenario supporting iteration over its time periods. It is created when iterating through [`OpScens`](@ref). """ -struct OperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} +struct OperationalScenario{T,OP<:TimeStructure{T}} <: + AbstractOperationalScenario{T} scen::Int mult_sc::Float64 probability::Float64 @@ -135,7 +136,9 @@ function Base.eltype(_::Type{OperationalScenario{T,OP}}) where {T,OP} return ScenarioPeriod{eltype(OP)} end function Base.iterate(osc::OperationalScenario, state = nothing) - next = isnothing(state) ? iterate(osc.operational) : iterate(osc.operational, state) + next = + isnothing(state) ? iterate(osc.operational) : + iterate(osc.operational, state) next === nothing && return nothing return ScenarioPeriod(osc, next[1]), next[2] end @@ -150,6 +153,7 @@ function Base.eachindex(osc::OperationalScenario) return eachindex(osc.operational) end + """ OpScens{T,OP} @@ -161,6 +165,8 @@ struct OpScens{T,OP} ts::OperationalScenarios{T,OP} end +_oper_it(oscs::OpScens) = oscs.ts + """ When the `TimeStructure` is an [`OperationalScenarios`](@ref), `opscenarios` returns the iterator [`OpScens`](@ref). From 3ab28b993001c63a6ab23c0f0a3a1830122b3b9e Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Tue, 17 Sep 2024 14:42:50 +0200 Subject: [PATCH 19/20] Formatting and removal of nightly --- src/op_scenarios/opscenarios.jl | 8 ++------ src/op_scenarios/strat_periods.jl | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 33dcf75..d27207a 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -106,8 +106,7 @@ end A type representing a single operational scenario supporting iteration over its time periods. It is created when iterating through [`OpScens`](@ref). """ -struct OperationalScenario{T,OP<:TimeStructure{T}} <: - AbstractOperationalScenario{T} +struct OperationalScenario{T,OP<:TimeStructure{T}} <: AbstractOperationalScenario{T} scen::Int mult_sc::Float64 probability::Float64 @@ -136,9 +135,7 @@ function Base.eltype(_::Type{OperationalScenario{T,OP}}) where {T,OP} return ScenarioPeriod{eltype(OP)} end function Base.iterate(osc::OperationalScenario, state = nothing) - next = - isnothing(state) ? iterate(osc.operational) : - iterate(osc.operational, state) + next = isnothing(state) ? iterate(osc.operational) : iterate(osc.operational, state) next === nothing && return nothing return ScenarioPeriod(osc, next[1]), next[2] end @@ -153,7 +150,6 @@ function Base.eachindex(osc::OperationalScenario) return eachindex(osc.operational) end - """ OpScens{T,OP} diff --git a/src/op_scenarios/strat_periods.jl b/src/op_scenarios/strat_periods.jl index cdfa4af..76e837a 100644 --- a/src/op_scenarios/strat_periods.jl +++ b/src/op_scenarios/strat_periods.jl @@ -94,6 +94,7 @@ function StratOperationalScenario(oscs::StratOpScens, scen::Int, per) ) end +# Add basic functions of iterators Base.length(oscs::StratOpScens) = length(_oper_it(oscs)) function Base.iterate(oscs::StratOpScens, state = (nothing, 1)) next = isnothing(state[1]) ? iterate(_oper_it(oscs)) : iterate(_oper_it(oscs), state[1]) From 2d965ceb28d21f8dd6d0d174cb0ea115da729077 Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Wed, 18 Sep 2024 08:17:53 +0200 Subject: [PATCH 20/20] Removed nightly as I forgot it --- .github/workflows/ci.yml | 6 +++--- src/op_scenarios/core_types.jl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a572ef5..049d654 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,9 +28,9 @@ jobs: - version: '1.9' # 1.9 os: ubuntu-latest arch: x86 - - version: 'nightly' - os: ubuntu-latest - arch: x64 + # - version: 'nightly' + # os: ubuntu-latest + # arch: x64 steps: - uses: actions/checkout@v3 - uses: julia-actions/setup-julia@v1 diff --git a/src/op_scenarios/core_types.jl b/src/op_scenarios/core_types.jl index 5fb22d2..5835640 100644 --- a/src/op_scenarios/core_types.jl +++ b/src/op_scenarios/core_types.jl @@ -77,7 +77,7 @@ function _multiple_adj(oscs::OperationalScenarios, scen) return stripunit(_total_duration(oscs) / _total_duration(oscs.scenarios[scen])) end -# Iteration through all time periods for the operational scenarios +# Add basic functions of iterators function Base.length(oscs::OperationalScenarios) return sum(length(oscs.scenarios[osc]) for osc in 1:oscs.len) end