### A Pluto.jl notebook ###
# v0.19.35
using Markdown
using InteractiveUtils
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
quote
local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
el
end
end
# ╔═╡ ca18be6d-8a9b-4c53-bd2e-553ea01ebb13
begin
import Pkg
Pkg.activate("/home/hisacro/.julia/environments/v1.10/Project.toml")
using PlutoUI, GLMakie, ImplicitIntegration
import GeoInterface as GI, GeometryOps as GO
import Contour.lines as clines
import Contour.levels as clevels
import Contour.contours as ccontours
import ProgressLogging
import Plots.plot! as pplot
import Plots.plot as fplot
import Plots.vline! as pvline
import Plots.mm as pmm
import Plots.twiny as ptwiny
import Plots.scatter! as pscatter;
# import Plots.scatter as sscatter;
end
# ╔═╡ 2e0376f2-4c2e-45a1-ba75-8151a265f90b
TableOfContents()
# ╔═╡ fbf43036-fc71-4c26-a712-96143bcd1a71
md"""
# Abstract
Encapsulating the features of libration motion with the parameter of the system. It includes the birth of Type I, II libration as well as the mixed and chaotic motions.
# Goal
Linking the phase space closed contours with the system motion.
# Methodology
1. Fix (x, y) range and generate $range^2$ dimension matrix mapping (x,y) -> coloumb yukawa function.
2. For each energy levels, compute the contours using clevels from Contour.jl.
3. Convert the points to vectors, check for a closed ring structure comparing start vs end vector index.
4. Using GeometryInterface.jl, segregate ring structures into individual polygons.
5. Each of the polygon is a closed contour, associated with Type I, II libration and utilizing GeometryOps.jl's area function, area of all contours can be summed.
6. Iterate steps 2-5 for energy levels starting from 0.0 to 0.9
7. Construct the Energy vs area of contour plot, no. of closed contour as well as seperate plots for Type I, Type II contour contributions.
# Consequence
Within the energy regime $V(L)<E<V(r_{end})$, the no. of closed contours is reported as 4. Although the count is correct, but in the phase space the system is confined only in 2 of the contours. The reason being, the confinement opens up right after the energy levels V(S1) or V(L), eg. letting $\theta_1$ cross from $-\pi$ to $\pi$.
"""
# ╔═╡ 6212c5cb-236c-4dc4-bf45-ea271078d71b
md"""
# Loading Packages
"""
# ╔═╡ 7c4414f9-a97a-4dd6-95ab-e2997e7f32a6
md"""
# Potential Contours
"""
# ╔═╡ 503aa0a0-ece8-4acc-b437-3119055d22bc
md"""
Potential energy = $(@bind height PlutoUI.Slider(0.01:0.001:1; default=0, show_value=true))
"""
# ╔═╡ fb22baa2-0d17-4f21-88ab-d17f848e77b4
#save(string("/tmp/contour/",height, ".png"),f)
# ╔═╡ ca1086bc-7146-495b-ad75-c9583ba4c2bc
clevels
# ╔═╡ 91452dba-a09c-4172-97b0-553e25f9182c
md"""
#### Potential order, n = ($(@bind n Scrubbable(1:1:5,default=3)))
#### Exponential factor, K = $(@bind K PlutoUI.NumberField(1.5:0.1:20.0,default=2.0))
#### Length between 2 rotors L = $(@bind L PlutoUI.NumberField(0.5:0.1:10., default=1.5; ))
"""
# ╔═╡ 26793608-7208-4783-9a7a-ac7305e68e47
begin
V(r)=(1-exp(-K*r^n))/r
# height=V(2-L) #c
# height = V(0.9837048597697602) #end
end
# ╔═╡ 58d43864-9a74-42da-96d5-afb59f22cd0f
begin
len = 2000
x = range(-2*pi, 2*pi, length=len)
y = range(-2*pi, 2*pi, length=len)
function coulomb_yukawa(x, y)
term1 = sqrt((2+L^2-2*L*(cos(x)-cos(y))-2*cos(x-y)));
return (1-exp(-K*(term1)^n))/(term1)
end
z = [coulomb_yukawa(x[i], y[j]) for i in 1:len, j in 1:len]
end
# ╔═╡ ad62047b-b6e0-4bcb-9cec-813dafcbd731
md"""
# Bifurcation plot with area as a parameter
"""
# ╔═╡ cc214bc8-48a0-4e3b-bc59-83ec71ee78a1
md"""
Function to calculate the number of closed contours at each energy levels, then add up the area of those contours. These are plotted against energy.
* V(L1), V(L3) are associated with Type I libration.
* V(L2), V(L4) are associated with Type II libration.
"""
# ╔═╡ 638efa65-f78b-4f60-a103-99f37a9a3ae2
begin
closed1_list = Vector{Float64}(undef,0)
area1_list = Vector{Float64}(undef,0)
type1_list = Vector{Float64}(undef,0)
type2_list = Vector{Float64}(undef,0)
pot1_list = Vector{Float64}(undef,0)
# len = 500
# x = range(-2*pi, 2*pi, length=len)
# y = range(-2*pi, 2*pi, length=len)
# function coulomb_yukawa(x, y)
# term1 = sqrt((2+L^2-2*L*(cos(x)-cos(y))-2*cos(x-y)));
# return (1-exp(-K*(term1)^n))/(term1)
# end
# z = [coulomb_yukawa(x[i], y[j]) for i in 1:len, j in 1:len]
@ProgressLogging.progress for height_e in 0.001:0.001:0.86501
cls = ccontours(x, y, z, [height_e]) # compute the contours at manually specified levels
level = only(clevels(cls)) # extract the only result (since we only asked for one level)
points = [l.vertices for l in clines(level)] # ::Vector{Vector{Tuple{Float64, Float64}}}
closed_ring_points = filter(points) do pointvec
pointvec[begin] == pointvec[end] # you can also make this approximate, if you want
end
# construct geometry from vectors of points
rings = GI.LinearRing.(closed_ring_points)
polygons = [GI.Polygon([ring]) for ring in rings]
areas = GO.area.(polygons)
# Capturing birth of Point B other since at the this point area of Type II will be minimum, so navie algorithm doesn't work
if GO.within((pi,0),polygons[argmin(areas)]) || GO.within((-pi,0),polygons[argmin(areas)])
push!(type1_list, maximum(areas))
push!(type2_list, minimum(areas))
else
push!(type1_list, minimum(areas))
# before Point B, append zero to Type II's area
if round(minimum(GO.area.(polygons)),digits=2) == round(maximum(GO.area.(polygons)),digits=2)
push!(type2_list, 0.0)
else
push!(type2_list, maximum(areas))
end
end
# push!(type1_list, minimum(areas))
# push!(type2_list, maximum(areas))
push!(closed1_list, length(polygons))
push!(area1_list, sum(areas; init=0) )
push!(pot1_list , height_e )
end
end
# ╔═╡ 85a0af7b-ef30-4d5c-82c0-520d1254f252
length(closed1_list)
# ╔═╡ ffe3ffe9-6cef-47c7-8716-e64acafa060c
#=╠═╡
type1_list.+type2_list
╠═╡ =#
# ╔═╡ 2b581616-c2a2-4947-90f4-35073c679e8c
md"""
## Type I libration
"""
# ╔═╡ 4764edde-f53b-4f90-80ba-f7e4564cf00d
# ╠═╡ disabled = true
#=╠═╡
vend=0.9837048597697398
╠═╡ =#
# ╔═╡ 543da592-1ee0-4efa-8d65-3dd7a9bf2a4e
md"""
## Type II libration
"""
# ╔═╡ 9c44f6b3-d9ab-4142-8feb-4734260358eb
# ╠═╡ disabled = true
#=╠═╡
Plots.scatter(pot1_list,area1_list)
╠═╡ =#
# ╔═╡ 2dab2fcd-77bf-4651-8223-6854863f425a
md"""
# Appendix
"""
# ╔═╡ fe9a3e65-212c-40a9-8b81-7dbdfa885a54
md"""
Testing functions from Contours.jl, GeoInterface.jl and GeometryOps.jl to find the area of the enclosed contours. The usage is detailed in Methodology.
"""
# ╔═╡ 25f758a8-e7bd-4ce4-9eca-84edf36265a3
md"""
## Computing workspace
"""
# ╔═╡ 723a6f35-fb5e-49a3-9ccb-15c45af40bf4
cls = ccontours(x, y, z, [height]) # compute the contours at manually specified levels
# ╔═╡ 51df77c4-261f-40d3-859c-aeb55f5a3a47
begin
level = only(clevels(cls)) # extract the only result (since we only asked for one level)
points = [l.vertices for l in clines(level)] # ::Vector{Vector{Tuple{Float64, Float64}}}
end
# ╔═╡ efd4df8d-03d0-42d9-886a-cf5f34159135
closed_ring_points = filter(points) do pointvec
pointvec[begin] == pointvec[end] # you can also make this approximate, if you want
end
# ╔═╡ fef543b4-0899-47ad-8ae5-699022744879
begin
# construct geometry from vectors of points
rings = GI.LinearRing.(closed_ring_points)
polygons = [GI.Polygon([ring]) for ring in rings]
end
# ╔═╡ f8c7c4a9-9d18-46cc-95a4-c65b0ab1b2b5
md"""
Number of Closed Contours = $(length(polygons))
"""
# ╔═╡ e39373bd-8e12-469f-bd66-2d005ff6cff2
begin
# Setup of the figure
approx_h = round(height, digits=4)
f = Figure(
fontsize = 38, fonts=(; regular = "Liberation Serif ", bold ="Liberation Serif bold");size=(1400,800),figure_padding = 10)
# ax1 = Axis3(f[1, 1], xreversed=true, )
# ax2 = Axis(f[1, 2])
ax1 = Axis3(f[1, 1][1,1], xticks=MultiplesTicks(2,pi,"π"),
yticks=MultiplesTicks(2,pi,"π"),
xlabel=" θ₁ ",ylabel=" θ₂ ", zlabel=" V ",
# xlabel=" ",ylabel=" ", zlabel=" ",
zticks=([0.0,0.3,0.6,0.9]),
viewmode=:stretch, zlabelrotation=deg2rad(90))
ax2 = Axis(f[1, 1][1,2], xticks=MultiplesTicks(5,pi,"π"),
yticks=MultiplesTicks(5,pi,"π"), xlabel="θ₁",ylabel="θ₂"
,title="Energy = $(approx_h)", titlegap=50)
# colgap!(ax1, )
Label(f[1, 1][2,2], rich("Number of Closed Contours = $(length(polygons))"
))
# Draw the contour in the 3D graph
plane = [height for i in 1:length(x), j in 1:length(y)];
# Drawing the function and the contour on the 3G graph
surface!(ax1, x, y, z, colormap=:viridis)
surface!(ax1, x, y, plane; transparency=true, colormap=[:white,:white])
# # Drawing the function and the countour on the heatmap
# levels = [V(2-L), V(L), V(L+2)]
hm = contourf!(ax2, x, y, z)
Colorbar(f[1, 1][2,1], hm, ; vertical=false, ticklabelrotation=deg2rad(0), size=30)
contour!(ax2, x, y, z; linewidth=6, label=true, levels=[height], color=:white)
# visualize the quadrature nodes
# scatter!(ax2, quad.coords; markersize = .1,color=:red)
colsize!(f.layout, 1, Relative(2.8/3))
end
# ╔═╡ 9c17c0d8-e4cb-41c0-b180-b18a3c87e265
# Displaying the function
f
# ╔═╡ 96d3ccec-f3a1-4721-bde7-cb14148616f4
collect(GI.getpoint(polygons[2]))
# ╔═╡ 0fc12429-d02f-4e52-8687-983e892b391a
GO.within((pi,0),polygons[4])
# ╔═╡ 2b4de722-14ef-4311-8fc0-bc3568458d46
round(minimum(GO.area.(polygons)),digits=2)==round(maximum(GO.area.(polygons)),digits=2) || println(1)
# ╔═╡ eb1059eb-20a7-41dd-9b7b-aac5867f4990
areas = (GO.area.(polygons))
# ╔═╡ d7345e48-da84-4e99-8543-12c8409394f7
begin
argmax(areas)
argmin(areas)
end
# ╔═╡ 7d41ab5b-c4a4-4a09-9c58-e74e1359b22a
length(polygons)
# ╔═╡ 9d90eb32-c6d7-47e4-9d91-8d02b99c191c
# ╠═╡ disabled = true
#=╠═╡
begin
begin
t=collect(GI.getpoint(polygons[2]))
scatter(t,color=:red,markersize=10)
end
end
╠═╡ =#
# ╔═╡ 58e827c7-bb64-474b-b646-a7897685f765
# if you want to be efficient / nonallocating
total_area = sum(GO.area, polygons) # 0.6775172177537117
# ╔═╡ a79e71d5-c4a2-4393-a53f-4bc2e9a1f47d
mp = GI.MultiPolygon(polygons)
# ╔═╡ 2736cd26-b277-4ca4-87b5-37db68b09aef
fixed_mp = GO.fix(mp; corrections = [GO.UnionIntersectingPolygons()])
# ╔═╡ 19a76ec9-bed6-4fb7-afcd-4c0474b5f924
# ╠═╡ disabled = true
#=╠═╡
begin
# compute a quadrature for the implicit region ϕ<0
ϕ = (x) -> coulomb_yukawa(x[1], x[2]) - height # level-set
quad, logger = quadgen(ϕ, (-2*pi, -2*pi), (2*pi, 2*pi); order = 1)
end
╠═╡ =#
# ╔═╡ ad152b27-121d-4b5c-a350-ca3a7c080bdf
#=╠═╡
println("Estimated area: ", integrate(x -> 1, quad))
╠═╡ =#
# ╔═╡ 521d1030-4780-4def-a828-5827932eedca
md"""
## Finding r_end
The fucntion is documented in the appendix of bifn_energy.jl
"""
# ╔═╡ 942e30fd-7ec8-4728-91bc-35af356268cd
function rmax()
# for newer version of root finding.
# dvdr(r)=(exp(K*r^n)-K*n*(r^n)-1)
# dvdr(r)=(exp(-K*r^n)*(exp(K*r^n)-K*n*(r^n)-1))/r^2
# rts_vmax=roots(dvdr, 0.5..1.5)
# return [ mid.(root_region(rt)) for rt in rts_vmax ][1]
# Implicit differentiation of the potential, V(r)
dvdr(r)=(exp(-K*r^n)*(exp(K*r^n)-K*n*(r^n)-1))/r^2
# Uses IntervalRootingFinding.jl to search for roots
rts_vmax=roots(dvdr, 0.5..1.5, Newton, 1e-8);
# Returns the maxima of r
return mid.(interval.(rts_vmax))[1];
end
# ╔═╡ c026d2a0-e6d2-4618-a42b-9b801d074eec
vend = 0.8650975900298344
# ╔═╡ ea245c7d-c3c3-4083-979b-95b10c09c26c
V(vend)
# ╔═╡ 52274348-0306-4c71-9135-0f6466ee40bb
r_end = 0.9837048597697398
# ╔═╡ b361abb7-1205-45ff-9d87-8777c1033afb
begin
# adding zeros at the end
f1 = Figure(fontsize = 28, fonts=(; regular = "Liberation Serif ", bold ="Liberation Serif bold"),size=(1100,800), )
# tick configurations.
# bottom axis with 2 significant digits.
xmain_ticks = ([0, V(L+2), V(2-L), V(L), V(r_end)], [ "0", "$(round(V(L+2),sigdigits=2))", "$(round(V(2-L),sigdigits=2))", "$(round(V(L),sigdigits=2))", "$(round(V(r_end),sigdigits=2))"])
# top axis for libration points.
xtop_ticks = ([0, V(L+2), V(2-L), V(L), V(r_end)],[L"V(L1)", L"V(L2)", L"V(L3)", L"V(L4)", L"V(r_{end})"])
xtop_ticks_pre = ([0, V(L+2), V(2-L), V(L), V(r_end)],[L"V(S4)", L"V(S2)", L"V(S3)", L"V(S1)", L"V(r_{end})"])
# Type I top axis
xtop_ticks_I = ([0, V(2-L) , V(r_end)],[L"V(0)", L"V(2-L)", L"V(r_{end})"])
# Type II top axis
xtop_ticks_II = ([ V(L+2), V(L), V(r_end)], [L"V(L+2)", L"V(L)", L"V(r_{end})"])
# 1st subplot
ax_cc = Axis(f1[1, 1],title="Area of All Closed Contours", titlegap = 50 ,ylabel=L"units^2",
xticks = xmain_ticks,
)
# dirty hack to align the V(Li) labels on top
ax_cc1 = Axis(f1[1, 1], xticklabelcolor = :black, yticklabelcolor= :black, xaxisposition = :top, yaxisposition = :right,
xticks = xtop_ticks_pre,
yticks = ([],[]),
xticklabelsize = 22
)
GLMakie.lines!(ax_cc , pot1_list,area1_list, color=:black, linewidth = 5)
# GLMakie.scatter!(Axis(f1[1, 1],title="Total Area",ylabel="units²"), pot1_list,type1_list.+type2_list, color=:black)
# 2nd subplot
ax_ct = Axis(f1[1, 2],ylabel="Number", title="No. of Closed Contours", yticks=[0,2,4,6,8,10],
xticks = xmain_ticks,
titlegap = 50
)
GLMakie.scatter!(ax_ct, pot1_list,closed1_list, color=:black)
GLMakie.scatter!(ax_ct,0.88:0.001:1.0,fill(0.0, length(0.88:0.001:1.0)),color=:black)
# dirty hack to align the V(Li) labels on top
ax_ct2 = Axis(f1[1, 2], xticklabelcolor = :black, yticklabelcolor= :black, xaxisposition = :top, yaxisposition = :right,
xticks = xtop_ticks_pre,
yticks = ([],[]), xticklabelsize = 22
)
# 3rd subplot
ax_type1 = Axis(f1[2, 1], title="Area of Type I Contours",xlabel="Energy",ylabel=L"units^2",
xticks = xmain_ticks, titlegap = 50,
titlecolor=:blue3
)
GLMakie.lines!(ax_type1 ,pot1_list,type1_list, color=:blue3, linewidth = 5)
# aligning the top
ax_type1_2 = Axis(f1[2, 1], xticklabelcolor = :blue3, yticklabelcolor= :blue3, xaxisposition = :top, yaxisposition = :right,
xticks = xtop_ticks_I,
yticks = ([],[]), xticklabelsize = 22
)
# 4th subplot
ax_type2 = Axis(f1[2, 2], title="Area of Type II Contours",xlabel="Energy",ylabel=L"units^2",
xticks = xmain_ticks, titlegap = 50,
titlecolor=:red3
)
GLMakie.lines!(ax_type2, pot1_list,type2_list, color=:red3,linewidth = 5 )
# aligning the top
ax_type2_2 = Axis(f1[2, 2], xticklabelcolor = :red3, yticklabelcolor= :red3, xaxisposition = :top, yaxisposition = :right,
xticks = xtop_ticks_II,
yticks = ([],[]), xticklabelsize = 22
)
# setting up xlims on the subplots
axis_names = [ax_cc, ax_cc1, ax_ct, ax_ct2, ax_type1, ax_type1_2, ax_type2, ax_type2_2]
for axis_i in axis_names
GLMakie.xlims!(axis_i, -0.1, 1.0)
end
f1
end
# ╔═╡ e62ea829-3bb2-4525-8476-9331a231d29a
begin
fplot(pot1_list,type1_list, lw=3, color=:blue3)
pplot(markersize=2, xlabel="Energy", ylabel=L"unit^2", tickfontsize=14, guidefontsize=12, fontfamily="Computer Modern", legend=false, xlims=[-0.01,1])
pplot(
# xticks=([V(L+2), V(2-L), V(L), V(r_end)],[L"V(L+2)", L"V(2-L)", L"V(L)", L"V(r_{end})"]),
xticks=([ V(2-L), V(r_end)],[ L"V(2-L)", L"V(r_{end})"]),
yticks=(0.0:2:6),
grid=false,
xtickfontcolor=:blue3)
pplot(ptwiny(),[],[],
xticks=([V(L+2), V(L)],[L"V(L+2)", L"V(L)" ]),
markersize=2, tickfontsize=14, guidefontsize=12,framestyle=:box, fontfamily="Computer Modern", legend=false, xlims=[-0.01,1], yticks=(0.0:2:6)
)
pvline([V(L),V(L+2),V(2-L), V(r_end)], lw=:2, ls=:dash, color=:gray)
pvline([1], lw=:2, color=:black)
end
# ╔═╡ 2647a37d-aa77-4144-99ee-54476142f267
begin
fplot(pot1_list,type2_list, lw=3, color=:red3)
pplot(markersize=2, xlabel="Energy", ylabel=L"unit^2", tickfontsize=14, guidefontsize=12,framestyle=:box, fontfamily="Computer Modern", legend=false, grid=false,
xlims=[-0.01,1], xticks=(0.0:0.4:1.2),
yticks=(0.0:10:25),
)
pplot(
# xticks=([ V(2-L), V(r_end)],[ L"V(2-L)", L"V(r_{end})"]),
xticks=([V(L+2), V(2-L), V(L), V(r_end)],[ round(V(L+2),sigdigits=2), round(V(2-L),sigdigits=2), round(V(L),sigdigits=2), round(V(r_end),sigdigits=2)]),
grid=false)
pplot(ptwiny(),[],[],
xticks=([V(L+2), V(2-L), V(L), V(r_end)],[L"V(L+2)", L"V(2-L)", L"V(L)", L"V(r_{end})"]),
# xticks=([V(L+2), V(L)],[L"V(L+2)", L"V(L)" ]),
markersize=2, tickfontsize=12, guidefontsize=12,framestyle=:box, fontfamily="Computer Modern", legend=false, xlims=[-0.01,1],
)
pvline([V(L),V(L+2),V(2-L), V(r_end)], lw=:2, ls=:dash, color=:gray)
pvline([1], lw=:2, color=:black)
end
# ╔═╡ Cell order:
# ╟─2e0376f2-4c2e-45a1-ba75-8151a265f90b
# ╠═fbf43036-fc71-4c26-a712-96143bcd1a71
# ╟─6212c5cb-236c-4dc4-bf45-ea271078d71b
# ╠═ca18be6d-8a9b-4c53-bd2e-553ea01ebb13
# ╟─7c4414f9-a97a-4dd6-95ab-e2997e7f32a6
# ╟─503aa0a0-ece8-4acc-b437-3119055d22bc
# ╟─26793608-7208-4783-9a7a-ac7305e68e47
# ╟─f8c7c4a9-9d18-46cc-95a4-c65b0ab1b2b5
# ╠═9c17c0d8-e4cb-41c0-b180-b18a3c87e265
# ╠═fb22baa2-0d17-4f21-88ab-d17f848e77b4
# ╠═e39373bd-8e12-469f-bd66-2d005ff6cff2
# ╠═ca1086bc-7146-495b-ad75-c9583ba4c2bc
# ╟─91452dba-a09c-4172-97b0-553e25f9182c
# ╠═58d43864-9a74-42da-96d5-afb59f22cd0f
# ╟─ad62047b-b6e0-4bcb-9cec-813dafcbd731
# ╟─cc214bc8-48a0-4e3b-bc59-83ec71ee78a1
# ╠═638efa65-f78b-4f60-a103-99f37a9a3ae2
# ╠═85a0af7b-ef30-4d5c-82c0-520d1254f252
# ╠═b361abb7-1205-45ff-9d87-8777c1033afb
# ╟─ffe3ffe9-6cef-47c7-8716-e64acafa060c
# ╟─2b581616-c2a2-4947-90f4-35073c679e8c
# ╠═e62ea829-3bb2-4525-8476-9331a231d29a
# ╟─4764edde-f53b-4f90-80ba-f7e4564cf00d
# ╠═ea245c7d-c3c3-4083-979b-95b10c09c26c
# ╟─543da592-1ee0-4efa-8d65-3dd7a9bf2a4e
# ╠═2647a37d-aa77-4144-99ee-54476142f267
# ╠═9c44f6b3-d9ab-4142-8feb-4734260358eb
# ╟─2dab2fcd-77bf-4651-8223-6854863f425a
# ╟─fe9a3e65-212c-40a9-8b81-7dbdfa885a54
# ╟─25f758a8-e7bd-4ce4-9eca-84edf36265a3
# ╠═723a6f35-fb5e-49a3-9ccb-15c45af40bf4
# ╠═51df77c4-261f-40d3-859c-aeb55f5a3a47
# ╠═efd4df8d-03d0-42d9-886a-cf5f34159135
# ╠═fef543b4-0899-47ad-8ae5-699022744879
# ╠═96d3ccec-f3a1-4721-bde7-cb14148616f4
# ╠═0fc12429-d02f-4e52-8687-983e892b391a
# ╠═2b4de722-14ef-4311-8fc0-bc3568458d46
# ╠═eb1059eb-20a7-41dd-9b7b-aac5867f4990
# ╠═d7345e48-da84-4e99-8543-12c8409394f7
# ╠═7d41ab5b-c4a4-4a09-9c58-e74e1359b22a
# ╠═9d90eb32-c6d7-47e4-9d91-8d02b99c191c
# ╠═58e827c7-bb64-474b-b646-a7897685f765
# ╠═a79e71d5-c4a2-4393-a53f-4bc2e9a1f47d
# ╟─2736cd26-b277-4ca4-87b5-37db68b09aef
# ╠═19a76ec9-bed6-4fb7-afcd-4c0474b5f924
# ╠═ad152b27-121d-4b5c-a350-ca3a7c080bdf
# ╟─521d1030-4780-4def-a828-5827932eedca
# ╟─942e30fd-7ec8-4728-91bc-35af356268cd
# ╠═c026d2a0-e6d2-4618-a42b-9b801d074eec
# ╠═52274348-0306-4c71-9135-0f6466ee40bb