Hi,
I’m trying to implement the 2D laplacian of a scalar field as a SciMLOperators.FunctionOperator, using centered finite differences. The op function needs to know the physical size of the grid (Lx, Ly) as well as the grid size (Nx, Ny).
Using LinearMaps, this is how I would proceed
struct LaplaceOperator <: LinearMaps.LinearMap{Float64}
Lx::Float64
Ly::Float64
Nx::Int
Ny::Int
size::Dims{2}
Δx²::Float64
Δy²::Float64
function LaplaceOperator(Lx::Float64, Ly::Float64, Nx::Int, Ny::Int)
ncells = Nx * Ny
return new{T}(Lx, Ly, Nx, Ny, Dims([ncells, ncells]), (Lx / Nx)^2, (Ly / Ny)^2)
end
end
Base.size(Δ::LaplaceOperator) = Δ.size
function LinearMaps._unsafe_mul!(v, Δ::LaplaceOperator, u::AbstractVector)
u_arr = reshape(u, Δ.Nx, Δ.Ny)
v_arr = reshape(v, Δ.Nx, Δ.Ny)
for j₀ ∈ 1:Δ.Ny
j₋₁ = j₀ == 1 ? Δ.Ny : j₀ - 1
j₊₁ = j₀ == Δ.Ny ? 1 : j₀ + 1
for i₀ ∈ 1:Δ.Nx
i₋₁ = i₀ == 1 ? Δ.Nx : i₀ - 1
i₊₁ = i₀ == Δ.Nx ? 1 : i₀ + 1
v_arr[i₀, j₀] = (
(u_arr[i₊₁, j₀] - 2 * u_arr[i₀, j₀] + u_arr[i₋₁, j₀]) / Δ.Δx² +
(u_arr[i₀, j₊₁] - 2 * u_arr[i₀, j₀] + u_arr[i₀, j₋₁]) / Δ.Δy²
)
end
end
return v
end
The resulting operator would then be created as follows
Lx = 2.5
Ly = 5.0
Nx = 4
Ny = 8
Δ_ref = LaplaceOperator(Lx, Ly, Nx, Ny)
ux = [sin(π * (n + 0.5) / Nx) for n = 0:(Nx-1)]
uy = [sin(π * (n + 0.5) / Ny) for n = 0:(Ny-1)]
u = ux .* uy'
u_vec = reshape(u, :)
v_ref = reshape(Δ_ref * u_vec, Nx, Ny)
With SciMLOperators, this is what I ended up with
struct LaplaceOperatorSciML
Lx::Float64
Ly::Float64
Nx::Int
Ny::Int
Δx²::Float64
Δy²::Float64
function LaplaceOperatorSciML(Lx::Float64, Ly::Float64, Nx::Int, Ny::Int)
return new(Lx, Ly, Nx, Ny, (Lx / Nx)^2, (Ly / Ny)^2)
end
end
function (Δ::LaplaceOperatorSciML)(v, u, p, t)
u_arr = reshape(u, Δ.Nx, Δ.Ny)
v_arr = reshape(v, Δ.Nx, Δ.Ny)
for j₀ ∈ 1:Δ.Ny
j₋₁ = j₀ == 1 ? Δ.Ny : j₀ - 1
j₊₁ = j₀ == Δ.Ny ? 1 : j₀ + 1
for i₀ ∈ 1:Δ.Nx
i₋₁ = i₀ == 1 ? Δ.Nx : i₀ - 1
i₊₁ = i₀ == Δ.Nx ? 1 : i₀ + 1
v_arr[i₀, j₀] = (
(u_arr[i₊₁, j₀] - 2 * u_arr[i₀, j₀] + u_arr[i₋₁, j₀]) / Δ.Δx² +
(u_arr[i₀, j₊₁] - 2 * u_arr[i₀, j₀] + u_arr[i₀, j₋₁]) / Δ.Δy²
)
end
end
return v
end
function laplace_operator_SciML(Lx::Float64, Ly::Float64, Nx::Int, Ny::Int)
Δ = LaplaceOperatorSciML(Lx, Ly, Nx, Ny)
u = zeros(Float64, Nx * Ny)
v = similar(u)
return FunctionOperator(Δ, u, v; isinplace=true)
end
Which is then used as follows
Δ_SciML = laplace_operator_SciML(Lx, Ly, Nx, Ny)
v_SciML = reshape(Δ_SciML * u_vec, Nx, Ny)
And it can be verified that
@assert all(isapprox.(v_SciML, v_ref, atol = 1e-15, rtol=1e-12))
I find the SciMLOperators implementation a bit awkward. Could you suggest a more idiomatic way? Thanks