Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow evaluating FE functions at arbitrary points #523

Merged
merged 68 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6064415
Allow evaluating FE functions at arbitrary points
eschnett Jan 26, 2021
f123693
Add function to evaluate FE functions
eschnett Jan 26, 2021
7e785be
New function inverse_map
ericneiva Jan 4, 2021
027fa14
bugfixed in CellField change_domain
ericneiva Jan 19, 2021
81bf213
Move code to the right location
eschnett Jan 26, 2021
0fd60ff
Reformat code
eschnett Jan 26, 2021
a7350a4
Merge branch 'master' into eschnett/evaluate
eschnett Jan 26, 2021
b6a18d4
Import return_cache
eschnett Jan 26, 2021
06b3524
Remove dependency on Bernstein.jl
eschnett Jan 27, 2021
c363d2d
Change sign convention for distance from simplex
eschnett Jan 27, 2021
0fcba26
Merge branch 'master' into eschnett/evaluate
eschnett Jan 27, 2021
489a377
Simplify finding minimum distance from simplices
eschnett Jan 27, 2021
01ef2f1
Calculate k-d tree only when needed
eschnett Jan 27, 2021
5103e07
simplexify: return tet simplex instead of hex simplex in 1d
eschnett Jan 28, 2021
bc6d049
Move evaluation function to MultiFields module
eschnett Jan 28, 2021
d1548f1
Comment code
eschnett Jan 28, 2021
c658948
Merge branch 'master' into eschnett/evaluate
eschnett Jan 31, 2021
921ced8
Merge branch 'master' into eschnett/evaluate
eschnett Feb 2, 2021
e7b2ddc
Merge branch 'master' into eschnett/evaluate
eschnett Feb 3, 2021
1e92b75
Merge branch 'master' into eschnett/evaluate
eschnett Feb 5, 2021
4f33d9b
Merge branch 'master' into eschnett/evaluate
eschnett Feb 28, 2021
9bd3a22
Add various comments with ideas or questions
eschnett Feb 28, 2021
c6ebff7
Add progress output while running test case
eschnett Feb 28, 2021
af56037
Shorten code
eschnett Feb 28, 2021
6b081cf
Rewrite code, making use of existing Gridap features
eschnett Mar 4, 2021
7c6038d
Merge branch 'master' into eschnett/evaluate
eschnett Mar 4, 2021
d6187d1
Commit current state
eschnett Mar 4, 2021
b2f4775
Merge branch 'master' into eschnett/evaluate
eschnett Mar 5, 2021
d5f15e6
Merge branch 'affine_map' into eschnett/evaluate
eschnett Mar 5, 2021
7544e8c
Things seem to work now.
eschnett Mar 5, 2021
0d765f1
Remove left-over comments
eschnett Mar 5, 2021
06ec403
Correct error bounds in self check
eschnett Mar 5, 2021
73672a3
Avoid temporary array
eschnett Mar 5, 2021
43afc11
Allow more general cell index types
eschnett Mar 5, 2021
f8998e3
Correct indentation
eschnett Mar 5, 2021
2f2e82f
Use vertex coordinates, not node coordinates to locate cells
eschnett Mar 6, 2021
aab614f
Merge branch 'master' into eschnett/evaluate
eschnett Mar 9, 2021
d02b072
Merge branch 'master' into eschnett/evaluate
eschnett Mar 9, 2021
3a393cd
Merge branch 'master' into eschnett/evaluate
eschnett Mar 25, 2021
db06d38
Merge branch 'master' into eschnett/evaluate
eschnett Mar 29, 2021
2c7bacd
Merge branch 'master' into eschnett/evaluate
eschnett Apr 9, 2021
7fccd3c
Merge branch 'master' into eschnett/evaluate
eschnett Apr 15, 2021
8d11d5c
Commit all changes
eschnett Apr 26, 2021
28a8563
Merge branch 'master' into eschnett/evaluate
eschnett Apr 26, 2021
6873673
Add tests for inverse maps
eschnett Apr 28, 2021
c1628dc
Merge branch 'master' into eschnett/evaluate
eschnett Apr 29, 2021
7197cec
Correct module imports
eschnett Apr 29, 2021
785876a
Make evaluate! for CellField more efficient
eschnett Apr 30, 2021
b65f9f6
Test evaluating cell fields on arrays of points
eschnett Apr 30, 2021
57ac76a
Remove duplicate include statement
eschnett Apr 30, 2021
4fef3f1
Implement efficient vectorized evaluation
eschnett Apr 30, 2021
a2fd337
Merge branch 'master' of github.com:gridap/Gridap.jl into HEAD
Balaje May 29, 2021
7548314
Move code to src/CellData from src/MultiField
Balaje May 31, 2021
5823b9a
Fixing comment in CellFields and remove repeating InverseFields Tests
Balaje May 31, 2021
a818db8
Merge pull request #1 from Balaje/eschnett/evaluate
eschnett Jun 1, 2021
0f6663f
Add more tests for evaluate
Balaje Jun 1, 2021
008a46a
Remove unused deps in MultiField.jl, more test for evaluate
Balaje Jun 1, 2021
e034de1
Fix an error
Balaje Jun 1, 2021
54962d9
Fix error in MultiField.jl
Balaje Jun 1, 2021
5c445d1
Remove comment
Balaje Jun 1, 2021
8ade674
Change to not export distance
Balaje Jun 1, 2021
0b4d2d9
Remove unused argument from point_to_cell
Balaje Jun 1, 2021
7a79a6e
Add interface for AbstractVector{<:Point}
Balaje Jun 2, 2021
620ee7f
Fix function names
Balaje Jun 2, 2021
0af55ff
Change from CellPoint to compute_cell_points_from_vector_of_points
Balaje Jun 2, 2021
4ff072c
Merge branch 'master' of github.com:gridap/Gridap.jl into tmp
Balaje Jun 2, 2021
b7f83ba
Update NEWS.md
Balaje Jun 2, 2021
6ce661f
Merge pull request #2 from Balaje/eschnett/evaluate
eschnett Jun 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56"
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Expand All @@ -40,6 +41,7 @@ JLD2 = "0.1.11, 0.3, 0.4"
JSON = "0.21.0"
LineSearches = "7.0.1"
NLsolve = "4.3.0"
NearestNeighbors = "0.4.8"
QuadGK = "2.3.1, 2.4"
SparseMatricesCSR = "0.6"
StaticArrays = "0.12.1, 1.0"
Expand Down
3 changes: 2 additions & 1 deletion src/CellData/CellData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ using DocStringExtensions
using FillArrays

using Gridap.Helpers
using Gridap.Arrays
using Gridap.Algebra
using Gridap.Arrays
using Gridap.TensorValues
using Gridap.Fields
using Gridap.ReferenceFEs
Expand All @@ -20,6 +20,7 @@ using Gridap.Geometry
import Gridap.Arrays: lazy_append
import Gridap.Arrays: get_array
import Gridap.Arrays: evaluate!
import Gridap.Arrays: return_cache
import Gridap.Fields: gradient
import Gridap.Fields: ∇∇
import Gridap.Fields: integrate
Expand Down
16 changes: 3 additions & 13 deletions src/CellData/CellFields.jl
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,6 @@ DomainStyle(::Type{GenericCellField{DS}}) where DS = DS()

(a::CellField)(x) = evaluate(a,x)

function evaluate!(cache,f::CellField,x::Point)
@notimplemented """\n
Evaluation of a CellField at a given Point is not implemented yet.

This is a feature that we want to have at some point in Gridap.
If you are ready to help with this implementation, please contact the
Gridap administrators.
"""
end

function evaluate!(cache,f::CellField,x::CellPoint)
_f, _x = _to_common_domain(f,x)
cell_field = get_data(_f)
Expand Down Expand Up @@ -334,11 +324,11 @@ struct OperationCellField{DS} <: CellField
r = Fields.BroadcastingFieldOpMap(op.op)(axi...)
catch
@unreachable """\n
It is not possible to perform operation $(op.op) on the given cell fields.
It is not possible to perform the operation "$(op.op)" on the given cell fields.

See the caught error for more information. (If you are using Visual
See the caught error for more information. (If you are using the Visual
Studio Code REPL you might not see the caught error, please use the
command-line REPL).
command-line REPL instead).
"""
end
end
Expand Down
11 changes: 10 additions & 1 deletion src/MultiField/MultiField.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@ using Gridap.TensorValues
using Gridap.CellData
using Gridap.Fields

using Gridap.FESpaces: FEBasis, TestBasis, TrialBasis, get_cell_dof_values
fverdugo marked this conversation as resolved.
Show resolved Hide resolved
using Gridap.FESpaces: SingleFieldFEBasis, TestBasis, TrialBasis
using Gridap.ReferenceFEs: HEX_AXIS, TET_AXIS, ExtrusionPolytope,
get_faces, get_node_coordinates, get_polytope,
num_cell_dims

using FillArrays
using SparseArrays
using LinearAlgebra
import BlockArrays
using BlockArrays
using NearestNeighbors
using StaticArrays

import Gridap.Arrays: evaluate!
import Gridap.Arrays: return_cache

export num_fields
export compute_field_offsets
Expand Down
164 changes: 164 additions & 0 deletions src/MultiField/MultiFieldCellFields.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,167 @@ num_fields(a::MultiFieldCellField) = length(a.single_fields)
Base.getindex(a::MultiFieldCellField,i::Integer) = a.single_fields[i]
Base.iterate(a::MultiFieldCellField) = iterate(a.single_fields)
Base.iterate(a::MultiFieldCellField,state) = iterate(a.single_fields,state)

fverdugo marked this conversation as resolved.
Show resolved Hide resolved
# Evaluation of CellFields
fverdugo marked this conversation as resolved.
Show resolved Hide resolved

# This code lives here (instead of in the CellData module) because we
# need access to MultiField functionality.

"""
dist = distance(polytope::ExtrusionPolytope,
inv_cmap::Field,
x::Point)

Calculate distance from point `x` to the polytope. The polytope is
given by its type and by the inverse cell map, i.e. by the map from
the physical to the reference space.

Positive distances are outside the polytope, negative distances are
inside the polytope.

The distance is measured in an unspecified norm, currently the L∞
norm.
"""
function distance(polytope::ExtrusionPolytope, inv_cmap::Field, x::Point)
extrusion = polytope.extrusion
isempty(extrusion) && return zero(eltype(x))
p = inv_cmap(x)
if all(e == HEX_AXIS for e in extrusion)
# Boundaries are at `a=0` and `a=1` in each direction
return maximum(max(0 - a, a - 1) for a in p)
elseif all(e == TET_AXIS for e in extrusion)
# Calculate barycentric coordinates
λ = Point(p..., 1 - sum(p))
return maximum(-λ)
else
@notimplemented "Only hypercubes and simplices are implemented so far"
end
end

function return_cache(f::CellField,x::Point)
trian = get_triangulation(f)
topo = GridTopology(trian) # Note: this is expensive
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)
cache1 = kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map

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,f::CellField,x::Point)
kdtree, vertex_to_cells, cell_to_ctype, ctype_to_polytope, cell_map = cache

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

# Find all neighbouring cells
cells = 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
# 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
end

function evaluate!(cache,f::CellField,x::Point)
cache1,cache2 = cache
cell_f_cache, f_cache, cell_f, f₀ = cache2
@check f === f₀ "Wrong cache"

cell = point_to_cell!(cache1,f,x)
cf = getindex!(cell_f_cache, cell_f, cell)
fx = evaluate!(f_cache, cf, x)
return fx
end

return_cache(f::CellField,xs::AbstractVector{<:Point}) = return_cache(f,testitem(xs))

# # Simple version:
# function evaluate!(cache,f::CellField,xs::AbstractVector{<:Point})
# return map(x->evaluate!(cache,f,x), xs)
# end

function make_inverse_table(i2j::AbstractVector{<:Integer},nj::Int)
ni = length(i2j)
@assert nj≥0

p = sortperm(i2j)
# i2j[p] is a sorted array of js

data = p
ptrs = Array{Int}(undef, nj+1)
i2lis = Array{Int}(undef, ni)
jprev = zero(eltype(i2j))
liprev = 0
for (n,i) in enumerate(p)
j = i2j[i]
@assert jprev≤j≤nj
li = (j==jprev ? liprev : 0) + 1
ptrs[jprev+1:j] .= n
i2lis[i] = li
jprev = j
liprev = li
end
ptrs[jprev+1:nj+1] .= ni+1
j2is = Table(data,ptrs)

return j2is,i2lis
end

# Efficient version:
function evaluate!(cache,f::CellField,point_to_x::AbstractVector{<:Point})
cache1,cache2 = return_cache(f,testitem(point_to_x))
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"

ncells = length(cell_map)
x_to_cell(x) = point_to_cell!(cache1,f,x)
point_to_cell = map(x_to_cell,point_to_x)
cell_to_points,point_to_lpoint = make_inverse_table(point_to_cell,ncells)
cell_to_xs = lazy_map(Broadcasting(Reindex(point_to_x)),cell_to_points)
cell_to_f = get_array(f)
cell_to_fxs = lazy_map(evaluate,cell_to_f,cell_to_xs)
point_to_fxs = lazy_map(Reindex(cell_to_fxs),point_to_cell)
point_to_fx = lazy_map(getindex,point_to_fxs,point_to_lpoint)
collect(point_to_fx) # Collect into a plain array
end
7 changes: 3 additions & 4 deletions test/CellDataTests/CellFieldsTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ using Test
using BlockArrays
using FillArrays
using Gridap.Arrays
using Gridap.Fields
using Gridap.TensorValues
using Gridap.Fields
using Gridap.ReferenceFEs
using Gridap.Geometry
using Gridap.CellData
using Gridap.FESpaces

domain = (0,1,0,1)
cells = (3,3)
Expand Down Expand Up @@ -152,9 +154,6 @@ test_array(hx_N,collect(hx_N))






#np = 3
#ndofs = 4
#
Expand Down
2 changes: 2 additions & 0 deletions test/FieldsTests/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ using Test

@testset "InverseFields" begin include("InverseFieldsTests.jl") end

@testset "InverseFields" begin include("InverseFieldsTests.jl") end
fverdugo marked this conversation as resolved.
Show resolved Hide resolved

end
46 changes: 46 additions & 0 deletions test/MultiFieldTests/MultiFieldCellFieldsTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ using Gridap.Fields
using Gridap.CellData
using Gridap.MultiField
using Gridap.TensorValues
using Random
using StaticArrays
using Test

domain = (0,1,0,1)
Expand Down Expand Up @@ -116,6 +118,50 @@ cellmat1_Γ = integrate(((n⋅dv.⁺)-dq.⁻)*((n⋅du.⁺)+dp.⁻),quad_Γ)
cellmat2_Γ = integrate((n⋅dv.⁺)*(n⋅du.⁺)+(n⋅dv.⁺)*dp.⁻-dq.⁻*(n⋅du.⁺)-dq.⁻*dp.⁻,quad_Γ)
test_array(cellmat1_Γ,cellmat2_Γ,≈)

# Test function evaluation
fverdugo marked this conversation as resolved.
Show resolved Hide resolved

# Set reproducible random number seed
Random.seed!(0)
@testset "evaluating functions" for D in 1:5
xmin = 0
xmax = 1
domain = repeat([xmin, xmax], D)
ncells = 3
partition = repeat([ncells], D)
model = CartesianDiscreteModel(domain, partition)
# TODO: test both with and without this
model = simplexify(model)

order = 2
reffe = ReferenceFE(lagrangian, Float64, order)
V = FESpace(model, reffe)

coeff0 = rand(Float64)
coeffs = rand(SVector{D,Float64})
f(x) = coeffs ⋅ SVector(Tuple(x)) + coeff0
# TODO: use this mechanism instead to project
# Francesc Verdugo @fverdugo 13:11
# a(u,v) = ∫( u*v )dΩ
# l(v) = a(f,v)
# Solve a fe problem with this weak form
# See also tutorial 10, "Isotropic damage model", section "L2
# projection", function "project"
fh = interpolate_everywhere(f, V)
fhcache = return_cache(fh, VectorValue(zeros(D)...))

xs = [VectorValue(rand(D)...) for i in 1:10]
for x in xs
x = VectorValue(rand(D)...)
fx = f(x)
fhx = evaluate!(fhcache, fh, x)
@test fhx ≈ fx
end
fhxs = fh(xs)
@test fhxs ≈ f.(xs)
end



#a = cellmat_Γ
#using BenchmarkTools
#cache = array_cache(a)
Expand Down