diff --git a/Manifest.toml b/Manifest.toml index 6c62bc0..718c6fb 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.11.5" manifest_format = "2.0" -project_hash = "5fd84347cd356de5b819aa3ea793fa63c45180e4" +project_hash = "b5f5f0b50b1e0b7dd05bb93e0b1bb03fea235d53" [[deps.ADTypes]] git-tree-sha1 = "e2478490447631aedba0823d4d7a80b2cc8cdb32" @@ -635,9 +635,9 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.5" [[deps.EnzymeCore]] -git-tree-sha1 = "7d7822a643c33bbff4eab9c87ca8459d7c688db0" +git-tree-sha1 = "8272a687bca7b5c601c0c24fc0c71bff10aafdfd" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.8.11" +version = "0.8.12" weakdeps = ["Adapt"] [deps.EnzymeCore.extensions] @@ -1419,9 +1419,9 @@ version = "1.11.0" [[deps.MathTeXEngine]] deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] -git-tree-sha1 = "31a99cb7537f812e1d6be893a71804c35979f1be" +git-tree-sha1 = "6e64d2321257cc52f47e193407d0659ea1b2b431" uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" -version = "0.6.4" +version = "0.6.5" [[deps.MatrixFactorizations]] deps = ["ArrayLayouts", "LinearAlgebra", "Printf", "Random"] @@ -2784,9 +2784,9 @@ version = "2.0.3+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "002748401f7b520273e2b506f61cab95d4701ccf" +git-tree-sha1 = "cd155272a3738da6db765745b89e466fa64d0830" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.48+0" +version = "1.6.49+0" [[deps.libsixel_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "libpng_jll"] diff --git a/scripts/main.jl b/scripts/main.jl index 9543a01..45738d4 100644 --- a/scripts/main.jl +++ b/scripts/main.jl @@ -1,58 +1,34 @@ -include("../src/utils/constants.jl") -#include("../src/fhn_solver.jl") -include("../src/gray_scott_solver.jl") include("../src/visualization.jl") using Observables using GLMakie -using .Constants +using .Visualization.Constants using .Visualization -# GSParams +# GSParams AND FHNParams N = 128 dx = 1.0 -Du, Dv = Observable(0.16), Observable(0.08) -F, k = Observable(0.060), Observable(0.062) - -param_observables = ( - Du=Du, - Dv=Dv, - F=F, - k=k, +params = ( + N=Observable(128), + dx=Observable(1.0), + Du=Observable(0.16), + Dv=Observable(0.08), + F=Observable(0.060), + k=Observable(0.062), + ϵ=Observable(0.05), + a=Observable(0.7), + b=Observable(0.8) ) -""" -# FHNParams -N = 128 -dx = 1.0 -Du, Dv = Observable(0.016), Observable(0.1) -ϵ, a, b = Observable(0.1), Observable(0.5), Observable(0.9) -param_observables = ( - Du=Du, - Dv=Dv, - ϵ=ϵ, - a=a, - b=b -) -""" -params_obs = Observable(Constants.GSParams(N, dx, Du[], Dv[], F[], k[])) -#params_obs = Observable(FHNParams(N=N, dx=dx, Du=Du[], Dv=Dv[], ϵ=ϵ[], a=a[], b=b[])) - - -lift(Du, Dv, F, k) do u, v, f, ki - params_obs[] = GSParams(N, dx, u, v, f, ki) +params_obs = Observable{Constants.CombinedPDEParams}(CombinedPDEParams(N, dx, params.Du[], params.Dv[], params.F[], params.k[], params.ϵ[], params.a[], params.b[])) +lift(params.N, params.dx, params.Du, params.Dv, params.F, params.k, params.ϵ, params.a, params.b) do N, dx, Du, Dv, F, k, ϵ, a, b + params_obs[] = CombinedPDEParams(N, dx, Du, Dv, F, k, ϵ, a, b) end -""" -lift(Du, Dv, ϵ, a, b) do d_u, d_v, eps, aa, bb - params_obs[] = FHNParams(N=N, dx=dx, Du=d_u, Dv=d_v, ϵ=eps, a=aa, b=bb) -end -""" - U = ones(N, N) V = zeros(N, N) heat_obs = Observable(U) -fig = build_ui(U, V, param_observables, params_obs, heat_obs) +fig = build_ui(U, V, params, params_obs, heat_obs) display(fig) diff --git a/src/fhn_solver.jl b/src/fhn_solver.jl index 75ca9b3..31dbc87 100644 --- a/src/fhn_solver.jl +++ b/src/fhn_solver.jl @@ -1,3 +1,5 @@ +module FHNSolver + include("utils/laplacian.jl") using DifferentialEquations @@ -45,7 +47,8 @@ function fhn!(du, u, p::FHNParams, t) du .= vcat(vec(fu_full), vec(fv_full)) end -function step!(U, V, params_obs::Observable; dx=1) +function step_fhn!(U, V, params_obs::Observable; dx=1) + """ p = params_obs[] # Flatten initial condition (activation u, recovery v) @@ -65,11 +68,24 @@ function step!(U, V, params_obs::Observable; dx=1) U .= u_new V .= v_new + return U + """ + + params = params_obs[] + + Δu = laplacian(U, params.dx) + Δv = laplacian(V, params.dx) + + u = U[2:end-1, 2:end-1] + v = V[2:end-1, 2:end-1] + + fu = params.Du .* Δu .+ u .- (u .^ 3) ./ 3 .- v + fv = params.Dv .* Δv .+ params.ϵ .* (u .+ params.a .- params.b .* v) + + U[2:end-1, 2:end-1] .+= 0.1 .* fu + V[2:end-1, 2:end-1] .+= 0.1 .* fv + return U end -function multi_step!(state, n_steps, heat_obs::Observable, params_obs::Observable; dx=1) - for _ in 1:n_steps - heat_obs[] = step!(state[1], state[2], params_obs; dx=1) - end -end +end # Module end \ No newline at end of file diff --git a/src/gray_scott_solver.jl b/src/gray_scott_solver.jl index e9e2c5f..418015a 100644 --- a/src/gray_scott_solver.jl +++ b/src/gray_scott_solver.jl @@ -1,3 +1,5 @@ +module GrayScottSolver + include("utils/constants.jl") include("utils/laplacian.jl") @@ -6,7 +8,7 @@ using Observables using .Constants using .Laplacian -function step!(U, V, params_obs::Observable; dx=1) +function step_gray_scott!(U, V, params_obs::Observable; dx=1) # Extract parameters p = params_obs[] Du, Dv = p.Du, p.Dv @@ -42,8 +44,5 @@ function step!(U, V, params_obs::Observable; dx=1) return U # for visualization end -function multi_step!(state, n_steps, heat_obs::Observable, params_obs::Observable; dx=1) - for _ in 1:n_steps - heat_obs[] = step!(state[1], state[2], params_obs; dx=1) - end -end + +end # Module end \ No newline at end of file diff --git a/src/utils/constants.jl b/src/utils/constants.jl index a07f4f0..d3e37a0 100644 --- a/src/utils/constants.jl +++ b/src/utils/constants.jl @@ -10,11 +10,6 @@ struct FHNParams <: PDEParams ϵ::Float64 a::Float64 b::Float64 - - # Inner constructor that takes keyword arguments - # The semicolon ';' separates positional arguments from keyword arguments - FHNParams(; N::Int, dx::Float64, Du::Float64, Dv::Float64, ϵ::Float64, a::Float64, b::Float64) = - new(N, dx, Du, Dv, ϵ, a, b) end struct GSParams <: PDEParams @@ -27,6 +22,18 @@ struct GSParams <: PDEParams end -export PDEParams, FHNParams, GSParams +struct CombinedPDEParams <: PDEParams + N::Int + dx::Float64 + Du::Float64 + Dv::Float64 + F::Float64 + k::Float64 + ϵ::Float64 + a::Float64 + b::Float64 +end + +export PDEParams, FHNParams, GSParams, CombinedPDEParams end # module Constants diff --git a/src/visualization.jl b/src/visualization.jl index c5a6fb0..257955c 100644 --- a/src/visualization.jl +++ b/src/visualization.jl @@ -1,8 +1,18 @@ module Visualization -include("gray_scott_solver.jl") -#include("fhn_solver.jl") -using GLMakie, Observables, Makie +include("../src/utils/constants.jl") +include("gray_scott_solver.jl") +include("fhn_solver.jl") + +using Observables, Makie, GLMakie +using .Constants +using .GrayScottSolver: step_gray_scott! +using .FHNSolver: step_fhn! + +const STEP_FUNCTIONS = Dict( + :gray_scott => GrayScottSolver.step_gray_scott!, + :fhn => FHNSolver.step_fhn! +) function coord_to_index(x, y, N) ix = clamp(round(Int, x), 1, N) @@ -34,9 +44,11 @@ function reset!(U, V, heat_obs) heat_obs[] = copy(U) end -function param_box!(grid, row, labeltxt, observable::Observable) - Label(grid[row, 1], labeltxt) - box = Textbox(grid[row, 2], validator=Float64, width=50, placeholder=labeltxt, stored_string="$(observable[])") +function param_box!(grid, row, labeltxt, observable::Observable; col=1) + label_cell = 2 * col - 1 + textbox_cell = 2 * col + Label(grid[row, label_cell], labeltxt) + box = Textbox(grid[row, textbox_cell], validator=Float64, width=50, placeholder=labeltxt, stored_string="$(observable[])") on(box.stored_string) do s try observable[] = parse(Float64, s) @@ -45,13 +57,27 @@ function param_box!(grid, row, labeltxt, observable::Observable) @warn "Invalid input for $labeltxt: $s" end end + on(observable) do val + box.displayed_string[] = string(val) + end +end + +function multi_step!(state, n_steps, heat_obs::Observable, params_obs::Observable; step_method=step_gray_scott!, dx=1) + for _ in 1:n_steps + heat_obs[] = step_method(state[1], state[2], params_obs; dx=1) + end end function build_ui(U, V, param_obs_map::NamedTuple, params_obs, heat_obs) + reset!(U, V, heat_obs) fig = Figure(size=(800, 800)) + gh = GridLayout(fig[1, 1]) - ax = Axis(gh[1, 1]) + dropdown = Menu(fig, options=collect(zip(["Gray-Scott", "FHN"], [:gray_scott, :fhn]))) + gh[1, 1] = dropdown + ax = Axis(gh[2, 1]) + hm = Makie.heatmap!(ax, heat_obs, colormap=:viridis) deactivate_interaction!(ax, :rectanglezoom) ax.aspect = DataAspect() @@ -59,33 +85,39 @@ function build_ui(U, V, param_obs_map::NamedTuple, params_obs, heat_obs) run_label = Observable{String}("Run") stepsize = Observable(30) spoint = select_point(ax.scene) + step_method = Observable{Function}(step_gray_scott!) # Controls fig[2, 1] = buttongrid = GridLayout(ax.scene, tellwidth=false) btn_step = Button(buttongrid[1, 1], width=50, label="Step") btn_start = Button(buttongrid[1, 2], width=50, label=run_label) btn_reset = Button(buttongrid[1, 3], width=50, label="Reset") - slidergrid = SliderGrid(fig[3, 1], (label="Speed", range=1:1:100, format="{}x", width=350, startvalue=stepsize[])) + slidergrid = SliderGrid(fig[3, 1], (label="Speed", range=1:1:100, format="{}x", width=350, startvalue=stepsize[]), tellwidth=false) speed_slider = slidergrid.sliders[1].value + # place all the parameter boxes + gh[2, 2] = textboxgrid = GridLayout(ax.scene, tellwidth=false) - gh[1, 2] = textboxgrid = GridLayout(ax.scene, tellwidth=false) - rowsize!(gh, 1, Relative(1.0)) + param_box!(textboxgrid, 1, "Du", param_obs_map.Du, col=1) + param_box!(textboxgrid, 2, "Dv", param_obs_map.Dv, col=1) + param_box!(textboxgrid, 3, "Feed", param_obs_map.F, col=1) + param_box!(textboxgrid, 4, "Kill", param_obs_map.k, col=1) + # FHN column (col 2) + param_box!(textboxgrid, 1, "Du", param_obs_map.Du, col=2) + param_box!(textboxgrid, 2, "Dv", param_obs_map.Dv, col=2) + param_box!(textboxgrid, 3, "ϵ", param_obs_map.ϵ, col=2) + param_box!(textboxgrid, 4, "a", param_obs_map.a, col=2) + param_box!(textboxgrid, 5, "b", param_obs_map.b, col=2) - param_box!(textboxgrid, 1, "Du", param_obs_map.Du) - param_box!(textboxgrid, 2, "Dv", param_obs_map.Dv) - - if haskey(param_obs_map, :F) - param_box!(textboxgrid, 3, "Feed", param_obs_map.F) - param_box!(textboxgrid, 4, "kill", param_obs_map.k) - elseif haskey(param_obs_map, :ϵ) - param_box!(textboxgrid, 3, "ϵ", param_obs_map.ϵ) - param_box!(textboxgrid, 4, "a", param_obs_map.a) - param_box!(textboxgrid, 5, "b", param_obs_map.b) + rowsize!(gh, 1, Fixed(50)) # small row for the menu + rowsize!(gh, 2, Relative(1.0)) + for c in 1:4 + colsize!(textboxgrid, c, Relative(1.0 / 4.0)) end + # Events ############################################################## # Timer and state for animation running = Observable(false) @@ -102,7 +134,7 @@ function build_ui(U, V, param_obs_map::NamedTuple, params_obs, heat_obs) end # Button Listeners on(btn_step.clicks) do _ - multi_step!((U, V), stepsize[], heat_obs, params_obs) + multi_step!((U, V), stepsize[], heat_obs, params_obs; step_method=step_method[]) end on(btn_start.clicks) do _ @@ -111,7 +143,7 @@ function build_ui(U, V, param_obs_map::NamedTuple, params_obs, heat_obs) on(btn_start.clicks) do _ @async while running[] - multi_step!((U, V), stepsize[], heat_obs, params_obs) + multi_step!((U, V), stepsize[] * 5, heat_obs, params_obs; step_method=step_method[]) sleep(0.0015) # ~20 FPS end end @@ -127,7 +159,6 @@ function build_ui(U, V, param_obs_map::NamedTuple, params_obs, heat_obs) return end x, y = pt - println("params_obs[].N, ", params_obs[].N) i, j = coord_to_index(x, y, params_obs[].N) # get corners of square that will get filled with concentration @@ -142,6 +173,24 @@ function build_ui(U, V, param_obs_map::NamedTuple, params_obs, heat_obs) heat_obs[] = copy(U) end + + on(dropdown.selection) do sel + if sel isa Tuple + label, selected_model = sel + else + selected_model = sel + label = string(sel) + end + + @info "Selected model: $label → $selected_model" + + if haskey(STEP_FUNCTIONS, selected_model) + step_method[] = STEP_FUNCTIONS[selected_model] + else + @warn "Unknown model selected: $selected_model" + end + end + return fig end