File bif_area.jl artifact 98384150ae on branch trunk


### 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