Skip to content

Commit

Permalink
Add interpolations (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
utkinis authored Mar 27, 2024
1 parent 13cddba commit f7a5415
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 123 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Chmy"
uuid = "33a72cf0-4690-46d7-b987-06506c2248b9"
authors = ["Ivan Utkin <[email protected]>, Ludovic Raess <[email protected]>, and contributors"]
version = "0.1.4"
version = "0.1.5"

[deps]
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
Expand Down
2 changes: 1 addition & 1 deletion src/Chmy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ include("utils.jl")

include("Architectures.jl")
include("Grids/Grids.jl")
include("GridOperators/GridOperators.jl")
include("Fields/Fields.jl")
include("GridOperators/GridOperators.jl")

include("BoundaryConditions/BoundaryConditions.jl")

Expand Down
1 change: 0 additions & 1 deletion src/Fields/Fields.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export divg
using Chmy
using Chmy.Grids
using Chmy.Architectures
import Chmy.GridOperators
import Chmy: @add_cartesian

import Base.@propagate_inbounds
Expand Down
51 changes: 0 additions & 51 deletions src/Fields/abstract_field.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,54 +25,3 @@ end
function Base.show(io::IO, field::AbstractField{T,N,L}) where {T,N,L}
print(io, "$(N)D $(join(size(field), "×")) $(nameof(typeof(field))){$T}")
end

# grid operators
@propagate_inbounds @add_cartesian function GridOperators.left(f::AbstractField, dim, I::Vararg{Integer,N}) where {N}
GridOperators.left(f, flip(location(f, dim)), dim, I...)
end

@propagate_inbounds @add_cartesian function GridOperators.right(f::AbstractField, dim, I::Vararg{Integer,N}) where {N}
GridOperators.right(f, flip(location(f, dim)), dim, I...)
end

@propagate_inbounds @add_cartesian function GridOperators.δ(f::AbstractField, dim, I::Vararg{Integer,N}) where {N}
GridOperators.δ(f, flip(location(f, dim)), dim, I...)
end

@propagate_inbounds @add_cartesian function GridOperators.(f::AbstractField, grid, dim, I::Vararg{Integer,N}) where {N}
GridOperators.(f, flip(location(f, dim)), grid, dim, I...)
end

# operators on Cartesian grids
for (dim, coord) in enumerate((:x, :y, :z))
left = Symbol(:left, coord)
right = Symbol(:right, coord)
δ = Symbol(, coord)
= Symbol(:∂, coord)

@eval begin
@propagate_inbounds @add_cartesian function GridOperators.$left(f::AbstractField, I::Vararg{Integer,N}) where {N}
GridOperators.left(f, flip(location(f, Dim($dim))), Dim($dim), I...)
end

@propagate_inbounds @add_cartesian function GridOperators.$right(f::AbstractField, I::Vararg{Integer,N}) where {N}
GridOperators.right(f, flip(location(f, Dim($dim))), Dim($dim), I...)
end

@propagate_inbounds @add_cartesian function GridOperators.(f::AbstractField, I::Vararg{Integer,N}) where {N}
GridOperators.δ(f, flip(location(f, Dim($dim))), Dim($dim), I...)
end

@propagate_inbounds @add_cartesian function GridOperators.$∂(f::AbstractField, grid, I::Vararg{Integer,N}) where {N}
GridOperators.(f, flip(location(f, Dim($dim))), grid, Dim($dim), I...)
end
end
end

const SG = StructuredGrid

@propagate_inbounds @generated function divg(V::NamedTuple{names,<:NTuple{N,AbstractField}}, grid::SG{N}, I::Vararg{Integer,N}) where {names,N}
:(Base.Cartesian.@ncall $N (+) D -> GridOperators.(V[D], grid, Dim(D), I...))
end

@propagate_inbounds divg(V::NamedTuple{names,<:NTuple{N,AbstractField}}, grid::SG{N}, I::CartesianIndex{N}) where {names,N} = divg(V, grid, Tuple(I)...)
5 changes: 0 additions & 5 deletions src/Fields/field.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ const LocOrLocs{N} = Union{Location,NTuple{N,Location}}
# adapt to GPU
Adapt.adapt_structure(to, f::Field{T,N,L,H}) where {T,N,L,H} = Field{L,H}(Adapt.adapt(to, f.data), f.dims)

# fields on grids

expand_loc(::Val{N}, locs::NTuple{N,Location}) where {N} = locs
expand_loc(::Val{N}, loc::Location) where {N} = ntuple(_ -> loc, Val(N))

"""
Field(backend, grid, loc, type=eltype(grid); halo=1)
Expand Down
83 changes: 21 additions & 62 deletions src/GridOperators/GridOperators.jl
Original file line number Diff line number Diff line change
@@ -1,76 +1,35 @@
module GridOperators

using Chmy
using Chmy.Grids
import Chmy.@add_cartesian

export left, right, δ, ∂, lerp

Base.@assume_effects :foldable p(::Dim{D}, I::Vararg{Integer,N}) where {D,N} = ntuple(i -> i == D ? I[i] + oneunit(I[i]) : I[i], Val(N))
Base.@assume_effects :foldable m(::Dim{D}, I::Vararg{Integer,N}) where {D,N} = ntuple(i -> i == D ? I[i] - oneunit(I[i]) : I[i], Val(N))

@add_cartesian left(f, ::Vertex, dim, I::Vararg{Integer,N}) where {N} = f[m(dim, I...)...]
@add_cartesian left(f, ::Center, dim, I::Vararg{Integer,N}) where {N} = f[I...]

@add_cartesian right(f, ::Vertex, dim, I::Vararg{Integer,N}) where {N} = f[I...]
@add_cartesian right(f, ::Center, dim, I::Vararg{Integer,N}) where {N} = f[p(dim, I...)...]

# finite difference
@add_cartesian δ(f, loc, dim, I::Vararg{Integer,N}) where {N} = right(f, loc, dim, I...) - left(f, loc, dim, I...)
export left, right, δ, ∂

# partial derivative
@add_cartesian (f, loc, grid, dim, I::Vararg{Integer,N}) where {N} = δ(f, loc, dim, I...) * (grid, loc, dim, I...)
export InterpolationRule, Linear, HarmonicLinear
export itp, lerp, hlerp

# interpolation
@add_cartesian lerp(f, ::Center, grid, dim, I::Vararg{Integer,N}) where {N} = eltype(f)(0.5) * (f[I...] + f[p(dim, I...)...])
@add_cartesian function lerp(f, ::Vertex, grid, dim, I::Vararg{Integer,N}) where {N}
t = eltype(f)(0.5) * Δ(grid, Center(), dim, m(dim, I...)...) * (grid, Vertex(), dim, I...)
a = f[I...]
b = f[m(dim, I...)...]
return muladd(t, a - b, b)
end

# more efficient for uniform grids
@add_cartesian lerp(f, ::Vertex, grid::UniformGrid, dim, I::Vararg{Integer,N}) where {N} = eltype(f)(0.5) * (f[m(dim, I...)...] + f[I...])

# operators on Cartesian grids
for (dim, coord) in enumerate((:x, :y, :z))
left = Symbol(:left, coord)
right = Symbol(:right, coord)
δ = Symbol(, coord)
= Symbol(:∂, coord)

@eval begin
export $δ, $∂, $left, $right
using Chmy
using Chmy.Grids
using Chmy.Fields

"""
$($left)(f, loc, I)
import Chmy.@add_cartesian

"left side" of a field (`[1:end-1]`) in $($(string(coord))) direction.
"""
@add_cartesian $left(f, loc, I::Vararg{Integer,N}) where {N} = left(f, loc, Val($dim), I...)
import Base: @propagate_inbounds, front

"""
$($right)(f, loc, I)
p(::Dim{D}, I::Vararg{Integer,N}) where {D,N} = ntuple(i -> i == D ? I[i] + oneunit(I[i]) : I[i], Val(N))
m(::Dim{D}, I::Vararg{Integer,N}) where {D,N} = ntuple(i -> i == D ? I[i] - oneunit(I[i]) : I[i], Val(N))

"right side" of a field (`[2:end]`) in $($(string(coord))) direction.
"""
@add_cartesian $right(f, loc, I::Vararg{Integer,N}) where {N} = right(f, loc, Val($dim), I...)
@add_cartesian il(loc::Vertex, from::Center, dim, I::Vararg{Integer,N}) where {N} = I
@add_cartesian il(loc::Center, from::Vertex, dim, I::Vararg{Integer,N}) where {N} = m(dim, I...)

"""
$($δ)(f, loc, I)
@add_cartesian ir(loc::Vertex, from::Center, dim, I::Vararg{Integer,N}) where {N} = p(dim, I...)
@add_cartesian ir(loc::Center, from::Vertex, dim, I::Vararg{Integer,N}) where {N} = I

Finite difference in $($(string(coord))) direction.
"""
@add_cartesian $δ(f, loc, I::Vararg{Integer,N}) where {N} = δ(f, loc, Val($dim), I...)
@add_cartesian il(loc::L, from::L, dim, I::Vararg{Integer,N}) where {N,L<:Location} = m(dim, I...)
@add_cartesian ir(loc::L, from::L, dim, I::Vararg{Integer,N}) where {N,L<:Location} = p(dim, I...)

"""
$($∂)(f, loc, grid, I)
@add_cartesian left(f, loc, from, dim, I::Vararg{Integer,N}) where {N} = f[il(loc, from, dim, I...)...]
@add_cartesian right(f, loc, from, dim, I::Vararg{Integer,N}) where {N} = f[ir(loc, from, dim, I...)...]

Directional partial derivative in $($(string(coord))) direction.
"""
@add_cartesian $(f, loc, grid, I::Vararg{Integer,N}) where {N} = (f, loc, grid, Val($dim), I...)
end
end
include("partial_derivatives.jl")
include("interpolation.jl")
include("field_operators.jl")

end
65 changes: 65 additions & 0 deletions src/GridOperators/field_operators.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# staggered grid operators
@add_cartesian left(f::AbstractField, dim, I::Vararg{Integer,N}) where {N} = left(f, location(f, dim), flip(location(f, dim)), dim, I...)

@add_cartesian right(f::AbstractField, dim, I::Vararg{Integer,N}) where {N} = right(f, location(f, dim), flip(location(f, dim)), dim, I...)

@add_cartesian δ(f::AbstractField, dim, I::Vararg{Integer,N}) where {N} = δ(f, location(f, dim), flip(location(f, dim)), dim, I...)

@add_cartesian (f::AbstractField, grid, dim, I::Vararg{Integer,N}) where {N} = (f, location(f, dim), flip(location(f, dim)), grid, dim, I...)

# staggered operators on Cartesian grids
for (dim, coord) in enumerate((:x, :y, :z))
_l = Symbol(:left, coord)
_r = Symbol(:right, coord)
= Symbol(, coord)
_∂ = Symbol(:∂, coord)

@eval begin
export $_δ, $_∂, $_l, $_r

"""
$($_l)(f, I)
"left side" of a field (`[1:end-1]`) in $($(string(coord))) direction.
"""
@add_cartesian function $_l(f::AbstractField, I::Vararg{Integer,N}) where {N}
left(f, Dim($dim), I...)
end

"""
$($_r)(f, I)
"right side" of a field (`[2:end]`) in $($(string(coord))) direction.
"""
@add_cartesian function $_r(f::AbstractField, I::Vararg{Integer,N}) where {N}
right(f, Dim($dim), I...)
end

"""
$($_δ)(f, I)
Finite difference in $($(string(coord))) direction.
"""
@add_cartesian function $_δ(f::AbstractField, I::Vararg{Integer,N}) where {N}
δ(f, Dim($dim), I...)
end

"""
$($_∂)(f, grid, I)
Directional partial derivative in $($(string(coord))) direction.
"""
@add_cartesian function $_∂(f::AbstractField, grid, I::Vararg{Integer,N}) where {N}
(f, grid, Dim($dim), I...)
end
end
end

# covariant derivatives
@propagate_inbounds @generated function divg(V::NamedTuple{names,<:NTuple{N,AbstractField}}, grid::StructuredGrid{N}, I::Vararg{Integer,N}) where {names,N}
return :(Base.Cartesian.@ncall $N (+) D -> (V[D], grid, Dim(D), I...))
end

@propagate_inbounds function divg(V::NamedTuple{names,<:NTuple{N,AbstractField}}, grid::StructuredGrid{N}, I::CartesianIndex{N}) where {names,N}
return divg(V, grid, Tuple(I)...)
end
97 changes: 97 additions & 0 deletions src/GridOperators/interpolation.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 1D interpolation rules

abstract type InterpolationRule end

struct Linear <: InterpolationRule end
struct HarmonicLinear <: InterpolationRule end

itp_rule(::Linear, t, a, b) = muladd(t, b - a, a)
itp_rule(::HarmonicLinear, t, a, b) = inv(muladd(t, inv(b) - inv(a), inv(a)))

# recursive rule application

itp_impl(r, t, v0, v1) = itp_impl(Dim(length(t)), r, t, v0, v1)

itp_impl(::Dim{D}, r, t, v0, v1) where {D} = itp_rule(r, last(t),
itp_impl(Dim(D - 1), r, front(t), v0...),
itp_impl(Dim(D - 1), r, front(t), v1...))

itp_impl(::Dim{1}, r, t, v0, v1) = itp_rule(r, last(t), v0, v1)

# interpolation weights

itp_weight(ax::AbstractAxis, ::Vertex, ::Center, i) = convert(eltype(ax), 0.5)
itp_weight(ax::AbstractAxis, ::Center, ::Vertex, i) = convert(eltype(ax), 0.5) * Δ(ax, Center(), i - oneunit(i)) * (ax, Vertex(), i)

# special rule for efficient interpolation on uniform axes
itp_weight(ax::UniformAxis, ::Center, ::Vertex, i) = convert(eltype(ax), 0.5)

# dimensions to interpolate

@generated function itp_dims(from::NTuple{N,Location}, to::NTuple{N,Location}) where {N}
dims = Tuple(Dim(D) for D in 1:N)
locs = zip(dims, from.instance, to.instance)

pred = (_, l1, l2) -> l1 !== l2
filtered_locs = Iterators.filter(splat(pred), locs)

dims_r, from_r, to_r = (zip(filtered_locs...)...,)

return :($dims_r, $from_r, $to_r)
end

# interpolation knots

@propagate_inbounds itp_knots(f, from, to, dims, I) = itp_knots_impl(f, from, to, dims, I)

@propagate_inbounds itp_knots_impl(f, from, to, dims, I) = (itp_knots_impl(f, front(from), front(to), front(dims), il(last(from), last(to), last(dims), I...)),
itp_knots_impl(f, front(from), front(to), front(dims), ir(last(from), last(to), last(dims), I...)))

@propagate_inbounds itp_knots_impl(f, from::Tuple{}, to::Tuple{}, dims::Tuple{}, I) = f[I...]

# interpolation interface

"""
itp(f, to, r, grid, I...)
Interpolates the field `f` from its current location to the specified location(s) `to` using the given interpolation rule `r`.
The indices specify the position within the grid at location(s) `to`.
"""
@add_cartesian function itp(f::AbstractField, to::NTuple{N,Location}, r::InterpolationRule, grid::StructuredGrid{N}, I::Vararg{Integer,N}) where {N}
from = location(f)
if typeof(from) === typeof(to)
return f[I...]
end

dims_r, from_r, to_r = itp_dims(from, to)

t = ntuple(Val(length(dims_r))) do D
ax = axis(grid, dims_r[D])
itp_weight(ax, from_r[D], to_r[D], I[D])
end

v = itp_knots(f, from_r, to_r, dims_r, I)

return itp_impl(r, t, v...)
end

# version for repeated locations
@add_cartesian itp(f, to::Location, r, grid, I::Vararg{Integer,N}) where {N} = itp(f, expand_loc(Val(N), to), r, grid, I...)

# shortcuts for common use cases

"""
lerp(f, to, grid, I...)
Linearly interpolate values of a field `f` to location `to`.
"""
@add_cartesian lerp(f, to, grid, I::Vararg{Integer,N}) where {N} = itp(f, to, Linear(), grid, I...)

"""
hlerp(f, to, grid, I...)
Interpolate a field `f` to location to using harmonic linear interpolation rule.
`rule(t, v0, v1) = 1/(1/v0 + t * (1/v1 - 1/v0))`
"""
@add_cartesian hlerp(f, to, grid, I::Vararg{Integer,N}) where {N} = itp(f, to, HarmonicLinear(), grid, I...)
5 changes: 5 additions & 0 deletions src/GridOperators/partial_derivatives.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# finite difference
@add_cartesian δ(f, loc, from, dim, I::Vararg{Integer,N}) where {N} = right(f, loc, from, dim, I...) - left(f, loc, from, dim, I...)

# partial derivative
@add_cartesian (f, loc, from, grid, dim, I::Vararg{Integer,N}) where {N} = δ(f, loc, from, dim, I...) * (grid, loc, dim, I...)
4 changes: 4 additions & 0 deletions src/Grids/Grids.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export StructuredGrid, UniformGrid
export nvertices, ncenters, spacing, inv_spacing, Δ, iΔ, coord, coords, center, vertex, centers, vertices
export origin, extent, bounds, axis
export direction, axes_names
export expand_loc
export connectivity

using Chmy
Expand All @@ -34,6 +35,9 @@ struct Periodic <: Connectivity end
struct Connected <: Connectivity end
struct Flat <: Connectivity end

expand_loc(::Val{N}, locs::NTuple{N,Location}) where {N} = locs
expand_loc(::Val{N}, loc::Location) where {N} = ntuple(_ -> loc, Val(N))

include("abstract_axis.jl")
include("uniform_axis.jl")
include("function_axis.jl")
Expand Down
4 changes: 3 additions & 1 deletion src/Grids/abstract_axis.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
abstract type AbstractAxis{T} end

Base.eltype(::AbstractAxis{T}) where {T} = T

ncenters(ax::AbstractAxis{T}) where {T} = nvertices(ax) - 1

Base.length(ax::AbstractAxis, ::Vertex) = nvertices(ax)
Expand All @@ -19,7 +21,7 @@ origin(ax::AbstractAxis, ::Center) = @inbounds center(ax, 1)
@propagate_inbounds spacing(ax::AbstractAxis, ::Center, i::Integer) = vertex(ax, i + 1) - vertex(ax, i)
@propagate_inbounds spacing(ax::AbstractAxis, ::Vertex, i::Integer) = center(ax, i) - center(ax, i - 1)

@propagate_inbounds inv_spacing(ax::AbstractAxis{T}, loc, i) where {T} = one(T) / spacing(ax, loc, i)
@propagate_inbounds inv_spacing(ax::AbstractAxis{T}, loc, i) where {T} = inv(spacing(ax, loc, i))

coords(ax::AbstractAxis, loc::Location) = @inbounds [coord(ax, loc, i) for i in 1:length(ax, loc)]

Expand Down
Loading

0 comments on commit f7a5415

Please sign in to comment.