From 0279156e164c11f2b3556d0c8d73f15fcdd6a95b Mon Sep 17 00:00:00 2001 From: Truls Flatberg Date: Sun, 3 Nov 2024 22:05:24 +0100 Subject: [PATCH 1/5] Initial tests with a Makie extension --- Project.toml | 3 +++ ext/TimeStructMakieExt.jl | 43 +++++++++++++++++++++++++++++++++++++++ src/TimeStruct.jl | 2 ++ src/utils.jl | 9 ++++++++ test/test_makie.jl | 20 ++++++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 ext/TimeStructMakieExt.jl create mode 100644 test/test_makie.jl diff --git a/Project.toml b/Project.toml index 63ef4c1..cd0cc48 100644 --- a/Project.toml +++ b/Project.toml @@ -9,15 +9,18 @@ TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" [weakdeps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [extensions] TimeStructDataFramesExt = "DataFrames" +TimeStructMakieExt = "Makie" TimeStructUnitfulExt = "Unitful" [compat] DataFrames = "1" Dates = "1" +Makie = "0.21" TimeZones = "1" Unitful = "1" julia = "^1.9" diff --git a/ext/TimeStructMakieExt.jl b/ext/TimeStructMakieExt.jl new file mode 100644 index 0000000..15287a8 --- /dev/null +++ b/ext/TimeStructMakieExt.jl @@ -0,0 +1,43 @@ +module TimeStructMakieExt +using Makie +using TimeStruct +import TimeStruct: profilechart, profilechart! + +function Makie.convert_arguments(P::PointBased, periods, profile::TimeProfile) + pts = [Point2(start_oper_time(t, periods), profile[t]) for t in periods] + l = last(periods) + push!(pts, Point2(end_oper_time(l, periods), profile[l])) + return (pts,) +end + +@recipe(ProfileChart) do scene + Attributes( + type = :stairs + ) +end + +function Makie.plot!(sc::ProfileChart{<:Tuple{<:TimeStructure, <:TimeProfile}}) + periods = sc[1] + profile = sc[2] + + for opscen in opscenarios(periods[]) + stairs!(sc, opscen, profile[]; step = :post) + end + + return sc +end + +function Makie.plot(periods::TwoLevel, profile::TimeProfile) + fig = Figure() + for (i, sp) in enumerate(strat_periods(periods)) + fig[1, i] = Axis(fig, title = "sp = $sp") + for opscen in opscenarios(sp) + stairs!(fig[1, i ], opscen, profile; step = :post) + end + end + fig +end + + + +end diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 53aa4d9..47c5075 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -58,4 +58,6 @@ export Discounter export discount export objective_weight +export profilechart + end # module diff --git a/src/utils.jl b/src/utils.jl index 8ce1bf6..629dc8d 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -123,10 +123,16 @@ function end_oper_time(t::TimePeriod, ts::OperationalScenarios) return end_oper_time(t, ts.scenarios[_opscen(t)]) end +function end_oper_time(t::TimePeriod, opscen::AbstractOperationalScenario) + return end_oper_time(t, opscen.operational) +end + + function end_oper_time(t::TimePeriod, ts::TwoLevel) return end_oper_time(t, ts.operational[_strat_per(t)]) end + function start_oper_time(t::TimePeriod, ts::TimeStructure) return end_oper_time(t, ts) - duration(t) end @@ -155,3 +161,6 @@ function Base.iterate(ts::TreeStructure, state = nothing) return TreePeriod(ts, next[1]), next[2] end + +function profilechart end +function profilechart! end diff --git a/test/test_makie.jl b/test/test_makie.jl new file mode 100644 index 0000000..df3e4bb --- /dev/null +++ b/test/test_makie.jl @@ -0,0 +1,20 @@ +using CairoMakie +using TimeStruct + + +periods = SimpleTimes([1, 2, 4, 2, 3]) +profile = OperationalProfile([2.0, 3.4, 3.5, 1.2, 0.6]) + +stairs(periods, profile; step = :post) +scatter!(periods, profile) +current_figure() + +lines(periods, profile) + + +scens = OperationalScenarios(3, periods) +scen_prof = ScenarioProfile([profile, 0.8 * profile, 1.2 * profile]) +profilechart(scens, scen_prof) + +two_level = TwoLevel(3, scens) +plot(two_level, scen_prof) From 3f69fb5141a9c98dcc5b2f8998e44ad242572b29 Mon Sep 17 00:00:00 2001 From: Truls Flatberg Date: Sun, 3 Nov 2024 22:18:36 +0100 Subject: [PATCH 2/5] Export profilechart! --- src/TimeStruct.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 47c5075..83cc598 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -58,6 +58,6 @@ export Discounter export discount export objective_weight -export profilechart +export profilechart, profilechart! end # module From 6791e6d7bdce3b55a1edb924f29c51fe18b81eda Mon Sep 17 00:00:00 2001 From: Truls Flatberg Date: Sun, 3 Nov 2024 22:20:15 +0100 Subject: [PATCH 3/5] Format fix --- ext/TimeStructMakieExt.jl | 12 ++++-------- src/utils.jl | 2 -- test/test_makie.jl | 2 -- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/ext/TimeStructMakieExt.jl b/ext/TimeStructMakieExt.jl index 15287a8..80d9860 100644 --- a/ext/TimeStructMakieExt.jl +++ b/ext/TimeStructMakieExt.jl @@ -11,12 +11,10 @@ function Makie.convert_arguments(P::PointBased, periods, profile::TimeProfile) end @recipe(ProfileChart) do scene - Attributes( - type = :stairs - ) + return Attributes(type = :stairs) end -function Makie.plot!(sc::ProfileChart{<:Tuple{<:TimeStructure, <:TimeProfile}}) +function Makie.plot!(sc::ProfileChart{<:Tuple{<:TimeStructure,<:TimeProfile}}) periods = sc[1] profile = sc[2] @@ -32,12 +30,10 @@ function Makie.plot(periods::TwoLevel, profile::TimeProfile) for (i, sp) in enumerate(strat_periods(periods)) fig[1, i] = Axis(fig, title = "sp = $sp") for opscen in opscenarios(sp) - stairs!(fig[1, i ], opscen, profile; step = :post) + stairs!(fig[1, i], opscen, profile; step = :post) end end - fig + return fig end - - end diff --git a/src/utils.jl b/src/utils.jl index 629dc8d..65bf4c3 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -127,12 +127,10 @@ function end_oper_time(t::TimePeriod, opscen::AbstractOperationalScenario) return end_oper_time(t, opscen.operational) end - function end_oper_time(t::TimePeriod, ts::TwoLevel) return end_oper_time(t, ts.operational[_strat_per(t)]) end - function start_oper_time(t::TimePeriod, ts::TimeStructure) return end_oper_time(t, ts) - duration(t) end diff --git a/test/test_makie.jl b/test/test_makie.jl index df3e4bb..ddc7871 100644 --- a/test/test_makie.jl +++ b/test/test_makie.jl @@ -1,7 +1,6 @@ using CairoMakie using TimeStruct - periods = SimpleTimes([1, 2, 4, 2, 3]) profile = OperationalProfile([2.0, 3.4, 3.5, 1.2, 0.6]) @@ -11,7 +10,6 @@ current_figure() lines(periods, profile) - scens = OperationalScenarios(3, periods) scen_prof = ScenarioProfile([profile, 0.8 * profile, 1.2 * profile]) profilechart(scens, scen_prof) From e77bdba6c6c9ee9eed5d146badbaac7465964a0f Mon Sep 17 00:00:00 2001 From: Truls Flatberg Date: Fri, 22 Nov 2024 09:37:41 +0100 Subject: [PATCH 4/5] Add rowtables to test AOG --- src/utils.jl | 83 ++++++++++++++++++++++++++++++++++++++++++++++ test/test_makie.jl | 52 ++++++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index 65bf4c3..47f5232 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -127,6 +127,10 @@ function end_oper_time(t::TimePeriod, opscen::AbstractOperationalScenario) return end_oper_time(t, opscen.operational) end +function end_oper_time(t::TimePeriod, ts::RepresentativePeriods) + return end_oper_time(t, ts.rep_periods[_rper(t)]) +end + function end_oper_time(t::TimePeriod, ts::TwoLevel) return end_oper_time(t, ts.operational[_strat_per(t)]) end @@ -162,3 +166,82 @@ end function profilechart end function profilechart! end + +_scen(sc::AbstractOperationalScenario) = "sc-$(TimeStruct._opscen(sc))" +_repr(rp::AbstractRepresentativePeriod) = "rp-$(TimeStruct._rper(rp))" +_strat(sp::AbstractStrategicPeriod) = "sp-$(TimeStruct._strat_per(sp))" + +function rowtable(profile::TimeProfile, periods::SimpleTimes; include_end = true) + rowtable = [] + for t in periods + push!(rowtable, (t = start_oper_time(t, periods), value = profile[t])) + end + if include_end + last_period = last(periods) + push!( + rowtable, + (t = end_oper_time(last_period, periods), value = profile[last_period]), + ) + end + return rowtable +end + +function rowtable(profile::TimeProfile, periods::OperationalScenarios; include_end = true) + rowtable = [] + for sc in opscenarios(periods) + for t in sc + push!( + rowtable, + (opscen = _scen(sc), t = start_oper_time(t, periods), value = profile[t]), + ) + end + if include_end + last_period = last(sc) + push!( + rowtable, + ( + opscen = _scen(sc), + t = end_oper_time(last_period, periods), + value = profile[last_period], + ), + ) + end + end + return rowtable +end + +function rowtable(profile::TimeProfile, periods::TwoLevel; include_end = true) + rowtable = [] + for sp in strat_periods(periods) + for rp in repr_periods(sp) + for sc in opscenarios(rp) + for t in sc + push!( + rowtable, + ( + strat = _strat(sp), + repr = _repr(rp), + opscen = _scen(sc), + t = start_oper_time(t, periods), + value = profile[t], + ), + ) + end + if include_end + last_period = last(sc) + push!( + rowtable, + ( + strat = _strat(sp), + repr = _repr(rp), + opscen = _scen(sc), + t = end_oper_time(last_period, periods), + value = profile[last_period], + ), + ) + end + end + end + end + return rowtable +end diff --git a/test/test_makie.jl b/test/test_makie.jl index ddc7871..6141e0d 100644 --- a/test/test_makie.jl +++ b/test/test_makie.jl @@ -1,5 +1,6 @@ using CairoMakie using TimeStruct +using AlgebraOfGraphics periods = SimpleTimes([1, 2, 4, 2, 3]) profile = OperationalProfile([2.0, 3.4, 3.5, 1.2, 0.6]) @@ -14,5 +15,54 @@ scens = OperationalScenarios(3, periods) scen_prof = ScenarioProfile([profile, 0.8 * profile, 1.2 * profile]) profilechart(scens, scen_prof) -two_level = TwoLevel(3, scens) +repr = RepresentativePeriods(2, [0.7, 0.3], [scens, scens]) + +two_level = TwoLevel(3, repr) plot(two_level, scen_prof) + +# Testing with AlgebraOfGraphics and rowtables +set_aog_theme!() +axis = (width = 225, height = 225) + +tab = TimeStruct.rowtable(profile, periods) + +plt = data(tab) * mapping(:t, :value) * visual(Stairs, step = :post) +draw(plt; axis = axis) + +sc_tab = TimeStruct.rowtable(scen_prof, scens) +plt = data(sc_tab) * mapping(:t, :value, color = :opscen) * visual(Stairs, step = :post) +draw(plt; axis = axis) + +plt = data(sc_tab) * mapping(:t, :value, col = :opscen) * visual(Stairs, step = :post) +draw(plt; axis = axis) + +two_tab = TimeStruct.rowtable(scen_prof, two_level) +plt = + data(two_tab) * + mapping(:t, :value, row = :repr, col = :strat, color = :opscen) * + visual(Stairs, step = :post) +draw(plt; axis = axis) + +plt = data(two_tab) * mapping(:t, :value, layout = :strat) * visual(Stairs, step = :post) +draw(plt; axis = axis) + +using JuMP, HiGHS + +m = Model() +@variable(m, x[periods] >= 0) +@constraint(m, [t in periods], x[t] == profile[t]) +@objective(m, Min, 0) + +set_optimizer(m, HiGHS.Optimizer) +optimize!(m) + +tab = Containers.rowtable(value, x) + +set_aog_theme!() +axis = (width = 225, height = 225) + +plt = data(tab) * mapping(:x1 => (t -> start_oper_time(t, periods)), :y) +draw(plt; axis = axis) + +plt = data(tab) * mapping(:x1, :y) +draw(plt; axis = axis) From 7603b6758d4f79974f208cc248e644b45dd02a4d Mon Sep 17 00:00:00 2001 From: Truls Flatberg Date: Fri, 22 Nov 2024 09:44:38 +0100 Subject: [PATCH 5/5] Define TimeProfile before use --- src/TimeStruct.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TimeStruct.jl b/src/TimeStruct.jl index 83cc598..bf46f60 100644 --- a/src/TimeStruct.jl +++ b/src/TimeStruct.jl @@ -22,9 +22,9 @@ include("op_scenarios/rep_periods.jl") include("op_scenarios/strat_periods.jl") include("op_scenarios/tree_periods.jl") -include("utils.jl") include("discount.jl") include("profiles.jl") +include("utils.jl") export TimeStructure export SimpleTimes