Skip to content

Commit

Permalink
Merge pull request #729 from gridap/interpolation_point_error
Browse files Browse the repository at this point in the history
Interpolation point error
  • Loading branch information
fverdugo authored Dec 31, 2021
2 parents 735391b + 6838ed5 commit 1dae811
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 70 deletions.
1 change: 1 addition & 0 deletions src/CellData/CellData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export CellDof
export get_normal_vector
export get_cell_measure
export Interpolable
export KDTreeSearch

export make_inverse_table
export compute_cell_points_from_vector_of_points
Expand Down
149 changes: 80 additions & 69 deletions src/CellData/CellFields.jl
Original file line number Diff line number Diff line change
Expand Up @@ -256,74 +256,49 @@ function distance(polytope::ExtrusionPolytope, inv_cmap::Field, x::Point)
end
end

function return_cache(f::CellField,x::Point)
trian = get_triangulation(f)
cache1 = _point_to_cell_cache(trian)

cell_f = get_array(f)
cell_f_cache = array_cache(cell_f)
cf = testitem(cell_f)
f_cache = return_cache(cf,x)
cache2 = cell_f_cache, f_cache, cell_f, f

return cache1,cache2
end

function _point_to_cell_cache(trian::Triangulation)
model = get_background_model(trian)
topo = get_grid_topology(model)
vertex_coordinates = Geometry.get_vertex_coordinates(topo)
kdtree = KDTree(map(nc -> SVector(Tuple(nc)), vertex_coordinates))
D = num_cell_dims(trian)
vertex_to_cells = get_faces(topo, 0, D)
cell_to_ctype = get_cell_type(trian)
ctype_to_reffe = get_reffes(trian)
ctype_to_polytope = map(get_polytope, ctype_to_reffe)
cell_map = get_cell_map(trian)
table_cache = array_cache(vertex_to_cells)
cache1 = kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map, table_cache
end

function _point_to_cell!(cache, x::Point)
kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map, table_cache = cache

# Find nearest vertex
id,dist = nn(kdtree, SVector(Tuple(x)))

# Find all neighbouring cells
cells = getindex!(table_cache,vertex_to_cells,id)
@assert !isempty(cells)

# Calculate the distance from the point to all the cells. Without
# round-off, and with non-overlapping cells, the distance would be
# negative for exactly one cell and positive for all other ones. Due
# to round-off, the distance can be slightly negative or slightly
# positive for points near cell boundaries, in particular near
# vertices. In this case, choose the cell with the smallest
# distance, and check that the distance (if positive) is at most at
# round-off level.
T = eltype(dist)
function cell_distance(cell::Integer)
ctype = cell_to_ctype[cell]
polytope = ctype_to_polytope[ctype]
cmap = cell_map[cell]
inv_cmap = inverse_map(cmap)
return distance(polytope, inv_cmap, x)
end
# findmin, without allocating an array
cell = zero(eltype(cells))
dist = T(Inf)
for jcell in cells
jdist = cell_distance(jcell)
if jdist < dist
cell = jcell
dist = jdist
searchmethod, kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map, table_cache = cache

# Loop over the first m.num_nearest_vertex
for (id,dist) in zip(knn(kdtree, SVector(Tuple(x)), searchmethod.num_nearest_vertices, true)...)

# Find all neighbouring cells
cells = getindex!(table_cache,vertex_to_cells,id)
@assert !isempty(cells)

# Calculate the distance from the point to all the cells. Without
# round-off, and with non-overlapping cells, the distance would be
# negative for exactly one cell and positive for all other ones. Due
# to round-off, the distance can be slightly negative or slightly
# positive for points near cell boundaries, in particular near
# vertices. In this case, choose the cell with the smallest
# distance, and check that the distance (if positive) is at most at
# round-off level.
T = eltype(dist)
function cell_distance(cell::Integer)
ctype = cell_to_ctype[cell]
polytope = ctype_to_polytope[ctype]
cmap = cell_map[cell]
inv_cmap = inverse_map(cmap)
return distance(polytope, inv_cmap, x)
end
# findmin, without allocating an array
cell = zero(eltype(cells))
dist = T(Inf)
for jcell in cells
jdist = cell_distance(jcell)
if jdist < dist
cell = jcell
dist = jdist
end
end

dist 1000eps(T) && return cell

end
# Ensure the point is inside one of the cells, up to round-off errors
@check dist 1000eps(T) "Point is not inside any cell"

return cell
# Output error message if cell not found
@check false "Point $x is not inside any cell"
end

function evaluate!(cache,f::CellField,x::Point)
Expand Down Expand Up @@ -374,7 +349,7 @@ end
# Efficient version:
function evaluate!(cache,f::CellField,point_to_x::AbstractVector{<:Point})
cache1,cache2 = cache
kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map = cache1
searchmethod, kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map = cache1
cell_f_cache, f_cache, cell_f, f₀ = cache2
@check f === f₀ "Wrong cache"

Expand All @@ -391,7 +366,8 @@ function evaluate!(cache,f::CellField,point_to_x::AbstractVector{<:Point})
end

function compute_cell_points_from_vector_of_points(xs::AbstractVector{<:Point}, trian::Triangulation, domain_style::PhysicalDomain)
cache1 = _point_to_cell_cache(trian)
searchmethod = KDTreeSearch()
cache1 = _point_to_cell_cache(searchmethod,trian)
x_to_cell(x) = _point_to_cell!(cache1, x)
point_to_cell = map(x_to_cell, xs)
ncells = num_cells(trian)
Expand Down Expand Up @@ -623,7 +599,7 @@ function _to_common_domain(a::CellField...)
Cannote operate cellfields defined over more than 2 different
triangulations at this moment.
"""
@notimplemented
@notimplemented
end
map(i->change_domain(i,target_trian,target_domain),a)
end
Expand Down Expand Up @@ -815,7 +791,12 @@ function (a::SkeletonPair{<:CellField})(x)
end

# Interpolable struct
struct KDTreeSearch end
struct KDTreeSearch
num_nearest_vertices::Int
function KDTreeSearch(;num_nearest_vertices=1)
new(num_nearest_vertices)
end
end

struct Interpolable{M,A} <: Function
uh::A
Expand All @@ -826,5 +807,35 @@ struct Interpolable{M,A} <: Function
end
end

return_cache(a::Interpolable,x::Point) = return_cache(a.uh,x)
(a::Interpolable)(x) = evaluate(a,x)
evaluate!(cache,a::Interpolable,x::Point) = evaluate!(cache,a.uh,x)
return_cache(f::CellField,x::Point) = return_cache(Interpolable(f),x)

function return_cache(a::Interpolable,x::Point)
f = a.uh
trian = get_triangulation(f)
cache1 = _point_to_cell_cache(a.searchmethod,trian)

cell_f = get_array(f)
cell_f_cache = array_cache(cell_f)
cf = testitem(cell_f)
f_cache = return_cache(cf,x)
cache2 = cell_f_cache, f_cache, cell_f, f

return cache1,cache2
end

function _point_to_cell_cache(searchmethod::KDTreeSearch,trian::Triangulation)
model = get_background_model(trian)
topo = get_grid_topology(model)
vertex_coordinates = Geometry.get_vertex_coordinates(topo)
kdtree = KDTree(map(nc -> SVector(Tuple(nc)), vertex_coordinates))
D = num_cell_dims(trian)
vertex_to_cells = get_faces(topo, 0, D)
cell_to_ctype = get_cell_type(trian)
ctype_to_reffe = get_reffes(trian)
ctype_to_polytope = map(get_polytope, ctype_to_reffe)
cell_map = get_cell_map(trian)
table_cache = array_cache(vertex_to_cells)
cache1 = searchmethod, kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map, table_cache
end
19 changes: 18 additions & 1 deletion test/FESpacesTests/CellFieldsTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ source_model = CartesianDiscreteModel((0,1,0,1),(2,2))
for pt in pts
@test gh(pt) fh(pt)
end

# Deformed mesh
function map(x)
if x[1]1.0 && x[2]1.0
x = VectorValue(0.6,0.6)
end
x
end
model = CartesianDiscreteModel((0,1,0,1),(1,1),map=map) |> simplexify
reffe = ReferenceFE(lagrangian,Float64,1)
V = FESpace(model,reffe)
f(x) = x[1]+x[2]
u = interpolate_everywhere(f,V)
x = VectorValue(0.45,0.45)
@test_throws AssertionError u(x)
sm=KDTreeSearch(num_nearest_vertices=2)
ux = Interpolable(u;searchmethod=sm)(x)
@test ux == 0.9
end

@testset "Test interpolation RT" begin
Expand All @@ -162,4 +180,3 @@ end


end # module

0 comments on commit 1dae811

Please sign in to comment.