-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Problem statement
As outlined in #73, we currently allow for identifying whether a TimePeriod, AbstractOperationalScenario, or AbstractRepresentativePeriod is within an AbstractStrategicPeriod using the function _sp_period. We require it for the discounting approach to identify which strategic period has to be utilized when the input to discount or objective_weight is not a strategic period. The current system works but has a major issue as outlined in #73: It only checks that the field sp is the same (or sp and branch in the case of TwoLevelTree).
This implies that when working with multiple time structures, e.g., when using Plasmo, we can experience problems.
In addition, we only utilize the current system for strategic periods as we do not need it for a different purpose. It can be easily implemented as well for other types.
Solution
A new implementation for strategic periods is given by
_check_period(sp::AbstractStrategicPeriod, t::TimePeriod) = t in sp
_check_period(sp::AbstractStrategicPeriod, t::AbstractOperationalScenario) = t in opscenarios(sp)
_check_period(sp::AbstractStrategicPeriod, t::AbstractRepresentativePeriod) = t in repr_periods(sp)
_identify_sp(sp::AbstractStrategicPeriod, ts::TimeStructure) = sp
function _identify_sp(t::Union{TimePeriod, AbstractOperationalScenario, AbstractRepresentativePeriod}, ts::TimeStructure)
sps = collect(strat_periods(ts))
filter_fun(sp) = _check_period(sp, t)
per = findfirst(filter_fun, sps)
isnothing(per) && throw(ErrorException("Period not part of any strategic period"))
return sps[per]
endThis implementation can be extended for both representative periods
_check_period(rp::AbstractRepresentativePeriod, t::TimePeriod) = t in rp
_check_period(rp::AbstractRepresentativePeriod, t::AbstractOperationalScenario) = t in opscenarios(rp)
_identify_rp(rp::AbstractRepresentativePeriod, ts::TimeStructure) = rp
function _identify_rp(t::Union{TimePeriod, AbstractOperationalScenario}, ts::TimeStructure)
rps = collect(repr_periods(ts))
filter_fun(sp) = _check_period(sp, t)
per = findfirst(filter_fun, rps)
isnothing(per) && throw(ErrorException("Period not part of any representative period"))
return rps[per]
endand operational scenarios
_check_period(osc::AbstractOperationalScenario, t::TimePeriod) = t in osc
_identify_osc(osc::AbstractOperationalScenario, ts::TimeStructure) = osc
function _identify_osc(t::TimePeriod, ts::TimeStructure)
oscs = collect(opscenarios(ts))
filter_fun(sp) = _check_period(sp, t)
per = findfirst(filter_fun, oscs)
isnothing(per) && throw(ErrorException("Period not part of any operational scenario"))
return oscs[per]
endDiscussion
The new approach is in practice slower than the old approach as we do not only compare the field sp, but the complete OperationalPeriod. Due to the application of findfirst, its penalty is dependent on which period we are interested in (in which strategic period it is located) and how the time structure looks like. The penalty can be high if we have a lot of strategic periods and operational periods.
As an example, consider the following time structure:
# 15 strategic periods with 4 weeks each
ts = TwoLevel(15, 5, SimpleTimes(168*4, 1); op_per_strat=8760)The required time through specifying the functions
function test_old(t, ts)
for k ∈ range(1,1e5)
TimeStruct._sp_period(t, ts)
end
end
function test_new(t, ts)
for k ∈ range(1,1e5)
_identify_sp(t, ts)
end
endis dependent on the chosen time period (which strategic period it is in). If it is in the first strategic period, it requires on my PC 0.11 s compared to 0.08 s for the old method. If it is in the 15th, that changes to 0.52 s compared to 0.07 s.
This leaves the question whether we want to include it or not. Do we see any problems by just comparing a subset of parameters (_strat_per and comparable for the new methods) of a period? What are your thoughts @hellemo and @trulsf?