154 lines
4.1 KiB
Julia
154 lines
4.1 KiB
Julia
using DifferentialEquations
|
||
using Random
|
||
using .Constants
|
||
|
||
"""
|
||
fhn(du, u, p:FHNParams, t:)
|
||
|
||
Implements the spatial dynamics of FitzHugh-Nagumo (fhn). Designed to be
|
||
within a larger numerical solver of partial differential equations.
|
||
|
||
# Arguments
|
||
- `du`: output argument which stores the calculated derivatives
|
||
- `u`: input vector containing the current state of the system at time t
|
||
- `p`: holds all the fixed parameters of the FHN model
|
||
- `t`: current time
|
||
|
||
# Returns
|
||
- `du`: calculated derivatives put back into the du array
|
||
"""
|
||
function fhn!(du, u, p::FHNParams, t=0)
|
||
u_mat = reshape(u[1:p.N^2], p.N, p.N) # activation variable
|
||
v_mat = reshape(u[p.N^2+1:end], p.N, p.N) # deactivation variable
|
||
|
||
Δu = reshape(laplacian(u_mat, p.N, p.dx), p.N, p.N)
|
||
Δv = reshape(laplacian(v_mat, p.N, p.dx), p.N, p.N)
|
||
|
||
fu = p.Du * Δu .+ u_mat .- u_mat .^ 3 ./ 3 .- v_mat
|
||
fv = p.Dv * Δv .+ p.ϵ * (u_mat .+ p.a .- p.b .* v_mat)
|
||
|
||
du .= vcat(vec(fu), vec(fv))
|
||
end
|
||
|
||
"""
|
||
run_simulation(tspan::Tuple{Float64,Float64}, N::Int)
|
||
|
||
solving the ODE and modelling it after FHN
|
||
|
||
# Arguments
|
||
- `tspan`: tuple of two Float64's representing start and end times for simulation
|
||
- `N`: size of the N×N grid
|
||
|
||
# Returns
|
||
- `sol`: solved differential equation (ODE)
|
||
"""
|
||
function run_simulation(tspan::Tuple{Float64,Float64}, N::Int)
|
||
# Turing-spot parameters
|
||
p = FHNParams(N=N, dx=1.0, Du=1e-5, Dv=1e-3, ϵ=0.01, a=0.1, b=0.5)
|
||
|
||
# Initial conditions (random noise)
|
||
Random.seed!(1234)
|
||
# Create two vectors with length N*N with numbers between 0.1 and 0.11
|
||
u0 = vec(0.1 .+ 0.01 .* rand(N, N))
|
||
v0 = vec(0.1 .+ 0.01 .* rand(N, N))
|
||
|
||
y0 = vcat(u0, v0)
|
||
|
||
prob = ODEProblem(fhn!, y0, tspan, p)
|
||
sol = solve(prob, BS3(), saveat=1.0) # You can try `Rosenbrock23()` too
|
||
|
||
return sol
|
||
end
|
||
|
||
# Laplacian with 5-point stencil
|
||
function laplacianA(A::Matrix{Float64}, N::Int, dx::Float64)
|
||
dx2 = dx^2
|
||
lap = zeros(N, N)
|
||
lap[2:end-1, 2:end-1] .= (
|
||
A[1:end-2, 2:end-1] .+ A[3:end, 2:end-1] # up/down
|
||
.+
|
||
A[2:end-1, 1:end-2] .+ A[2:end-1, 3:end] # left/right
|
||
.-
|
||
4 .* A[2:end-1, 2:end-1]
|
||
) ./ dx2
|
||
return lap
|
||
end
|
||
|
||
# Gray-Scott model: reaction + diffusion
|
||
function grayscott!(du, u, p::GSParams, t=0)
|
||
N = p.N
|
||
U = reshape(u[1:N^2], N, N)
|
||
V = reshape(u[N^2+1:end], N, N)
|
||
|
||
ΔU = laplacianA(U, N, p.dx)
|
||
ΔV = laplacianA(V, N, p.dx)
|
||
|
||
UVV = U .* V .* V
|
||
|
||
dU = p.Du * ΔU .- UVV .+ p.F .* (1 .- U)
|
||
dV = p.Dv * ΔV .+ UVV .- (p.F .+ p.k) .* V
|
||
|
||
du .= vcat(vec(dU), vec(dV))
|
||
end
|
||
|
||
# Run simulation
|
||
function run_simulationG(tspan::Tuple{Float64,Float64}, N::Int, GSP::GSParams)
|
||
# Replace this in run_simulation
|
||
p = GSP
|
||
|
||
# Initial conditions: U = 1, V = 0 everywhere
|
||
U = ones(N, N)
|
||
V = zeros(N, N)
|
||
|
||
# Seed a square in the center
|
||
center = N ÷ 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
|
||
|
||
y0 = vcat(vec(U), vec(V))
|
||
|
||
prob = ODEProblem(grayscott!, y0, tspan, p)
|
||
sol = solve(prob, BS3(), saveat=10.0) # or Rosenbrock23()
|
||
|
||
return sol
|
||
end
|
||
|
||
function run_simulationG_no_ode(tspan::Tuple{Float64,Float64}, N::Int, GSP::GSParams)
|
||
p = GSP
|
||
dx2 = p.dx^2
|
||
dt = 1.0 # fixed timestep
|
||
|
||
# Initialize U and V
|
||
U = ones(N, N)
|
||
V = zeros(N, N)
|
||
|
||
# Seed pattern in the center
|
||
center = N ÷ 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
|
||
|
||
# Store history for visualization (optional)
|
||
snapshots = []
|
||
|
||
for i in 1:tspan[2]
|
||
print("Running $i/$(tspan[2])\r")
|
||
|
||
lap_U = laplacianA(U, N, p.dx)
|
||
lap_V = laplacianA(V, N, p.dx)
|
||
|
||
UVV = U .* V .* V
|
||
dU = p.Du * lap_U .- UVV .+ p.F * (1 .- U)
|
||
dV = p.Dv * lap_V .+ UVV .- (p.F + p.k) .* V
|
||
|
||
U .+= dU * dt
|
||
V .+= dV * dt
|
||
|
||
if i % 100 == 0 # save every 100 steps
|
||
push!(snapshots, (copy(U), copy(V)))
|
||
end
|
||
end
|
||
return snapshots
|
||
end
|