Compare commits
8 Commits
main
...
feat/diff_
| Author | SHA1 | Date |
|---|---|---|
|
|
61a6425b4c | |
|
|
1d4fdda085 | |
|
|
4ea3d4a3ea | |
|
|
5b1a6f92b0 | |
|
|
a001c03104 | |
|
|
f4c94c7ace | |
|
|
0e004bfc54 | |
|
|
c1c5c34770 |
|
|
@ -19,7 +19,6 @@ lift(Du, Dv, F, k) do u, v, f, ki
|
|||
params_obs[] = GSParams(N, dx, u, v, f, ki)
|
||||
end
|
||||
|
||||
|
||||
U = ones(N, N)
|
||||
V = zeros(N, N)
|
||||
heat_obs = Observable(U)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ using .Visualization
|
|||
N = 128
|
||||
dx = 1.0
|
||||
# diffusion rates of substance 'u' and 'v'
|
||||
Du, Dv = Observable(0.16), Observable(0.08)
|
||||
Du, Dv = Observable(0.142), Observable(0.078)
|
||||
# feed rate of 'u' and kill rate of 'v'
|
||||
F, k = Observable(0.060), Observable(0.062)
|
||||
F, k = Observable(0.0617), Observable(0.062)
|
||||
dt = 1.0
|
||||
params_obs = Observable(GSParams(N, dx, Du[], Dv[], F[], k[]))
|
||||
|
||||
|
|
@ -24,6 +24,7 @@ stepsize = Observable{Int}(30)
|
|||
function update_params!(params_obs::Observable, u, v, feed, kill)
|
||||
old = params_obs[]
|
||||
params_obs[] = GSParams(old.N, old.dx, u, v, feed, kill)
|
||||
current_params()
|
||||
end
|
||||
|
||||
# Whenever a value gets changed via the textbox, update params object to reflect changes in simulation
|
||||
|
|
@ -35,8 +36,40 @@ V = zeros(N, N)
|
|||
center = N ÷ 2
|
||||
radius = 10
|
||||
# set a cube in the center with starting concentrations for 'u' and 'v'
|
||||
U[center-radius:center+radius, center-radius:center+radius] .= 0.50
|
||||
V[center-radius:center+radius, center-radius:center+radius] .= 0.25
|
||||
|
||||
jitter_amount = 5 # maximale Verschiebung in Pixeln
|
||||
margin = radius + 2 # Abstand zum Rand
|
||||
|
||||
# Manuell definierte gleichverteilte Positionen (6 Punkte)
|
||||
positions = [
|
||||
(1/4, 1/4),
|
||||
(3/4, 1/4),
|
||||
(1/2, 1/2),
|
||||
(1/4, 3/4),
|
||||
(3/4, 3/4),
|
||||
(1/2, 1/8)
|
||||
]
|
||||
|
||||
centers = []
|
||||
|
||||
for (px, py) in positions
|
||||
# Position relativ zu Feldgröße + Jitter
|
||||
cx = clamp(round(Int, px * N) + rand(-jitter_amount:jitter_amount), margin, N - margin)
|
||||
cy = clamp(round(Int, py * N) + rand(-jitter_amount:jitter_amount), margin, N - margin)
|
||||
|
||||
push!(centers, (cx, cy))
|
||||
|
||||
for x in (cx - radius):(cx + radius)
|
||||
for y in (cy - radius):(cy + radius)
|
||||
if x > 0 && x ≤ N && y > 0 && y ≤ N
|
||||
if sqrt((x - cx)^2 + (y - cy)^2) ≤ radius
|
||||
U[x, y] = 0.5
|
||||
V[x, y] = 0.25
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Observable holding current U for heatmap
|
||||
heat_obs = Observable(U)
|
||||
|
|
@ -170,12 +203,47 @@ param_box!(4, "kill", k)
|
|||
# Timer and state for animation
|
||||
running = Observable(false)
|
||||
function reset!(U, V, heat_obs)
|
||||
current_params()
|
||||
U .= 1.0
|
||||
V .= 0.0
|
||||
center = size(U, 1) ÷ 2
|
||||
radius = 10
|
||||
U[center-radius:center+radius, center-radius:center+radius] .= 0.50
|
||||
V[center-radius:center+radius, center-radius:center+radius] .= 0.25
|
||||
|
||||
num_spots = 6
|
||||
jitter_amount = 5 # maximale Verschiebung in Pixeln
|
||||
margin = radius + 2 # Abstand zum Rand
|
||||
|
||||
# Manuell definierte gleichverteilte Positionen (6 Punkte)
|
||||
positions = [
|
||||
(1/4, 1/4),
|
||||
(3/4, 1/4),
|
||||
(1/2, 1/2),
|
||||
(1/4, 3/4),
|
||||
(3/4, 3/4),
|
||||
(1/2, 1/8)
|
||||
]
|
||||
|
||||
centers = []
|
||||
|
||||
for (px, py) in positions
|
||||
# Position relativ zu Feldgröße + Jitter
|
||||
cx = clamp(round(Int, px * N) + rand(-jitter_amount:jitter_amount), margin, N - margin)
|
||||
cy = clamp(round(Int, py * N) + rand(-jitter_amount:jitter_amount), margin, N - margin)
|
||||
|
||||
push!(centers, (cx, cy))
|
||||
|
||||
for x in (cx - radius):(cx + radius)
|
||||
for y in (cy - radius):(cy + radius)
|
||||
if x > 0 && x ≤ N && y > 0 && y ≤ N
|
||||
if sqrt((x - cx)^2 + (y - cy)^2) ≤ radius
|
||||
U[x, y] = 0.5
|
||||
V[x, y] = 0.25
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
heat_obs[] = copy(U)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ struct FHNParams
|
|||
end
|
||||
|
||||
struct GSParams
|
||||
N::Int # grid size
|
||||
N::Int64 # grid size
|
||||
dx::Float64 # grid spacing
|
||||
Du::Float64 # diffusion rate U
|
||||
Dv::Float64 # diffusion rate V
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
module Visualization
|
||||
include("gray_scott_solver.jl")
|
||||
using GLMakie, Observables, Makie
|
||||
using Random
|
||||
using .GrayScottSolver
|
||||
|
||||
"""
|
||||
step_through_solution(sol::SolutionType, N::Int)
|
||||
|
||||
Function for visualization for the output of run_simulation
|
||||
|
||||
# Arguments
|
||||
- `sol`: computed differential equation by run_simulation
|
||||
- `N`: size of the N×N grid
|
||||
- sol: computed differential equation by run_simulation
|
||||
- N: size of the N×N grid
|
||||
|
||||
# Returns
|
||||
- ``: Displays created figure
|
||||
- Displays created figure
|
||||
"""
|
||||
function step_through_solution(sol, N::Int)
|
||||
fig = Figure(resolution=(600, 600))
|
||||
|
|
@ -24,45 +26,79 @@ function step_through_solution(sol, N::Int)
|
|||
# Initialize heatmap with first time step
|
||||
u0 = reshape(sol[1][1:N^2], N, N)
|
||||
heat_obs = Observable(u0)
|
||||
hmap = heatmap!(ax, heat_obs, colormap=:magma)
|
||||
heatmap!(ax, heat_obs, colormap=:magma)
|
||||
|
||||
on(textbox.stored_string) do s
|
||||
F[] = parse(Float64, s)
|
||||
end
|
||||
|
||||
# Update heatmap on slider movement
|
||||
on(slider.value) do i
|
||||
u = reshape(sol[i][1:N^2], N, N)
|
||||
heat_obs[] = u
|
||||
end
|
||||
|
||||
|
||||
display(fig)
|
||||
end
|
||||
|
||||
function coord_to_index(x, y, N)
|
||||
ix = clamp(round(Int, x), 1, N)
|
||||
iy = clamp(round(Int, y), 1, N)
|
||||
return ix, iy
|
||||
end
|
||||
|
||||
function reset!(U, V, heat_obs)
|
||||
U .= 1.0
|
||||
V .= 0.0
|
||||
# Default starting point for Run
|
||||
default_start!(U, V)
|
||||
heat_obs[] = copy(U)
|
||||
end
|
||||
|
||||
function default_start!(U, V)
|
||||
center = size(U, 1) ÷ 2
|
||||
radius = 10
|
||||
U[center-radius:center+radius, center-radius:center+radius] .= 0.50
|
||||
V[center-radius:center+radius, center-radius:center+radius] .= 0.25
|
||||
heat_obs[] = copy(U)
|
||||
end
|
||||
|
||||
function starting_points!(U, V)
|
||||
N = size(U, 1)
|
||||
radius = 10
|
||||
jitter = 5
|
||||
margin = radius + 2
|
||||
rels = [
|
||||
(1 / 4, 1 / 4), (3 / 4, 1 / 4), (1 / 2, 1 / 2),
|
||||
(1 / 4, 3 / 4), (3 / 4, 3 / 4), (1 / 2, 1 / 8)
|
||||
]
|
||||
for (px, py) in rels
|
||||
cx = clamp(round(Int, px * N) + rand(-jitter:jitter), margin, N - margin)
|
||||
cy = clamp(round(Int, py * N) + rand(-jitter:jitter), margin, N - margin)
|
||||
for x in (cx-radius):(cx+radius), y in (cy-radius):(cy+radius)
|
||||
if 1 ≤ x ≤ N && 1 ≤ y ≤ N && hypot(x - cx, y - cy) ≤ radius
|
||||
U[x, y] = 0.50
|
||||
V[x, y] = 0.25
|
||||
end
|
||||
end
|
||||
end
|
||||
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[])")
|
||||
box = Textbox(grid[row, 2], validator=Float64, width=50,
|
||||
placeholder=labeltxt, stored_string="$(observable[])")
|
||||
on(box.stored_string) do s
|
||||
try
|
||||
observable[] = parse(Float64, s)
|
||||
println("changed $labeltxt to $s")
|
||||
box.displayed_string[] = s
|
||||
catch
|
||||
@warn "Invalid input for $labeltxt: $s"
|
||||
end
|
||||
end
|
||||
on(observable) do val
|
||||
box.displayed_string[] = string(val)
|
||||
end
|
||||
return box
|
||||
end
|
||||
|
||||
function build_ui(U, V, Du, Dv, F, k, params_obs, heat_obs)
|
||||
|
|
@ -70,67 +106,72 @@ function build_ui(U, V, Du, Dv, F, k, params_obs, heat_obs)
|
|||
fig = Figure(size=(800, 800))
|
||||
gh = GridLayout(fig[1, 1])
|
||||
ax = Axis(gh[1, 1])
|
||||
hm = Makie.heatmap!(ax, heat_obs, colormap=:viridis)
|
||||
heatmap!(ax, heat_obs, colormap=:viridis)
|
||||
deactivate_interaction!(ax, :rectanglezoom)
|
||||
ax.aspect = DataAspect()
|
||||
|
||||
run_label = Observable{String}("Run")
|
||||
run_label = Observable("Run")
|
||||
stepsize = Observable(30)
|
||||
spoint = select_point(ax.scene)
|
||||
|
||||
# # 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[]))
|
||||
btn_step = Button(buttongrid[1, 1], width=60, label="Step")
|
||||
btn_start = Button(buttongrid[1, 2], width=60, label=run_label)
|
||||
btn_reset = Button(buttongrid[1, 3], width=60, label="Reset")
|
||||
btn_Jaguar = Button(buttongrid[1, 4], width=60, label="Jaguar")
|
||||
|
||||
slidergrid = SliderGrid(fig[3, 1], (label="Speed", range=1:100,
|
||||
format="{}x", width=350,
|
||||
startvalue=stepsize[]))
|
||||
speed_slider = slidergrid.sliders[1].value
|
||||
|
||||
|
||||
gh[1, 2] = textboxgrid = GridLayout(ax.scene, tellwidth=false)
|
||||
rowsize!(gh, 1, Relative(1.0))
|
||||
|
||||
|
||||
param_box!(textboxgrid, 1, "Du", Du)
|
||||
param_box!(textboxgrid, 2, "Dv", Dv)
|
||||
param_box!(textboxgrid, 3, "Feed", F)
|
||||
param_box!(textboxgrid, 4, "kill", k)
|
||||
|
||||
# Timer and state for animation
|
||||
running = Observable(false)
|
||||
|
||||
running = Observable(false)
|
||||
on(running) do r
|
||||
run_label[] = r ? "Pause" : "Run"
|
||||
end
|
||||
|
||||
on(speed_slider) do s
|
||||
try
|
||||
stepsize[] = s
|
||||
println("Changed stepsize to $s")
|
||||
catch
|
||||
@warn "Invalid input for $s"
|
||||
end
|
||||
stepsize[] = s
|
||||
println("Changed stepsize to $s")
|
||||
end
|
||||
# Button Listeners
|
||||
|
||||
on(btn_step.clicks) do _
|
||||
multi_step!((U, V), stepsize[], heat_obs, params_obs)
|
||||
end
|
||||
|
||||
on(btn_start.clicks) do _
|
||||
running[] = !running[]
|
||||
if running[]
|
||||
@async while running[]
|
||||
multi_step!((U, V), stepsize[], heat_obs, params_obs)
|
||||
sleep(0.0015)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
on(btn_start.clicks) do _
|
||||
@async while running[]
|
||||
multi_step!((U, V), stepsize[], heat_obs, params_obs)
|
||||
sleep(0.0015) # ~20 FPS
|
||||
end
|
||||
on(btn_Jaguar.clicks) do _
|
||||
Du[] = 0.142
|
||||
Dv[] = 0.078
|
||||
F[] = 0.0617
|
||||
k[] = 0.062
|
||||
U .= 1.0
|
||||
V .= 0.0
|
||||
starting_points!(U, V)
|
||||
heat_obs[] = copy(U)
|
||||
end
|
||||
|
||||
on(btn_reset.clicks) do _
|
||||
running[] = false
|
||||
reset!(U, V, heat_obs)
|
||||
|
||||
end
|
||||
on(spoint) do pt
|
||||
r = 5
|
||||
|
|
@ -138,7 +179,6 @@ function build_ui(U, V, Du, Dv, F, k, 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
|
||||
|
|
@ -156,6 +196,5 @@ function build_ui(U, V, Du, Dv, F, k, params_obs, heat_obs)
|
|||
return fig
|
||||
end
|
||||
|
||||
|
||||
export step_through_solution, build_ui
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue