Skip to content

Gray-Scott Reaction Diffusion ​

This example evolves two chemical fields with periodic boundaries according to the Gray-Scott PDE. The update function uses @accelerate to fuse eligible CUDA broadcasts and free temporary arrays after their last use.

julia
using cuNumeric

struct Params{T}
    dx::T
    dt::T
    c_u::T
    c_v::T
    f::T
    k::T

    function Params(dx=1.0f0, c_u=1.0f0, c_v=0.3f0, f=0.03f0, k=0.06f0)
        return new{Float32}(dx, dx/5, c_u, c_v, f, k)
    end
end

function bc!(u_new, v_new, u, v)
    u_new[:, 1] = u[:, end - 1]
    u_new[:, end] = u[:, 2]
    u_new[1, :] = u[end - 1, :]
    u_new[end, :] = u[2, :]
    v_new[:, 1] = v[:, end - 1]
    v_new[:, end] = v[:, 2]
    v_new[1, :] = v[end - 1, :]
    return v_new[end, :] = v[2, :]
end

@accelerate function step!(u, v, u_new, v_new, args::Params)
    F_u = @. -u[2:end-1, 2:end-1] * v[2:end-1, 2:end-1]^2 +
        args.f * (1.0f0 - u[2:end-1, 2:end-1])
    F_v = @. u[2:end-1, 2:end-1] * v[2:end-1, 2:end-1]^2 -
        (args.f + args.k) * v[2:end-1, 2:end-1]

    u_lap = @. (
        (u[3:end, 2:end-1] - 2u[2:end-1, 2:end-1] + u[1:end-2, 2:end-1]) / args.dx^2 +
        (u[2:end-1, 3:end] - 2u[2:end-1, 2:end-1] + u[2:end-1, 1:end-2]) / args.dx^2
    )
    v_lap = @. (
        (v[3:end, 2:end-1] - 2v[2:end-1, 2:end-1] + v[1:end-2, 2:end-1]) / args.dx^2 +
        (v[2:end-1, 3:end] - 2v[2:end-1, 2:end-1] + v[2:end-1, 1:end-2]) / args.dx^2
    )

    u_new[2:end-1, 2:end-1] = @. (args.c_u * u_lap + F_u) * args.dt + u[2:end-1, 2:end-1]
    v_new[2:end-1, 2:end-1] = @. (args.c_v * v_lap + F_v) * args.dt + v[2:end-1, 2:end-1]
    bc!(u_new, v_new, u, v)
    return nothing
end

function gray_scott()

    N = 100
    dims = (N, N)

    args = Params()

    n_steps = 2000 # number of steps to take
    snapshot_interval = 20 # steps to take between saved snapshots

    u = cuNumeric.ones(dims)
    v = cuNumeric.zeros(dims)
    u_new = cuNumeric.zeros(dims)
    v_new = cuNumeric.zeros(dims)

    # One flattened frame per column, the layout DMD wants.
    snapshots = cuNumeric.zeros(Float32, N * N, n_steps ÷ snapshot_interval)

    u[1:15,1:15] = cuNumeric.rand(15,15)
    v[1:15,1:15] = cuNumeric.rand(15,15)

    for n in 1:n_steps
        step!(u, v, u_new, v_new, args)
        # update u and v
        # this doesn't copy, this switching references
        u, u_new = u_new, u
        v, v_new = v_new, v

        if n%snapshot_interval == 0
            snapshots[:, n ÷ snapshot_interval] = cuNumeric.reshape(u, (N * N, 1))
        end
    end

    cuNumeric.h5write("gray-scott.h5", "u", snapshots)

    return u, v
end

u, v = gray_scott()