From 3b2d94adf14786b5ba5805a016b639a21af24d59 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Fri, 7 Jun 2024 08:38:47 +0200 Subject: [PATCH 01/56] WIP on local bdim --- src/bdim.jl | 147 ++++++++++++++++++++++++++++++++++++++++++++++ src/mesh.jl | 109 +++++++++++++++++++++++++++++++++- src/quadrature.jl | 2 + 3 files changed, 255 insertions(+), 3 deletions(-) diff --git a/src/bdim.jl b/src/bdim.jl index d6ffb105..82f961c2 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -177,3 +177,150 @@ function bdim_correction( δD = sparse(Is, Js, Ds, num_trgs, n) return δS, δD end + +function local_bdim_correction( + pde, + target, + source::Quadrature, + Sop, + Dop; + green_multiplier::Vector{<:Real}, + parameters = DimParameters(), + derivative::Bool = false, + maxdist = Inf, +) + imat_cond = imat_norm = res_norm = rhs_norm = -Inf + T = eltype(Sop) + N = ambient_dimension(source) + @assert eltype(Dop) == T "eltype of S and D must match" + m, n = length(target), length(source) + msh = source.mesh + qnodes = source.qnodes + neighbors = topological_neighbors(msh) + dict_near = etype_to_nearest_points(target, source; maxdist) + # find first an appropriate set of source points to center the monopoles + qmax = sum(size(mat, 1) for mat in values(source.etype2qtags)) # max number of qnodes per el + ns = ceil(Int, parameters.sources_oversample_factor * qmax) + # compute a bounding box for source points + low_corner = reduce((p, q) -> min.(coords(p), coords(q)), source) + high_corner = reduce((p, q) -> max.(coords(p), coords(q)), source) + xc = (low_corner + high_corner) / 2 + R = parameters.sources_radius_multiplier * norm(high_corner - low_corner) / 2 + xs = if N === 2 + uniform_points_circle(ns, R, xc) + elseif N === 3 + fibonnaci_points_sphere(ns, R, xc) + else + error("only 2D and 3D supported") + end + # figure out if we are dealing with a scalar or vector PDE + σ = if T <: Number + 1 + else + @assert allequal(size(T)) + size(T, 1) + end + # compute traces of monopoles on the source mesh + G = SingleLayerKernel(pde, T) + γ₁G = DoubleLayerKernel(pde, T) + γ₁ₓG = AdjointDoubleLayerKernel(pde, T) + γ₀B = Matrix{T}(undef, length(source), ns) + γ₁B = Matrix{T}(undef, length(source), ns) + for k in 1:ns + for j in 1:length(source) + γ₀B[j, k] = G(source[j], xs[k]) + γ₁B[j, k] = γ₁ₓG(source[j], xs[k]) + end + end + Is, Js, Ss, Ds = Int[], Int[], T[], T[] + for (E, qtags) in source.etype2qtags + els = elements(msh, E) + near_list = dict_near[E] + nq, ne = size(qtags) + @assert length(near_list) == ne + # preallocate a local matrix to store interpolant values resulting + # weights. To benefit from Lapack, we must convert everything to + # matrices of scalars, so when `T` is an `SMatrix` we are careful to + # convert between the `Matrix{<:SMatrix}` and `Matrix{<:Number}` formats + # by viewing the elements of type `T` as `σ × σ` matrices of + # `eltype(T)`. + M_ = Matrix{eltype(T)}(undef, 2 * nq * σ, ns * σ) + W_ = Matrix{eltype(T)}(undef, 2 * nq * σ, σ) + W = T <: Number ? W_ : Matrix{T}(undef, 2 * nq, 1) + Θi_ = Matrix{eltype(T)}(undef, σ, ns * σ) + Θi = T <: Number ? Θi_ : Matrix{T}(undef, 1, ns) + K = derivative ? γ₁ₓG : G + # for each element, we will solve Mᵀ W = Θiᵀ, where W is a vector of + # size 2nq, and Θi is a row vector of length(ns) + for n in 1:ne + # if there is nothing near, skip immediately to next element + isempty(near_list[n]) && continue + el = els[n] + # copy the monopoles/dipoles for the current element + jglob = @view qtags[:, n] + M0 = @view γ₀B[jglob, :] + M1 = @view γ₁B[jglob, :] + _copyto!(view(M_, 1:(nq*σ), :), M0) + _copyto!(view(M_, (nq*σ+1):2*nq*σ, :), M1) + F_ = qr!(transpose(M_)) + @debug (imat_cond = max(cond(M_), imat_cond)) maxlog = 0 + @debug (imat_norm = max(norm(M_), imat_norm)) maxlog = 0 + # quadrature for auxiliary surface. In global dim, this is the same + # as the source quadrature, and independent of element. In local + # dim, this is constructed for each element using its neighbors. + function translate(q::QuadratureNode, x, s) + return QuadratureNode(coords(q) + x, weight(q), s * normal(q)) + end + nei = neighbors[(E, n)] + qtags_nei = Int[] + for (E, n) in nei + append!(qtags_nei, source.etype2qtags[E][:, n]) + end + qnodes_nei = source.qnodes[qtags_nei] + jac = jacobian(el, 0.5) + ν = _normal(jac) + h = sum(qnodes[i].weight for i in jglob) + qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) + a, b = external_boundary() + # qnodes_aux = source.qnodes[jglob] + qnodes_aux = source.qnodes # this is the global dim + for i in near_list[n] + # integrate the monopoles/dipoles over the auxiliary surface + # with target x: Θₖ <-- S[γ₁Bₖ](x) - D[γ₀Bₖ](x) + μ * Bₖ(x) + x = target[i] + μ = green_multiplier[i] + for k in 1:ns + Θi[k] = μ * K(x, xs[k]) + end + for q in qnodes_aux + SK = G(x, q) + DK = γ₁G(x, q) + for k in 1:ns + Θi[k] += (SK * γ₁ₓG(q, xs[k]) - DK * G(q, xs[k])) * weight(q) + end + end + Θi_ = _copyto!(Θi_, Θi) + @debug (rhs_norm = max(rhs_norm, norm(Θi))) maxlog = 0 + W_ = ldiv!(W_, F_, transpose(Θi_)) + @debug (res_norm = max(norm(Matrix(F_) * W_ - transpose(Θi_)), res_norm)) maxlog = + 0 + W = T <: Number ? W_ : _copyto!(W, W_) + for k in 1:nq + push!(Is, i) + push!(Js, jglob[k]) + push!(Ss, -W[nq+k]) # single layer corresponds to α=0,β=-1 + push!(Ds, W[k]) # double layer corresponds to α=1,β=0 + end + end + end + end + @debug """Condition properties of bdim correction: + |-- max interp. matrix cond.: $imat_cond + |-- max interp. matrix norm : $imat_norm + |-- max residual error: $res_norm + |-- max norm of source term: $rhs_norm + """ + δS = sparse(Is, Js, Ss, m, n) + δD = sparse(Is, Js, Ds, m, n) + return δS, δD +end diff --git a/src/mesh.jl b/src/mesh.jl index 86e4307e..71017d8c 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -51,11 +51,11 @@ for a given mesh). function elements end """ - element_types(msh::AbstractMesh) + elements(msh::AbstractMesh,E::DataType) -Return the element types present in the `msh`. +Return an iterator for all elements of type `E` on a mesh `msh`. """ -function element_types end +elements(msh::AbstractMesh, E::DataType) = ElementIterator{E,typeof(msh)}(msh) """ struct LagrangeMesh{N,T} <: AbstractMesh{N,T} @@ -548,3 +548,106 @@ end centers = map(center, els) return inrange(balltree, centers, tol) end + +""" + topological_neighbors(msh::LagrangeMesh, k=1) + +Return the `k` neighbors of each element in `msh`. The one-neighbors are the +elements that share a common vertex with the element, `k` neighbors are the +one-neighbors of the `k-1` neighbors. +""" +function topological_neighbors(msh::LagrangeMesh, k = 1) + @assert k == 1 + # dictionary mapping a node index to all elements containing it. Note + # that the elements are stored as a tuple (type, index) + T = Tuple{DataType,Int} + node2els = Dict{Int,Vector{T}}() + for E in element_types(msh) + mat = msh.etype2mat[E]::Matrix{Int} # connectivity matrix + np, Nel = size(mat) + for n in 1:Nel + for i in 1:np + idx = mat[i, n] + els = get!(node2els, idx, Vector{T}()) + push!(els, (E, n)) + end + end + end + # now revert the map to get the neighbors + one_neighbors = Dict{T,Set{T}}() + for (_, els) in node2els + for el in els + nei = get!(one_neighbors, el, Set{T}()) + for el′ in els + push!(nei, el′) + end + end + end + #TODO: for k > 1, recursively compute the neighbors from the one-neighbors + return one_neighbors +end + +""" + element_to_near_targets(X,Y::AbstractMesh; tol) + +For each element `el` of type `E` in `Y`, return the indices of the points in +`X` which are closer than `tol` to the `center` of `el`. + +This function returns a dictionary where e.g. `dict[E][5] --> Vector{Int}` gives +the indices of points in `X` which are closer than `tol` to the center of the +fifth element of type `E`. + +If `tol` is a `Dict`, then `tol[E]` is the tolerance for elements of type `E`. +""" +function element_to_near_targets( + X::AbstractVector{<:SVector{N}}, + Y::AbstractMesh{N}; + tol, +) where {N} + @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" + # for each element type, build the list of targets close to a given element + dict = Dict{DataType,Vector{Vector{Int}}}() + balltree = BallTree(X) + for E in element_types(Y) + els = elements(Y, E) + tol_ = isa(tol, Number) ? tol : tol[E] + idxs = _element_to_near_targets(balltree, els, tol_) + dict[E] = idxs + end + return dict +end + +@noinline function _element_to_near_targets(balltree, els, tol) + centers = map(center, els) + return inrange(balltree, centers, tol) +end + +""" + target_to_near_elements(X::AbstractVector{<:SVector{N}}, Y::AbstractMesh{N}; + tol) + +For each target `x` in `X`, return a vector of tuples `(E, i)` where `E` is the +type of the element in `Y` and `i` is the index of the element in `Y` such that +`x` is closer than `tol` to the center of the element. +""" +function target_to_near_elements( + X::AbstractVector{<:SVector{N}}, + Y::AbstractMesh{N}; + tol, +) where {N} + @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" + dict = Dict{Int,Vector{Tuple{DataType,Int}}}() + balltree = BallTree(X) + for E in element_types(Y) + els = elements(Y, E) + tol_ = isa(tol, Number) ? tol : tol[E] + idxs = _target_to_near_elements(balltree, els, tol_) + for (i, idx) in enumerate(idxs) + dict[i] = get!(dict, i, Vector{Tuple{DataType,Int}}()) + for j in idx + push!(dict[i], (E, j)) + end + end + end + return dict +end diff --git a/src/quadrature.jl b/src/quadrature.jl index 73fef9d0..0e84ecfb 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -45,6 +45,8 @@ flip_normal(q::QuadratureNode) = QuadratureNode(q.coords, q.weight, -q.normal) weight(q::QuadratureNode) = q.weight +translate(q::QuadratureNode, x) = QuadratureNode(coords(q) + x, weight(q), normal(q)) + # useful for using either a quadrature node or a just a simple point in # `IntegralOperators`. coords(x::Union{SVector,Tuple}) = SVector(x) From 62dd139029c8ae497f57b7a77464f864e99e8fae Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Fri, 7 Jun 2024 09:06:44 +0200 Subject: [PATCH 02/56] begin working towards `local_bdim` --- src/adaptive.jl | 10 ++--- src/bdim.jl | 39 ++++++++--------- src/mesh.jl | 107 ++++++++++++++++------------------------------ test/ldim_test.jl | 69 ++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 95 deletions(-) create mode 100644 test/ldim_test.jl diff --git a/src/adaptive.jl b/src/adaptive.jl index 97ea7148..df6b4fdf 100644 --- a/src/adaptive.jl +++ b/src/adaptive.jl @@ -42,7 +42,7 @@ function adaptive_correction(iop::IntegralOperator; tol, maxdist = nothing, maxs else maxdist end - dict_near = near_interaction_list(X, Y; tol = maxdist) + dict_near = elements_to_near_targets(X, Y; tol = maxdist) T = eltype(iop) msh = mesh(Y) correction = (I = Int[], J = Int[], V = T[]) @@ -166,14 +166,14 @@ function adaptive_integration_singular(f, τ̂::ReferenceLine, x̂ₛ; kwargs... end end -function near_interaction_list(QX::Quadrature, QY::Quadrature; tol) +function elements_to_near_targets(QX::Quadrature, QY::Quadrature; tol) X = [coords(q) for q in QX] msh = mesh(QY) - return near_interaction_list(X, msh; tol) + return elements_to_near_targets(X, msh; tol) end -function near_interaction_list(X, QY::Quadrature; tol) +function elements_to_near_targets(X, QY::Quadrature; tol) msh = mesh(QY) - return near_interaction_list(X, msh; tol) + return elements_to_near_targets(X, msh; tol) end """ diff --git a/src/bdim.jl b/src/bdim.jl index 82f961c2..bf10c2e8 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -181,22 +181,19 @@ end function local_bdim_correction( pde, target, - source::Quadrature, - Sop, - Dop; + source::Quadrature; green_multiplier::Vector{<:Real}, parameters = DimParameters(), derivative::Bool = false, maxdist = Inf, ) imat_cond = imat_norm = res_norm = rhs_norm = -Inf - T = eltype(Sop) + T = default_kernel_eltype(pde) # Float64 N = ambient_dimension(source) - @assert eltype(Dop) == T "eltype of S and D must match" m, n = length(target), length(source) msh = source.mesh qnodes = source.qnodes - neighbors = topological_neighbors(msh) + # neighbors = topological_neighbors(msh) dict_near = etype_to_nearest_points(target, source; maxdist) # find first an appropriate set of source points to center the monopoles qmax = sum(size(mat, 1) for mat in values(source.etype2qtags)) # max number of qnodes per el @@ -268,27 +265,27 @@ function local_bdim_correction( # quadrature for auxiliary surface. In global dim, this is the same # as the source quadrature, and independent of element. In local # dim, this is constructed for each element using its neighbors. - function translate(q::QuadratureNode, x, s) - return QuadratureNode(coords(q) + x, weight(q), s * normal(q)) - end - nei = neighbors[(E, n)] - qtags_nei = Int[] - for (E, n) in nei - append!(qtags_nei, source.etype2qtags[E][:, n]) - end - qnodes_nei = source.qnodes[qtags_nei] - jac = jacobian(el, 0.5) - ν = _normal(jac) - h = sum(qnodes[i].weight for i in jglob) - qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) - a, b = external_boundary() + # function translate(q::QuadratureNode, x, s) + # return QuadratureNode(coords(q) + x, weight(q), s * normal(q)) + # end + # nei = neighbors[(E, n)] + # qtags_nei = Int[] + # for (E, n) in nei + # append!(qtags_nei, source.etype2qtags[E][:, n]) + # end + # qnodes_nei = source.qnodes[qtags_nei] + # jac = jacobian(el, 0.5) + # ν = _normal(jac) + # h = sum(qnodes[i].weight for i in jglob) + # qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) + # a, b = external_boundary() # qnodes_aux = source.qnodes[jglob] qnodes_aux = source.qnodes # this is the global dim for i in near_list[n] # integrate the monopoles/dipoles over the auxiliary surface # with target x: Θₖ <-- S[γ₁Bₖ](x) - D[γ₀Bₖ](x) + μ * Bₖ(x) x = target[i] - μ = green_multiplier[i] + μ = green_multiplier[i] # - 1/2 for k in 1:ns Θi[k] = μ * K(x, xs[k]) end diff --git a/src/mesh.jl b/src/mesh.jl index 71017d8c..0cf511c1 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -515,7 +515,7 @@ function connectivity(msh::SubMesh, E::DataType) end """ - near_interaction_list(X,Y::AbstractMesh; tol) + elements_to_near_targets(X,Y::AbstractMesh; tol) For each element `el` of type `E` in `Y`, return the indices of the points in `X` which are closer than `tol` to the `center` of `el`. @@ -526,7 +526,7 @@ fifth element of type `E`. If `tol` is a `Dict`, then `tol[E]` is the tolerance for elements of type `E`. """ -function near_interaction_list( +function elements_to_near_targets( X::AbstractVector{<:SVector{N}}, Y::AbstractMesh{N}; tol, @@ -538,23 +538,57 @@ function near_interaction_list( for E in element_types(Y) els = elements(Y, E) tol_ = isa(tol, Number) ? tol : tol[E] - idxs = _near_interaction_list(balltree, els, tol_) + idxs = _elements_to_near_targets(balltree, els, tol_) dict[E] = idxs end return dict end -@noinline function _near_interaction_list(balltree, els, tol) +@noinline function _elements_to_near_targets(balltree, els, tol) centers = map(center, els) return inrange(balltree, centers, tol) end +""" + target_to_near_elements(X::AbstractVector{<:SVector{N}}, Y::AbstractMesh{N}; + tol) + +For each target `x` in `X`, return a vector of tuples `(E, i)` where `E` is the +type of the element in `Y` and `i` is the index of the element in `Y` such that +`x` is closer than `tol` to the center of the element. +""" +function target_to_near_elements( + X::AbstractVector{<:SVector{N}}, + Y::AbstractMesh{N}; + tol, +) where {N} + @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" + dict = Dict{Int,Vector{Tuple{DataType,Int}}}() + balltree = BallTree(X) + for E in element_types(Y) + els = elements(Y, E) + tol_ = isa(tol, Number) ? tol : tol[E] + idxs = _target_to_near_elements(balltree, els, tol_) + for (i, idx) in enumerate(idxs) + dict[i] = get!(dict, i, Vector{Tuple{DataType,Int}}()) + for j in idx + push!(dict[i], (E, j)) + end + end + end + return dict +end + """ topological_neighbors(msh::LagrangeMesh, k=1) Return the `k` neighbors of each element in `msh`. The one-neighbors are the elements that share a common vertex with the element, `k` neighbors are the one-neighbors of the `k-1` neighbors. + +This function returns a dictionary where the key is an `(Eᵢ,i)` tuple denoting +the element `i` of type `E` in the mesh, and the value is a set of tuples +`(Eⱼ,j)` where `Eⱼ` is the type of the element and `j` its index. """ function topological_neighbors(msh::LagrangeMesh, k = 1) @assert k == 1 @@ -586,68 +620,3 @@ function topological_neighbors(msh::LagrangeMesh, k = 1) #TODO: for k > 1, recursively compute the neighbors from the one-neighbors return one_neighbors end - -""" - element_to_near_targets(X,Y::AbstractMesh; tol) - -For each element `el` of type `E` in `Y`, return the indices of the points in -`X` which are closer than `tol` to the `center` of `el`. - -This function returns a dictionary where e.g. `dict[E][5] --> Vector{Int}` gives -the indices of points in `X` which are closer than `tol` to the center of the -fifth element of type `E`. - -If `tol` is a `Dict`, then `tol[E]` is the tolerance for elements of type `E`. -""" -function element_to_near_targets( - X::AbstractVector{<:SVector{N}}, - Y::AbstractMesh{N}; - tol, -) where {N} - @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" - # for each element type, build the list of targets close to a given element - dict = Dict{DataType,Vector{Vector{Int}}}() - balltree = BallTree(X) - for E in element_types(Y) - els = elements(Y, E) - tol_ = isa(tol, Number) ? tol : tol[E] - idxs = _element_to_near_targets(balltree, els, tol_) - dict[E] = idxs - end - return dict -end - -@noinline function _element_to_near_targets(balltree, els, tol) - centers = map(center, els) - return inrange(balltree, centers, tol) -end - -""" - target_to_near_elements(X::AbstractVector{<:SVector{N}}, Y::AbstractMesh{N}; - tol) - -For each target `x` in `X`, return a vector of tuples `(E, i)` where `E` is the -type of the element in `Y` and `i` is the index of the element in `Y` such that -`x` is closer than `tol` to the center of the element. -""" -function target_to_near_elements( - X::AbstractVector{<:SVector{N}}, - Y::AbstractMesh{N}; - tol, -) where {N} - @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" - dict = Dict{Int,Vector{Tuple{DataType,Int}}}() - balltree = BallTree(X) - for E in element_types(Y) - els = elements(Y, E) - tol_ = isa(tol, Number) ? tol : tol[E] - idxs = _target_to_near_elements(balltree, els, tol_) - for (i, idx) in enumerate(idxs) - dict[i] = get!(dict, i, Vector{Tuple{DataType,Int}}()) - for j in idx - push!(dict[i], (E, j)) - end - end - end - return dict -end diff --git a/test/ldim_test.jl b/test/ldim_test.jl new file mode 100644 index 00000000..845c0021 --- /dev/null +++ b/test/ldim_test.jl @@ -0,0 +1,69 @@ +using Test +using LinearAlgebra +using Inti +using Random +using Meshes +using GLMakie + +include("test_utils.jl") +Random.seed!(1) + +N = 2 +t = :interior +pde = Inti.Laplace(; dim = N) +Inti.clear_entities!() +Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2) +Γ = Inti.external_boundary(Ω) + +Γ_msh = msh[Γ] +Ω_msh = msh[Ω] +nei = Inti.topological_neighbors(Ω_msh) |> collect +function viz_neighbors(i) + k, v = nei[i] + E, idx = k + el = Inti.elements(Ω_msh, E)[idx] + fig, ax, pl = viz(el; color = 0, showsegments = true) + for (E, i) in v + el = Inti.elements(Ω_msh, E)[i] + viz!(el; color = 1 / 2, showsegments = true, alpha = 0.2) + end + return display(fig) +end + +## + +quad = Inti.Quadrature(view(msh, Γ); qorder = 3) +σ = t == :interior ? 1 / 2 : -1 / 2 +xs = t == :interior ? ntuple(i -> 3, N) : ntuple(i -> 0.1, N) +T = Inti.default_density_eltype(pde) +c = rand(T) +u = (qnode) -> Inti.SingleLayerKernel(pde)(qnode, xs) * c +dudn = (qnode) -> Inti.AdjointDoubleLayerKernel(pde)(qnode, xs) * c +γ₀u = map(u, quad) +γ₁u = map(dudn, quad) +γ₀u_norm = norm(norm.(γ₀u, Inf), Inf) +γ₁u_norm = norm(norm.(γ₁u, Inf), Inf) +# single and double layer +G = Inti.SingleLayerKernel(pde) +S = Inti.IntegralOperator(G, quad) +Smat = Inti.assemble_matrix(S) +dG = Inti.DoubleLayerKernel(pde) +D = Inti.IntegralOperator(dG, quad) +Dmat = Inti.assemble_matrix(D) +e0 = norm(Smat * γ₁u - Dmat * γ₀u - σ * γ₀u, Inf) / γ₀u_norm + +green_multiplier = fill(-0.5, length(quad)) +# δS, δD = Inti.bdim_correction(pde, quad, quad, Smat, Dmat; green_multiplier) +δS, δD = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) +Sdim = Smat + δS +Ddim = Dmat + δD +# Sdim, Ddim = Inti.single_double_layer(; +# pde, +# target = quad, +# source = quad, +# compression = (method = :none,), +# correction = (method = :ldim,), +# ) +e1 = norm(Sdim * γ₁u - Ddim * γ₀u - σ * γ₀u, Inf) / γ₀u_norm +@show norm(e0, Inf) +@show norm(e1, Inf) From cb315c6d61f98e99d544bf71bef2ce8beec90098 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Fri, 7 Jun 2024 10:46:00 +0200 Subject: [PATCH 03/56] small changes --- src/reference_interpolation.jl | 1 + test/ldim_test.jl | 2 +- test/test_utils.jl | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 112318dd..6d481619 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -262,6 +262,7 @@ const LagrangeCube = LagrangeElement{ReferenceCube} """ vertices_idxs(el::LagrangeElement) + vertices_idxs(::Type{LagrangeElement}) The indices of the nodes in `el` that define the vertices of the element. """ diff --git a/test/ldim_test.jl b/test/ldim_test.jl index 845c0021..170618e8 100644 --- a/test/ldim_test.jl +++ b/test/ldim_test.jl @@ -12,7 +12,7 @@ N = 2 t = :interior pde = Inti.Laplace(; dim = N) Inti.clear_entities!() -Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2) +Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2, order = 2) Γ = Inti.external_boundary(Ω) Γ_msh = msh[Γ] diff --git a/test/test_utils.jl b/test/test_utils.jl index 5112c107..3d74eff5 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -1,7 +1,7 @@ using Inti using Gmsh -function gmsh_disk(; center, rx, ry, meshsize) +function gmsh_disk(; center, rx, ry, meshsize, order = 1) msh = try gmsh.initialize() gmsh.option.setNumber("General.Verbosity", 2) @@ -12,6 +12,7 @@ function gmsh_disk(; center, rx, ry, meshsize) gmsh.model.occ.addDisk(center[1], center[2], 0, rx, ry) gmsh.model.occ.synchronize() gmsh.model.mesh.generate(2) + gmsh.model.mesh.setOrder(order) Inti.import_mesh(; dim = 2) finally gmsh.finalize() From 814bfd1c8de9071229c51b899f370a52dcd1c3d7 Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Mon, 10 Jun 2024 21:31:15 +0800 Subject: [PATCH 04/56] sketch of a function returning boundary --- src/reference_interpolation.jl | 47 +++++++++++++++++++++++++++++---- test/ldim_test.jl | 48 +++++++++++++++++++++++++++++----- test/test_utils.jl | 3 ++- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 112318dd..a94b2221 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -262,6 +262,7 @@ const LagrangeCube = LagrangeElement{ReferenceCube} """ vertices_idxs(el::LagrangeElement) + vertices_idxs(::Type{LagrangeElement}) The indices of the nodes in `el` that define the vertices of the element. """ @@ -284,16 +285,52 @@ vertices(el::LagrangeElement) = view(vals(el), vertices_idxs(el)) The indices of the nodes in `el` that define the boundary of the element. """ -function boundary_idxs(el::LagrangeLine) - return 1, length(vals(el)) + +function boundary_idxs(::Type{<:LagrangeLine{P}}) where {P} + return 1, P end -function boundary_idxs(el::LagrangeTriangle{3}) +function boundary_idxs(::Type{<:LagrangeTriangle{3}}) return (1, 2), (2, 3), (3, 1) end -function boundary_idxs(el::LagrangeTriangle{6}) - return (1, 2), (2, 3), (3, 1) +function boundary_idxs(::Type{<:LagrangeTriangle{6}}) + return (1, 2, 4), (2, 3, 5), (3, 1, 6) +end + +function boundary1d(els, msh) + res = Set() + E, _ = first(els) + bdi = Inti.boundary_idxs(E) + for (E, i) in els + vertices = Inti.connectivity(msh, E)[:,i] + bords = [vertices[bi] for bi in bdi] + for bord in bords + bord in res ? delete!(res, bord) : push!(res, bord) + end + end + return res +end + +function boundarynd(els, msh) + res = Set() + E, _ = first(els) + bdi = Inti.boundary_idxs(E) + for (E, i) in els + vertices = Inti.connectivity(msh, E)[:,i] + bords = [[vertices[i] for i in bi] for bi in bdi] + for new_bord in bords + flag = true + for old_bord in res + if sort(new_bord) == sort(old_bord) + delete!(res, old_bord) + flag = false + end + end + flag && push!(res, new_bord) + end + end + return res end #= diff --git a/test/ldim_test.jl b/test/ldim_test.jl index 845c0021..046a0341 100644 --- a/test/ldim_test.jl +++ b/test/ldim_test.jl @@ -12,24 +12,60 @@ N = 2 t = :interior pde = Inti.Laplace(; dim = N) Inti.clear_entities!() -Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2) +Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2, order = 1) +# Ω, msh = gmsh_ball(; center = [0.0, 0.0, 0.0], radius=1.0, meshsize = 0.2) Γ = Inti.external_boundary(Ω) Γ_msh = msh[Γ] Ω_msh = msh[Ω] -nei = Inti.topological_neighbors(Ω_msh) |> collect -function viz_neighbors(i) +test_msh = Γ_msh +nei = Inti.topological_neighbors(test_msh) |> collect +function viz_neighbors(i, msh) k, v = nei[i] E, idx = k - el = Inti.elements(Ω_msh, E)[idx] - fig, ax, pl = viz(el; color = 0, showsegments = true) + el = Inti.elements(msh, E)[idx] + fig, _, _ = viz(el; color = 0, showsegments = true) for (E, i) in v - el = Inti.elements(Ω_msh, E)[i] + el = Inti.elements(msh, E)[i] viz!(el; color = 1 / 2, showsegments = true, alpha = 0.2) end return display(fig) end +function viz_elements_bords(els, bords, msh) + fig, _, _ = viz(msh; color = 0, showsegments = false,alpha=0.5) + for (E, i) in els + el = Inti.elements(msh, E)[i] + viz!(el; showsegments = false, alpha=0.7) + end + viz!(bords;color=4,showsegments = false) + display(fig) +end + +el_in_set(el, set) = any(x->sort(x) == sort(el), set) + + +I = 5 +test_els = copy(nei[I][2]) +push!(test_els, nei[I][1]) + +BD = Inti.boundary1d(test_els, test_msh) + +bords = Inti.LagrangeElement[] +for idxs in BD + vtxs = Inti.nodes(Ω_msh)[idxs] + bord = Inti.LagrangeLine(vtxs...) + push!(bords, bord) +end + +viz_elements_bords(test_els, bords, test_msh) + +for bord in bords + viz!(bord;color=4) +end +viz(bords) +viz(first(bords)) + ## quad = Inti.Quadrature(view(msh, Γ); qorder = 3) diff --git a/test/test_utils.jl b/test/test_utils.jl index 5112c107..3d74eff5 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -1,7 +1,7 @@ using Inti using Gmsh -function gmsh_disk(; center, rx, ry, meshsize) +function gmsh_disk(; center, rx, ry, meshsize, order = 1) msh = try gmsh.initialize() gmsh.option.setNumber("General.Verbosity", 2) @@ -12,6 +12,7 @@ function gmsh_disk(; center, rx, ry, meshsize) gmsh.model.occ.addDisk(center[1], center[2], 0, rx, ry) gmsh.model.occ.synchronize() gmsh.model.mesh.generate(2) + gmsh.model.mesh.setOrder(order) Inti.import_mesh(; dim = 2) finally gmsh.finalize() From 4934db59bb7736813085519b672a992aa5db7900 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Tue, 11 Jun 2024 18:01:49 +0200 Subject: [PATCH 05/56] tweak `topological_neighbors` to work on `AbstractMesh` --- src/mesh.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh.jl b/src/mesh.jl index 0cf511c1..94ba229d 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -590,14 +590,14 @@ This function returns a dictionary where the key is an `(Eᵢ,i)` tuple denoting the element `i` of type `E` in the mesh, and the value is a set of tuples `(Eⱼ,j)` where `Eⱼ` is the type of the element and `j` its index. """ -function topological_neighbors(msh::LagrangeMesh, k = 1) +function topological_neighbors(msh::AbstractMesh, k = 1) @assert k == 1 # dictionary mapping a node index to all elements containing it. Note # that the elements are stored as a tuple (type, index) T = Tuple{DataType,Int} node2els = Dict{Int,Vector{T}}() for E in element_types(msh) - mat = msh.etype2mat[E]::Matrix{Int} # connectivity matrix + mat = connectivity(msh, E)::Matrix{Int} # connectivity matrix np, Nel = size(mat) for n in 1:Nel for i in 1:np From 78b74d98a6f1e8532f4c2b92b9d9903dfa40fcc9 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Tue, 11 Jun 2024 18:27:49 +0200 Subject: [PATCH 06/56] implement `k` topological neighbors --- src/mesh.jl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mesh.jl b/src/mesh.jl index 94ba229d..d71460e4 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -591,7 +591,6 @@ the element `i` of type `E` in the mesh, and the value is a set of tuples `(Eⱼ,j)` where `Eⱼ` is the type of the element and `j` its index. """ function topological_neighbors(msh::AbstractMesh, k = 1) - @assert k == 1 # dictionary mapping a node index to all elements containing it. Note # that the elements are stored as a tuple (type, index) T = Tuple{DataType,Int} @@ -617,6 +616,17 @@ function topological_neighbors(msh::AbstractMesh, k = 1) end end end - #TODO: for k > 1, recursively compute the neighbors from the one-neighbors - return one_neighbors + # Recursively compute the neighbors from the one-neighbors + k_neighbors = deepcopy(one_neighbors) + while k > 1 + # update neighborhood of each element + for el in keys(one_neighbors) + knn = k_neighbors[el] + for el′ in copy(knn) + union!(knn, one_neighbors[el′]) + end + end + k -= 1 + end + return k_neighbors end From 942af12ea9444c6556d91398c024fa3a1923a8d9 Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Wed, 12 Jun 2024 17:44:51 +0800 Subject: [PATCH 07/56] sketch implement of local dim --- Project.toml | 3 ++ ext/IntiMeshesExt.jl | 4 +- src/Inti.jl | 1 + src/bdim.jl | 38 ++++++++++--------- src/reference_interpolation.jl | 7 ++-- test/boundary_test.jl | 69 ++++++++++++++++++++++++++++++++++ test/ldim_test.jl | 60 +++++------------------------ 7 files changed, 108 insertions(+), 74 deletions(-) create mode 100644 test/boundary_test.jl diff --git a/Project.toml b/Project.toml index 6d5b8ae4..5c679b85 100644 --- a/Project.toml +++ b/Project.toml @@ -6,10 +6,13 @@ version = "0.1.0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ElementaryPDESolutions = "88a69b33-da0f-4502-8c8c-d680cf4d883b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" diff --git a/ext/IntiMeshesExt.jl b/ext/IntiMeshesExt.jl index 43d577f7..16440ca8 100644 --- a/ext/IntiMeshesExt.jl +++ b/ext/IntiMeshesExt.jl @@ -75,10 +75,10 @@ function Meshes.viz!(el::Inti.ReferenceInterpolant, args...; kwargs...) end function Meshes.viz(els::AbstractVector{<:Inti.ReferenceInterpolant}, args...; kwargs...) - return viz([to_meshes(el) for el in els]) + return viz([to_meshes(el) for el in els], args...; kwargs...) end function Meshes.viz!(els::AbstractVector{<:Inti.ReferenceInterpolant}, args...; kwargs...) - return viz!([to_meshes(el) for el in els]) + return viz!([to_meshes(el) for el in els], args...; kwargs...) end function Meshes.viz(msh::Inti.AbstractMesh, args...; kwargs...) diff --git a/src/Inti.jl b/src/Inti.jl index 55d79610..b2321b24 100644 --- a/src/Inti.jl +++ b/src/Inti.jl @@ -14,6 +14,7 @@ using LinearMaps using NearestNeighbors using SparseArrays using StaticArrays +using QuadGK using SpecialFunctions using Printf diff --git a/src/bdim.jl b/src/bdim.jl index bf10c2e8..c2245ab0 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -193,7 +193,7 @@ function local_bdim_correction( m, n = length(target), length(source) msh = source.mesh qnodes = source.qnodes - # neighbors = topological_neighbors(msh) + neighbors = topological_neighbors(msh) dict_near = etype_to_nearest_points(target, source; maxdist) # find first an appropriate set of source points to center the monopoles qmax = sum(size(mat, 1) for mat in values(source.etype2qtags)) # max number of qnodes per el @@ -265,22 +265,26 @@ function local_bdim_correction( # quadrature for auxiliary surface. In global dim, this is the same # as the source quadrature, and independent of element. In local # dim, this is constructed for each element using its neighbors. - # function translate(q::QuadratureNode, x, s) - # return QuadratureNode(coords(q) + x, weight(q), s * normal(q)) - # end - # nei = neighbors[(E, n)] - # qtags_nei = Int[] - # for (E, n) in nei - # append!(qtags_nei, source.etype2qtags[E][:, n]) - # end - # qnodes_nei = source.qnodes[qtags_nei] - # jac = jacobian(el, 0.5) - # ν = _normal(jac) - # h = sum(qnodes[i].weight for i in jglob) - # qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) - # a, b = external_boundary() - # qnodes_aux = source.qnodes[jglob] - qnodes_aux = source.qnodes # this is the global dim + function translate(q::QuadratureNode, x, s) + return QuadratureNode(coords(q) + x, weight(q), s * normal(q)) + end + nei = neighbors[(E, n)] + qtags_nei = Int[] + for (E, m) in nei + append!(qtags_nei, source.etype2qtags[E][:, m]) + end + qnodes_nei = source.qnodes[qtags_nei] + jac = jacobian(el, 0.5) + ν = -_normal(jac) + h = sum(qnodes[i].weight for i in jglob) + qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) + bindx = boundary1d(nei, msh) + l, r = nodes(msh)[-bindx[1]], nodes(msh)[bindx[2]] + Q, W = gauss(3nq, 0, h) + qnodes_l = [QuadratureNode(l.+q.*ν, w, SVector(-ν[2], ν[1])) for (q, w) in zip(Q, W)] + qnodes_r = [QuadratureNode(r.+q.*ν, w, SVector(ν[2], -ν[1])) for (q, w) in zip(Q, W)] + qnodes_aux = append!(qnodes_nei, qnodes_op, qnodes_l, qnodes_r) + # qnodes_aux = source.qnodes # this is the global dim for i in near_list[n] # integrate the monopoles/dipoles over the auxiliary surface # with target x: Θₖ <-- S[γ₁Bₖ](x) - D[γ₀Bₖ](x) + μ * Bₖ(x) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index a94b2221..89e20cb2 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -304,12 +304,11 @@ function boundary1d(els, msh) bdi = Inti.boundary_idxs(E) for (E, i) in els vertices = Inti.connectivity(msh, E)[:,i] - bords = [vertices[bi] for bi in bdi] - for bord in bords - bord in res ? delete!(res, bord) : push!(res, bord) + for bord in [-vertices[bdi[1]], vertices[bdi[2]]] + -bord in res ? delete!(res, -bord) : push!(res, bord) end end - return res + return sort([res...]) end function boundarynd(els, msh) diff --git a/test/boundary_test.jl b/test/boundary_test.jl new file mode 100644 index 00000000..7a4c2f69 --- /dev/null +++ b/test/boundary_test.jl @@ -0,0 +1,69 @@ +using Test +using LinearAlgebra +using Inti +using Random +using Meshes +using GLMakie + +include("test_utils.jl") +Random.seed!(1) + +N = 2 +t = :interior +pde = Inti.Laplace(; dim = N) +Inti.clear_entities!() +Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2, order = 1) +# Ω, msh = gmsh_ball(; center = [0.0, 0.0, 0.0], radius=1.0, meshsize = 0.2) +Γ = Inti.external_boundary(Ω) + +Γ_msh = msh[Γ] +Ω_msh = msh[Ω] +test_msh = Γ_msh +nei = Inti.topological_neighbors(test_msh) |> collect +function viz_neighbors(i, msh) + k, v = nei[i] + E, idx = k + el = Inti.elements(msh, E)[idx] + fig, _, _ = viz(el; color = 0, showsegments = true) + for (E, i) in v + el = Inti.elements(msh, E)[i] + viz!(el; color = 1 / 2, showsegments = true, alpha = 0.2) + end + return display(fig) +end + +function viz_elements_bords(Ei, els, bords, msh) + el = el = Inti.elements(msh, Ei[1])[Ei[2]] + fig, _, _ = viz(msh; color = 0, showsegments = true,alpha=0.3) + viz!(el; color = 0, showsegments = true,alpha=0.5) + for (E, i) in els + el = Inti.elements(msh, E)[i] + viz!(el; showsegments = true, alpha=0.7) + end + viz!(bords;color=4,showsegments = false,segmentsize=5,segmentcolor=4) + display(fig) +end + +# el_in_set(el, set) = any(x->sort(x) == sort(el), set) + +I = 10 +test_els = copy(nei[I][2]) +push!(test_els, nei[I][1]) + +BD = Inti.boundary1d(test_els, test_msh) +bords = [Inti.nodes(test_msh)[abs(i)] for i in BD] + +bords = Inti.LagrangeElement[] +for idxs in BD + vtxs = Inti.nodes(Ω_msh)[idxs] + bord = Inti.LagrangeLine(vtxs...) + push!(bords, bord) +end + +viz_elements_bords(nei[I][1], test_els, bords, test_msh) + +for bord in bords + viz!(bord;color=4) +end +viz(bords) +viz(first(bords)) \ No newline at end of file diff --git a/test/ldim_test.jl b/test/ldim_test.jl index 046a0341..316e1b8a 100644 --- a/test/ldim_test.jl +++ b/test/ldim_test.jl @@ -16,59 +16,9 @@ Inti.clear_entities!() # Ω, msh = gmsh_ball(; center = [0.0, 0.0, 0.0], radius=1.0, meshsize = 0.2) Γ = Inti.external_boundary(Ω) -Γ_msh = msh[Γ] -Ω_msh = msh[Ω] -test_msh = Γ_msh -nei = Inti.topological_neighbors(test_msh) |> collect -function viz_neighbors(i, msh) - k, v = nei[i] - E, idx = k - el = Inti.elements(msh, E)[idx] - fig, _, _ = viz(el; color = 0, showsegments = true) - for (E, i) in v - el = Inti.elements(msh, E)[i] - viz!(el; color = 1 / 2, showsegments = true, alpha = 0.2) - end - return display(fig) -end - -function viz_elements_bords(els, bords, msh) - fig, _, _ = viz(msh; color = 0, showsegments = false,alpha=0.5) - for (E, i) in els - el = Inti.elements(msh, E)[i] - viz!(el; showsegments = false, alpha=0.7) - end - viz!(bords;color=4,showsegments = false) - display(fig) -end - -el_in_set(el, set) = any(x->sort(x) == sort(el), set) - - -I = 5 -test_els = copy(nei[I][2]) -push!(test_els, nei[I][1]) - -BD = Inti.boundary1d(test_els, test_msh) - -bords = Inti.LagrangeElement[] -for idxs in BD - vtxs = Inti.nodes(Ω_msh)[idxs] - bord = Inti.LagrangeLine(vtxs...) - push!(bords, bord) -end - -viz_elements_bords(test_els, bords, test_msh) - -for bord in bords - viz!(bord;color=4) -end -viz(bords) -viz(first(bords)) - ## -quad = Inti.Quadrature(view(msh, Γ); qorder = 3) +quad = Inti.Quadrature(msh[Γ]; qorder = 3) σ = t == :interior ? 1 / 2 : -1 / 2 xs = t == :interior ? ntuple(i -> 3, N) : ntuple(i -> 0.1, N) T = Inti.default_density_eltype(pde) @@ -90,6 +40,14 @@ e0 = norm(Smat * γ₁u - Dmat * γ₀u - σ * γ₀u, Inf) / γ₀u_norm green_multiplier = fill(-0.5, length(quad)) # δS, δD = Inti.bdim_correction(pde, quad, quad, Smat, Dmat; green_multiplier) + +# qnodes = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) +# X = [q.coords[1] for q in qnodes]; Y = [q.coords[2] for q in qnodes] +# u = [q.normal[1] for q in qnodes]; v = [q.normal[2] for q in qnodes] +# fig, _, _ = scatter(X, Y) +# arrows!(X, Y, u, v, lengthscale=0.01) +# display(fig) + δS, δD = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) Sdim = Smat + δS Ddim = Dmat + δD From 6c88ace411838082d0fb28c7e3c0bc2f173983b8 Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Thu, 13 Jun 2024 21:02:43 +0800 Subject: [PATCH 08/56] Implemented k neighbor ldim --- Project.toml | 1 + src/bdim.jl | 7 +- src/reference_interpolation.jl | 8 +-- test/boundary_test.jl | 11 +--- test/ldim_test.jl | 114 ++++++++++++++++++--------------- 5 files changed, 75 insertions(+), 66 deletions(-) diff --git a/Project.toml b/Project.toml index 5c679b85..56a1c10f 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ElementaryPDESolutions = "88a69b33-da0f-4502-8c8c-d680cf4d883b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" +Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" diff --git a/src/bdim.jl b/src/bdim.jl index c2245ab0..74651766 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -186,6 +186,7 @@ function local_bdim_correction( parameters = DimParameters(), derivative::Bool = false, maxdist = Inf, + kneighbor = 1, ) imat_cond = imat_norm = res_norm = rhs_norm = -Inf T = default_kernel_eltype(pde) # Float64 @@ -193,7 +194,7 @@ function local_bdim_correction( m, n = length(target), length(source) msh = source.mesh qnodes = source.qnodes - neighbors = topological_neighbors(msh) + neighbors = topological_neighbors(msh, kneighbor) dict_near = etype_to_nearest_points(target, source; maxdist) # find first an appropriate set of source points to center the monopoles qmax = sum(size(mat, 1) for mat in values(source.etype2qtags)) # max number of qnodes per el @@ -276,11 +277,11 @@ function local_bdim_correction( qnodes_nei = source.qnodes[qtags_nei] jac = jacobian(el, 0.5) ν = -_normal(jac) - h = sum(qnodes[i].weight for i in jglob) + h = sum(qnodes[i].weight for i in jglob)*4 qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) bindx = boundary1d(nei, msh) l, r = nodes(msh)[-bindx[1]], nodes(msh)[bindx[2]] - Q, W = gauss(3nq, 0, h) + Q, W = gauss(4nq, 0, h) qnodes_l = [QuadratureNode(l.+q.*ν, w, SVector(-ν[2], ν[1])) for (q, w) in zip(Q, W)] qnodes_r = [QuadratureNode(r.+q.*ν, w, SVector(ν[2], -ν[1])) for (q, w) in zip(Q, W)] qnodes_aux = append!(qnodes_nei, qnodes_op, qnodes_l, qnodes_r) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 89e20cb2..398f031a 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -286,8 +286,8 @@ vertices(el::LagrangeElement) = view(vals(el), vertices_idxs(el)) The indices of the nodes in `el` that define the boundary of the element. """ -function boundary_idxs(::Type{<:LagrangeLine{P}}) where {P} - return 1, P +function boundary_idxs(::Type{<:LagrangeLine}) + return 1, 2 end function boundary_idxs(::Type{<:LagrangeTriangle{3}}) @@ -299,12 +299,12 @@ function boundary_idxs(::Type{<:LagrangeTriangle{6}}) end function boundary1d(els, msh) - res = Set() + res = Set{Int}() E, _ = first(els) bdi = Inti.boundary_idxs(E) for (E, i) in els vertices = Inti.connectivity(msh, E)[:,i] - for bord in [-vertices[bdi[1]], vertices[bdi[2]]] + for bord in (-vertices[bdi[1]], vertices[bdi[2]]) -bord in res ? delete!(res, -bord) : push!(res, bord) end end diff --git a/test/boundary_test.jl b/test/boundary_test.jl index 7a4c2f69..cb60dd13 100644 --- a/test/boundary_test.jl +++ b/test/boundary_test.jl @@ -12,14 +12,15 @@ N = 2 t = :interior pde = Inti.Laplace(; dim = N) Inti.clear_entities!() -Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2, order = 1) +Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2, order = 2) # Ω, msh = gmsh_ball(; center = [0.0, 0.0, 0.0], radius=1.0, meshsize = 0.2) Γ = Inti.external_boundary(Ω) +k = 2 Γ_msh = msh[Γ] Ω_msh = msh[Ω] test_msh = Γ_msh -nei = Inti.topological_neighbors(test_msh) |> collect +nei = Inti.topological_neighbors(test_msh, k) |> collect function viz_neighbors(i, msh) k, v = nei[i] E, idx = k @@ -61,9 +62,3 @@ for idxs in BD end viz_elements_bords(nei[I][1], test_els, bords, test_msh) - -for bord in bords - viz!(bord;color=4) -end -viz(bords) -viz(first(bords)) \ No newline at end of file diff --git a/test/ldim_test.jl b/test/ldim_test.jl index 9b070d36..ad6dc3db 100644 --- a/test/ldim_test.jl +++ b/test/ldim_test.jl @@ -3,60 +3,72 @@ using LinearAlgebra using Inti using Random using Meshes -using GLMakie +using Plots include("test_utils.jl") Random.seed!(1) N = 2 t = :interior -pde = Inti.Laplace(; dim = N) -Inti.clear_entities!() -Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = 0.2, order = 2) -Γ = Inti.external_boundary(Ω) - -## - -quad = Inti.Quadrature(msh[Γ]; qorder = 3) -σ = t == :interior ? 1 / 2 : -1 / 2 -xs = t == :interior ? ntuple(i -> 3, N) : ntuple(i -> 0.1, N) -T = Inti.default_density_eltype(pde) -c = rand(T) -u = (qnode) -> Inti.SingleLayerKernel(pde)(qnode, xs) * c -dudn = (qnode) -> Inti.AdjointDoubleLayerKernel(pde)(qnode, xs) * c -γ₀u = map(u, quad) -γ₁u = map(dudn, quad) -γ₀u_norm = norm(norm.(γ₀u, Inf), Inf) -γ₁u_norm = norm(norm.(γ₁u, Inf), Inf) -# single and double layer -G = Inti.SingleLayerKernel(pde) -S = Inti.IntegralOperator(G, quad) -Smat = Inti.assemble_matrix(S) -dG = Inti.DoubleLayerKernel(pde) -D = Inti.IntegralOperator(dG, quad) -Dmat = Inti.assemble_matrix(D) -e0 = norm(Smat * γ₁u - Dmat * γ₀u - σ * γ₀u, Inf) / γ₀u_norm - -green_multiplier = fill(-0.5, length(quad)) -# δS, δD = Inti.bdim_correction(pde, quad, quad, Smat, Dmat; green_multiplier) - -# qnodes = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) -# X = [q.coords[1] for q in qnodes]; Y = [q.coords[2] for q in qnodes] -# u = [q.normal[1] for q in qnodes]; v = [q.normal[2] for q in qnodes] -# fig, _, _ = scatter(X, Y) -# arrows!(X, Y, u, v, lengthscale=0.01) -# display(fig) - -δS, δD = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) -Sdim = Smat + δS -Ddim = Dmat + δD -# Sdim, Ddim = Inti.single_double_layer(; -# pde, -# target = quad, -# source = quad, -# compression = (method = :none,), -# correction = (method = :ldim,), -# ) -e1 = norm(Sdim * γ₁u - Ddim * γ₀u - σ * γ₀u, Inf) / γ₀u_norm -@show norm(e0, Inf) -@show norm(e1, Inf) +pde = Inti.Stokes(; dim = N, μ=1.2) + +K = 5:5 +H = [0.2*2.0^(-i) for i in 0:0] +fig = plot(xscale=:log,yscale=:log,xticks=H) +for k in K + err = [] + for h in H + Inti.clear_entities!() + Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = h, order = 2) + Γ = Inti.external_boundary(Ω) + + ## + + quad = Inti.Quadrature(msh[Γ]; qorder = 3) + σ = t == :interior ? 1 / 2 : -1 / 2 + xs = t == :interior ? ntuple(i -> 3, N) : ntuple(i -> 0.1, N) + T = Inti.default_density_eltype(pde) + c = rand(T) + u = (qnode) -> Inti.SingleLayerKernel(pde)(qnode, xs) * c + dudn = (qnode) -> Inti.AdjointDoubleLayerKernel(pde)(qnode, xs) * c + γ₀u = map(u, quad) + γ₁u = map(dudn, quad) + γ₀u_norm = norm(norm.(γ₀u, Inf), Inf) + γ₁u_norm = norm(norm.(γ₁u, Inf), Inf) + # single and double layer + G = Inti.SingleLayerKernel(pde) + S = Inti.IntegralOperator(G, quad) + Smat = Inti.assemble_matrix(S) + dG = Inti.DoubleLayerKernel(pde) + D = Inti.IntegralOperator(dG, quad) + Dmat = Inti.assemble_matrix(D) + e0 = norm(Smat * γ₁u - Dmat * γ₀u - σ * γ₀u, Inf) / γ₀u_norm + + green_multiplier = fill(-0.5, length(quad)) + # δS, δD = Inti.bdim_correction(pde, quad, quad, Smat, Dmat; green_multiplier) + + # qnodes = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) + # X = [q.coords[1] for q in qnodes]; Y = [q.coords[2] for q in qnodes] + # u = [q.normal[1] for q in qnodes]; v = [q.normal[2] for q in qnodes] + # fig, _, _ = scatter(X, Y) + # arrows!(X, Y, u, v, lengthscale=0.01) + # display(fig) + + δS, δD = Inti.local_bdim_correction(pde, quad, quad; green_multiplier, kneighbor=k) + Sdim = Smat + δS + Ddim = Dmat + δD + # Sdim, Ddim = Inti.single_double_layer(; + # pde, + # target = quad, + # source = quad, + # compression = (method = :none,), + # correction = (method = :ldim,), + # ) + e1 = norm(Sdim * γ₁u - Ddim * γ₀u - σ * γ₀u, Inf) / γ₀u_norm + # @show norm(e0, Inf) + @show norm(e1, Inf) + push!(err, e1) + end + plot!(fig, H, err;lw=2,marker=:o,label=k) +end +display(fig) \ No newline at end of file From e2833947d64c40bcbccc322174247b6fd8f697c9 Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Fri, 14 Jun 2024 08:59:15 +0800 Subject: [PATCH 09/56] function connected_components --- src/reference_interpolation.jl | 24 ++++++++++++++++++++++++ test/boundary_test.jl | 26 ++++++++++++++++++-------- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 398f031a..f00eb358 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -332,6 +332,30 @@ function boundarynd(els, msh) return res end +## +function _dfs!(comp, el, nei, els) + for el_nei in nei[el] + if el_nei in els + push!(comp, el_nei) + delete!(els, el_nei) + _dfs!(comp, el_nei, nei, els) + end + end +end + +function connected_components(els, nei) + components = Set{Tuple{DataType,Int}}[] + while !isempty(els) + el = pop!(els) + comp = Set{Tuple{DataType,Int}}() + push!(comp, el) + _dfs!(comp, el, nei, els) + push!(components, comp) + end + return components +end +## + #= Hardcode some basic elements. TODO: Eventually this could/should be automated. diff --git a/test/boundary_test.jl b/test/boundary_test.jl index cb60dd13..44b02ab7 100644 --- a/test/boundary_test.jl +++ b/test/boundary_test.jl @@ -19,8 +19,9 @@ Inti.clear_entities!() k = 2 Γ_msh = msh[Γ] Ω_msh = msh[Ω] -test_msh = Γ_msh -nei = Inti.topological_neighbors(test_msh, k) |> collect +test_msh = Ω_msh +nei = Inti.topological_neighbors(test_msh, 1) +E = first(Inti.element_types(test_msh)) function viz_neighbors(i, msh) k, v = nei[i] E, idx = k @@ -33,8 +34,15 @@ function viz_neighbors(i, msh) return display(fig) end +function viz_elements(els, msh) + Els = [Inti.elements(msh, E)[i] for (E, i) in els] + fig, _, _ = viz(Els) + viz!(msh; color = 0, showsegments = true,alpha=0.3) + display(fig) +end + function viz_elements_bords(Ei, els, bords, msh) - el = el = Inti.elements(msh, Ei[1])[Ei[2]] + el = Inti.elements(msh, Ei[1])[Ei[2]] fig, _, _ = viz(msh; color = 0, showsegments = true,alpha=0.3) viz!(el; color = 0, showsegments = true,alpha=0.5) for (E, i) in els @@ -47,12 +55,14 @@ end # el_in_set(el, set) = any(x->sort(x) == sort(el), set) -I = 10 -test_els = copy(nei[I][2]) -push!(test_els, nei[I][1]) +I, J = 1, 3 +test_els = union(copy(nei[(E,1)]), nei[(E,2)], nei[(E,3)], nei[(E,4)]) +viz_elements(test_els, test_msh) + +components = Inti.connected_components(test_els, nei) -BD = Inti.boundary1d(test_els, test_msh) -bords = [Inti.nodes(test_msh)[abs(i)] for i in BD] +BD = Inti.boundarynd(test_els, test_msh) +# bords = [Inti.nodes(test_msh)[abs(i)] for i in BD] bords = Inti.LagrangeElement[] for idxs in BD From 12a0bd694bb44dc3405f603f694859d8e114b14b Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Wed, 19 Jun 2024 21:24:43 +0800 Subject: [PATCH 10/56] function etype_to_near_elements and test --- src/quadrature.jl | 56 +++++++++++++++++++++++++++++++++++++++++++ test/ldim_test.jl | 2 +- test/nearlist_test.jl | 31 ++++++++++++++++++++++++ test/test_utils.jl | 24 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 test/nearlist_test.jl diff --git a/src/quadrature.jl b/src/quadrature.jl index 0e84ecfb..f1384b00 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -278,6 +278,62 @@ function _etype_to_nearest_points(X, Y::Quadrature, maxdist) return etype2nearlist end +""" + etype_to_nearest_elements(X,Y::Quadrature; tol) + +Return `Nl = [[el in Y.mesh && dist(x, el) ≤ tol] for x in X]` +""" +function etype_to_near_elements(X,Y::Quadrature; tol) + y = [coords(q) for q in Y] + tree = BallTree(y) + etype2nearlist = Dict{DataType,Vector{Set{Int}}}() + for (E, Q) in Y.etype2qtags + P, _ = size(Q) + etype2nearlist[E] = source2el = [Set{Int}() for _ in 1:length(X)] + for (l, x) in enumerate(X) + for q in inrange(tree, x, tol) + push!(source2el[l], ceil(Int, q/P)) + end + end + end + return etype2nearlist +end + +""" + etype_to_nearest_elements(Y::Quadrature; tol) + +Return `Nl = [[el_j in Y.mesh && dist(el_j, el_i) ≤ tol] for el_i in Y.mesh]` +""" +function etype_to_near_elements(Y::Quadrature; tol) + y = [coords(q) for q in Y] + tree = BallTree(y) + etype2nearlist = Dict{DataType,Vector{Set{Int}}}() + for (E, Q) in Y.etype2qtags + P, N = size(Q) + etype2nearlist[E] = el2el = [Set{Int}() for _ in 1:N] + for n in 1:N + for i in 1:P + for q in inrange(tree, coords(qnodes(Y)[Q[i,n]]), tol) + push!(el2el[n], ceil(Int, q/P)) + end + end + end + end + return etype2nearlist +end + +# function _geometric_center_circum_radius(Y::Quadrature, E, Q, P, N) +# C = [(sum(1:P) do i +# coords(qnodes(Y)[Q[i,n]]) .* weight(qnodes(Y)[Q[i,n]]) +# end) / +# (sum(1:P) do i +# weight(qnodes(Y)[Q[i,n]]) +# end) for n in 1:N] +# r = [maximum(i->norm(C[n]-nodes(mesh(Y))[i]), connectivity(mesh(Y), E)[:,n]) for n in 1:N] +# return C, r +# end + + """ quadrature_to_node_vals(Q::Quadrature, qvals::AbstractVector) diff --git a/test/ldim_test.jl b/test/ldim_test.jl index ad6dc3db..dcc72e94 100644 --- a/test/ldim_test.jl +++ b/test/ldim_test.jl @@ -10,7 +10,7 @@ Random.seed!(1) N = 2 t = :interior -pde = Inti.Stokes(; dim = N, μ=1.2) +pde = Inti.Laplace(; dim = N) K = 5:5 H = [0.2*2.0^(-i) for i in 0:0] diff --git a/test/nearlist_test.jl b/test/nearlist_test.jl new file mode 100644 index 00000000..844be3de --- /dev/null +++ b/test/nearlist_test.jl @@ -0,0 +1,31 @@ +using Test +using LinearAlgebra +using Inti +using Random +using Meshes +using GLMakie + +include("test_utils.jl") +Random.seed!(1) + +N = 2 +t = :interior +pde = Inti.Laplace(; dim = N) +h = 0.2 + +Inti.clear_entities!() +Ω, msh = gmsh_disks([([0.0,0.0],1.0,1.0), ([-2.1,0.0],1.0,1.0)];meshsize = h, order = 2) +Γ = Inti.external_boundary(Ω) +quad = Inti.Quadrature(msh[Γ]; qorder = 3) + +Nl = Inti.etype_to_near_elements(quad;tol=1) +fig, _, _ = viz(msh;showsegments = false,alpha=0.3) + +for (E, nl) in Nl + i = 1 + viz!(Inti.elements(msh[Γ], E)[i];color=:red) + for j in nl[i] + viz!(Inti.elements(msh[Γ], E)[j];color=:blue,alpha=0.3) + end +end +display(fig) \ No newline at end of file diff --git a/test/test_utils.jl b/test/test_utils.jl index 3d74eff5..ba5bcb63 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -23,6 +23,30 @@ function gmsh_disk(; center, rx, ry, meshsize, order = 1) return Ω, msh end +function gmsh_disks(disks;meshsize, order = 1) + msh = try + gmsh.initialize() + gmsh.option.setNumber("General.Verbosity", 2) + gmsh.model.add("disk") + # set max and min meshsize to meshsize + gmsh.option.setNumber("Mesh.MeshSizeMax", meshsize) + gmsh.option.setNumber("Mesh.MeshSizeMin", meshsize) + for (center, rx, ry) in disks + gmsh.model.occ.addDisk(center[1], center[2], 0, rx, ry) + end + gmsh.model.occ.synchronize() + gmsh.model.mesh.generate(2) + gmsh.model.mesh.setOrder(order) + Inti.import_mesh(; dim = 2) + finally + gmsh.finalize() + end + Ω = Inti.Domain(Inti.entities(msh)) do e + return Inti.geometric_dimension(e) == 2 + end + return Ω, msh +end + function gmsh_ball(; center, radius, meshsize) msh = try gmsh.initialize() From 328d717bab2c1c9d998b8c2e19ecbc0e54cf6aa6 Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Thu, 20 Jun 2024 16:50:51 +0800 Subject: [PATCH 11/56] etype_to_near_elements correct version --- src/quadrature.jl | 22 +++++++++++++++++++--- test/nearlist_test.jl | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index f1384b00..04d2209e 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -288,11 +288,19 @@ function etype_to_near_elements(X,Y::Quadrature; tol) tree = BallTree(y) etype2nearlist = Dict{DataType,Vector{Set{Int}}}() for (E, Q) in Y.etype2qtags - P, _ = size(Q) + P, N = size(Q) etype2nearlist[E] = source2el = [Set{Int}() for _ in 1:length(X)] + quad2source = [Set{Int}() for _ in 1:length(y)] for (l, x) in enumerate(X) for q in inrange(tree, x, tol) - push!(source2el[l], ceil(Int, q/P)) + push!(quad2source[q], l) + end + end + for n in 1:N + for i in 1:P + for l in quad2source[Q[i,n]] + push!(source2el[l], n) + end end end end @@ -311,13 +319,21 @@ function etype_to_near_elements(Y::Quadrature; tol) for (E, Q) in Y.etype2qtags P, N = size(Q) etype2nearlist[E] = el2el = [Set{Int}() for _ in 1:N] + quad2el = [Set{Int}() for _ in 1:length(y)] for n in 1:N for i in 1:P for q in inrange(tree, coords(qnodes(Y)[Q[i,n]]), tol) - push!(el2el[n], ceil(Int, q/P)) + push!(quad2el[q], n) end end end + for n in 1:N + for i in 1:P + for m in quad2el[Q[i,n]] + push!(el2el[n], m) + end + end + end end return etype2nearlist end diff --git a/test/nearlist_test.jl b/test/nearlist_test.jl index 844be3de..0eced9c7 100644 --- a/test/nearlist_test.jl +++ b/test/nearlist_test.jl @@ -18,7 +18,7 @@ Inti.clear_entities!() Γ = Inti.external_boundary(Ω) quad = Inti.Quadrature(msh[Γ]; qorder = 3) -Nl = Inti.etype_to_near_elements(quad;tol=1) +Nl = Inti.etype_to_near_elements(quad;tol=0.01) fig, _, _ = viz(msh;showsegments = false,alpha=0.3) for (E, nl) in Nl From 56a6a5954b152cbcad640f9648452eadafdafc46 Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Mon, 24 Jun 2024 18:36:39 +0800 Subject: [PATCH 12/56] (etype_to_)near_elements takes different types of elements into account --- src/quadrature.jl | 28 +++++++++++++++------------- test/nearlist_test.jl | 20 +++++++++++++------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index 04d2209e..6ff97214 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -279,7 +279,7 @@ function _etype_to_nearest_points(X, Y::Quadrature, maxdist) end """ - etype_to_nearest_elements(X,Y::Quadrature; tol) + etype_to_near_elements(X,Y::Quadrature; tol) Return `Nl = [[el in Y.mesh && dist(x, el) ≤ tol] for x in X]` """ @@ -308,34 +308,36 @@ function etype_to_near_elements(X,Y::Quadrature; tol) end """ - etype_to_nearest_elements(Y::Quadrature; tol) + etype_to_near_elements(Y::Quadrature; tol) Return `Nl = [[el_j in Y.mesh && dist(el_j, el_i) ≤ tol] for el_i in Y.mesh]` """ -function etype_to_near_elements(Y::Quadrature; tol) +function near_elements(Y::Quadrature; tol) y = [coords(q) for q in Y] tree = BallTree(y) - etype2nearlist = Dict{DataType,Vector{Set{Int}}}() + el2el = Dict{Tuple{DataType,Int},Set{Tuple{DataType,Int}}}() + quad2el = [Set{Tuple{DataType,Int}}() for _ in 1:length(y)] + # for each element, loop over its qnodes, find q∈y close to one of its qnodes, add the element to quad2el[q] + # quad2el[q] is the set of elements whose qnodes are close to q for (E, Q) in Y.etype2qtags P, N = size(Q) - etype2nearlist[E] = el2el = [Set{Int}() for _ in 1:N] - quad2el = [Set{Int}() for _ in 1:length(y)] for n in 1:N for i in 1:P for q in inrange(tree, coords(qnodes(Y)[Q[i,n]]), tol) - push!(quad2el[q], n) + push!(quad2el[q], (E, n)) end end end + end + # for each element, the set of elements close to it is the + # union of the sets of elements close to its qnodes + for (E, Q) in Y.etype2qtags + P, N = size(Q) for n in 1:N - for i in 1:P - for m in quad2el[Q[i,n]] - push!(el2el[n], m) - end - end + el2el[(E, n)] = union([quad2el[Q[i,n]] for i in 1:P]...) end end - return etype2nearlist + return el2el end # function _geometric_center_circum_radius(Y::Quadrature, E, Q, P, N) diff --git a/test/nearlist_test.jl b/test/nearlist_test.jl index 0eced9c7..b987533c 100644 --- a/test/nearlist_test.jl +++ b/test/nearlist_test.jl @@ -18,14 +18,20 @@ Inti.clear_entities!() Γ = Inti.external_boundary(Ω) quad = Inti.Quadrature(msh[Γ]; qorder = 3) -Nl = Inti.etype_to_near_elements(quad;tol=0.01) +Nl = Inti.near_elements(quad;tol=0.2) fig, _, _ = viz(msh;showsegments = false,alpha=0.3) -for (E, nl) in Nl - i = 1 - viz!(Inti.elements(msh[Γ], E)[i];color=:red) - for j in nl[i] - viz!(Inti.elements(msh[Γ], E)[j];color=:blue,alpha=0.3) - end +E = first(keys(Nl))[1]; i = 1 +viz!(Inti.elements(msh[Γ], E)[i];color=:red) +for (E_, j) in Nl[(E, i)] + viz!(Inti.elements(msh[Γ], E_)[j];color=:blue,alpha=0.3) end + +# for (E, nl) in Nl +# i = 1 +# viz!(Inti.elements(msh[Γ], E)[i];color=:red) +# for j in nl[i] +# viz!(Inti.elements(msh[Γ], E)[j];color=:blue,alpha=0.3) +# end +# end display(fig) \ No newline at end of file From e609f932d399923e8b76c13aa8c981436a110c8b Mon Sep 17 00:00:00 2001 From: Dongchen He Date: Mon, 24 Jun 2024 18:47:17 +0800 Subject: [PATCH 13/56] near_components --- src/quadrature.jl | 13 ++++++++++++- test/nearlist_test.jl | 7 ++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index 6ff97214..94268e6e 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -308,7 +308,7 @@ function etype_to_near_elements(X,Y::Quadrature; tol) end """ - etype_to_near_elements(Y::Quadrature; tol) + near_elements(Y::Quadrature; tol) Return `Nl = [[el_j in Y.mesh && dist(el_j, el_i) ≤ tol] for el_i in Y.mesh]` """ @@ -340,6 +340,17 @@ function near_elements(Y::Quadrature; tol) return el2el end +""" + near_components(Y::Quadrature; tol) + +Calculate the connected components of each near_elements +""" +function near_components(Y::Quadrature; tol) + topo_neighbor = topological_neighbors(Y.mesh) + dist_neighbor = near_elements(Y; tol) + return Dict(E => connected_components(nl, topo_neighbor) for (E, nl) in dist_neighbor) +end + # function _geometric_center_circum_radius(Y::Quadrature, E, Q, P, N) # C = [(sum(1:P) do i # coords(qnodes(Y)[Q[i,n]]) .* weight(qnodes(Y)[Q[i,n]]) diff --git a/test/nearlist_test.jl b/test/nearlist_test.jl index b987533c..435f3e09 100644 --- a/test/nearlist_test.jl +++ b/test/nearlist_test.jl @@ -18,12 +18,13 @@ Inti.clear_entities!() Γ = Inti.external_boundary(Ω) quad = Inti.Quadrature(msh[Γ]; qorder = 3) -Nl = Inti.near_elements(quad;tol=0.2) +# Nl = Inti.near_elements(quad;tol=0.2) +Ncl = Inti.near_components(quad;tol=0.2) fig, _, _ = viz(msh;showsegments = false,alpha=0.3) -E = first(keys(Nl))[1]; i = 1 +E = first(keys(Ncl))[1]; i = 1 viz!(Inti.elements(msh[Γ], E)[i];color=:red) -for (E_, j) in Nl[(E, i)] +for (E_, j) in Ncl[(E, i)][2] viz!(Inti.elements(msh[Γ], E_)[j];color=:blue,alpha=0.3) end From d9ea0129d61f4ea0b6fd8ff02140005982c20e6c Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 17 Aug 2024 12:58:26 -0500 Subject: [PATCH 14/56] Initial Local VDIM work. --- Project.toml | 1 + ext/IntiMeshesExt.jl | 32 ++++ src/api.jl | 16 ++ src/mesh.jl | 4 + src/quadrature.jl | 38 +++++ src/utils.jl | 7 +- src/vdim.jl | 170 ++++++++++++++++++++++ test/Project.toml | 1 + test/boundary_test.jl | 57 +++++--- test/convergence/vdim_potential_script.jl | 64 ++++---- test/lvdim_test.jl | 124 ++++++++++++++++ 11 files changed, 462 insertions(+), 52 deletions(-) create mode 100644 test/lvdim_test.jl diff --git a/Project.toml b/Project.toml index 56a1c10f..11c6a49d 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" [weakdeps] FMM2D = "2d63477d-9690-4b75-bcc1-c3461d43fecc" diff --git a/ext/IntiMeshesExt.jl b/ext/IntiMeshesExt.jl index 16440ca8..6688eeff 100644 --- a/ext/IntiMeshesExt.jl +++ b/ext/IntiMeshesExt.jl @@ -1,6 +1,7 @@ module IntiMeshesExt using Meshes +import GLMakie as Mke import Inti using StaticArrays @@ -88,4 +89,35 @@ function Meshes.viz!(msh::Inti.AbstractMesh, args...; kwargs...) return viz!(to_meshes(msh), args...; kwargs...) end + +function Inti.viz_elements(els, msh) + E = first(Inti.element_types(msh)) + Els = [Inti.elements(msh, E)[i] for (E, i) in els] + fig, _, _ = viz(Els) + viz!(msh; color = 0, showsegments = true,alpha=0.3) + display(fig) +end + + +function Inti.viz_elements_bords(Ei, els, ell, bords, msh; quad = nothing) + E = first(Inti.element_types(msh)) + #ell = collect(Ei[(E, 1)])[1] + el = Inti.elements(msh, ell[1])[ell[2]] + fig, _, _ = viz(msh; color = 0, showsegments = true,alpha=0.3) + viz!(el; color = 0, showsegments = true,alpha=0.5) + for (E, i) in els + el = Inti.elements(msh, E)[i] + viz!(el; showsegments = true, alpha=0.7) + end + viz!(bords;color=4,showsegments = false,segmentsize=5,segmentcolor=4) + if !isnothing(quad) + xs = [qnode.coords[1] for qnode in quad.qnodes] + ys = [qnode.coords[2] for qnode in quad.qnodes] + nx = [Inti.normal(qnode)[1] for qnode in quad.qnodes] + ny = [Inti.normal(qnode)[2] for qnode in quad.qnodes] + Mke.arrows!(xs, ys, nx, ny; lengthscale = 0.15) + end + display(fig) +end + end # module diff --git a/src/api.jl b/src/api.jl index 36a4e2bc..0ab3a774 100644 --- a/src/api.jl +++ b/src/api.jl @@ -286,6 +286,22 @@ function volume_potential(; pde, target, source::Quadrature, compression, correc correction.maxdist, interpolation_order, ) + elseif correction.method == :ldim + loc = target === source ? :inside : correction.target_location + μ = _green_multiplier(loc) + green_multiplier = fill(μ, length(target)) + shift = Val(false) + δV = local_vdim_correction( + pde, + eltype(V), + target, + source, + correction.mesh; + green_multiplier, + correction.maxdist, + correction.interpolation_order, + shift, + ) else error("Unknown correction method. Available options: $CORRECTION_METHODS") end diff --git a/src/mesh.jl b/src/mesh.jl index d71460e4..b9396b53 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -630,3 +630,7 @@ function topological_neighbors(msh::AbstractMesh, k = 1) end return k_neighbors end + +function viz_elements(args...; kwargs...) end + +function viz_elements_bords(args...; kwargs...) end diff --git a/src/quadrature.jl b/src/quadrature.jl index 94268e6e..b7669887 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -121,6 +121,44 @@ function Quadrature(msh::AbstractMesh{N,T}, etype2qrule::Dict) where {N,T} return quad end +# Quadrature constructor for list of volume elements for local vdim +function Quadrature(msh::AbstractMesh{N,T}, elementlist::AbstractVector{E}; qorder) where {N,T,E} + #FIXME Ideally we would like to use Gauss quadrature on the local region (fewer points for same order of accuracy) but to do so we need to subtract away the contribution from the global quadrature rule on the local region (typically, these are VR nodes). So, for now, using the global quadrature rule on the local region gives better accuracy since it cancels these (inaccurate) computations out. + #etype2qrule = + # Dict(E => _qrule_for_reference_shape(domain(E), qorder)) + if domain(E) isa Inti.ReferenceSimplex + Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) + etype2qrule = Dict(E => Q) + else + etype2qrule = + Dict(E => _qrule_for_reference_shape(domain(E), qorder)) + end + + # initialize mesh with empty fields + quad = Quadrature{N,T}( + msh, + etype2qrule, + QuadratureNode{N,T}[], + Dict{DataType,Matrix{Int}}(), + ) + # loop element types and generate quadrature for each + qrule = etype2qrule[E] + _build_quadrature!(quad, elementlist, qrule) + + # check for entities with negative orientation and flip normal vectors if + # present + for ent in entities(msh) + if (sign(tag(ent)) < 0) && (N - geometric_dimension(ent) == 1) + @debug "Flipping normals of $ent" + tags = dom2qtags(quad, Domain(ent)) + for i in tags + quad[i] = flip_normal(quad[i]) + end + end + end + return quad +end + function Quadrature(msh::AbstractMesh; qorder) etype2qrule = Dict(E => _qrule_for_reference_shape(domain(E), qorder) for E in element_types(msh)) diff --git a/src/utils.jl b/src/utils.jl index e2c99a4e..5f20bd45 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -235,12 +235,12 @@ function _normalize_compression(compression, target, source) end function _normalize_correction(correction, target, source) - methods = (:dim, :adaptive, :none) + methods = (:dim, :ldim, :adaptive, :none) # check that method is valid correction.method ∈ methods || error("Unknown correction.method $(correction.method). Available options: $methods") # set default values if absent - if correction.method == :dim + if correction.method == :dim || correction.method == :ldim haskey(correction, :target_location) && target === source && correction.target_location != :on && @@ -252,6 +252,9 @@ function _normalize_correction(correction, target, source) haskey(correction, :maxdist) || target === source || @warn("missing maxdist field in correction: setting to Inf") + if correction.method == :ldim + haskey(correction, :mesh) || error("missing mesh information needed for local dim") + end correction = merge( (maxdist = Inf, interpolation_order = nothing, center = nothing), correction, diff --git a/src/vdim.jl b/src/vdim.jl index 9269ab4b..8cc0452b 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -27,6 +27,9 @@ See [anderson2024fast](@cite) for more details on the method. - `shift`: a boolean indicating whether the basis functions should be shifted and rescaled to each element. """ + +import GLMakie as Mke + function vdim_correction( pde, target, @@ -128,6 +131,104 @@ function vdim_correction( return δV end +function local_vdim_correction( + pde, + Eltype, + target, + source::Quadrature, + mesh::AbstractMesh; + green_multiplier::Vector{<:Real}, + interpolation_order = nothing, + maxdist = Inf, + center = nothing, + shift::Val{SHIFT} = Val(false), +) where {SHIFT} + # variables for debugging the condition properties of the method + vander_cond = vander_norm = rhs_norm = res_norm = shift_norm = -Inf + # figure out if we are dealing with a scalar or vector PDE + m, n = length(target), length(source) + N = ambient_dimension(pde) + @assert ambient_dimension(source) == N "vdim only works for volume potentials" + m, n = length(target), length(source) + # a reasonable interpolation_order if not provided + isnothing(interpolation_order) && + (interpolation_order = maximum(order, values(source.etype2qrule))) + # by default basis centered at origin + center = isnothing(center) ? zero(SVector{N,Float64}) : center + p, P, γ₁P, multiindices = polynomial_solutions_vdim(pde, interpolation_order, center) + dict_near = etype_to_nearest_points(target, source; maxdist) + # compute sparse correction + Is = Int[] + Js = Int[] + Vs = Eltype[] + for (E, qtags) in source.etype2qtags + els = elements(source.mesh, E) + near_list = dict_near[E] + nq, ne = size(qtags) + @assert length(near_list) == ne + topo_neighs = 1 + neighbors = Inti.topological_neighbors(mesh, topo_neighs) + for n in 1:ne + # indices of nodes in element `n` + isempty(near_list[n]) && continue + R = _local_vdim_auxiliary_quantities( + pde, + mesh, + neighbors, + n, + p, + P, + γ₁P, + target[near_list[n]], + green_multiplier; + ) + jglob = @view qtags[:, n] + # compute translation and scaling + c, r = translation_and_scaling(els[n]) + if SHIFT + iszero(center) || error("SHIFT is not implemented for non-zero center") + L̃ = [f((q.coords - c) / r) for f in p, q in view(source, jglob)] + S = change_of_basis(multiindices, p, c, r) + F = lu(L̃) + @debug (vander_cond = max(vander_cond, cond(L̃))) maxlog = 0 + @debug (shift_norm = max(shift_norm, norm(S))) maxlog = 0 + @debug (vander_norm = max(vander_norm, norm(L̃))) maxlog = 0 + else + L = [f(q.coords) for f in p, q in view(source, jglob)] + F = lu(L) + @debug (vander_cond = max(vander_cond, cond(L))) maxlog = 0 + @debug (shift_norm = max(shift_norm, 1)) maxlog = 0 + @debug (vander_norm = max(vander_norm, norm(L))) maxlog = 0 + end + # correct each target near the current element + for i in 1:length(near_list[n]) + b = @views R[i, :] + wei = SHIFT ? F \ (S * b) : F \ b # weights for the current element and target i + rhs_norm = max(rhs_norm, norm(b)) + res_norm = if SHIFT + max(res_norm, norm(L̃ * wei - S * b)) + else + max(res_norm, norm(L * wei - b)) + end + for k in 1:nq + push!(Is, near_list[n][i]) + push!(Js, jglob[k]) + push!(Vs, wei[k]) + end + end + end + end + @debug """Condition properties of vdim correction: + |-- max interp. matrix condition: $vander_cond + |-- max norm of source term: $rhs_norm + |-- max residual error: $res_norm + |-- max interp. matrix norm : $vander_norm + |-- max shift norm : $shift_norm + """ + δV = sparse(Is, Js, Vs, m, n) + return δV +end + function change_of_basis(multiindices, p, c, r) nbasis = length(multiindices) P = zeros(nbasis, nbasis) @@ -242,6 +343,75 @@ function translation_and_scaling(el::LagrangeTetrahedron) return center, R end +function _local_vdim_auxiliary_quantities( + pde, + mesh, + neighbors, + el, + p, + P, + γ₁P, + X, + μ; +) + # construct the local region + Etype = first(Inti.element_types(mesh)) + el_neighs = copy(neighbors[(Etype,el)]) + + loc_bdry = Inti.boundarynd(el_neighs, mesh) + # TODO handle curved boundary of Γ?? + bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] + for idxs in loc_bdry + vtxs = Inti.nodes(mesh)[idxs] + bord = Inti.LagrangeLine(vtxs...) + push!(bords, bord) + end + + # build O(h) volume neighbors + els_idxs = [i[2] for i in collect(el_neighs)] + els_list = mesh.etype2els[Etype][els_idxs] + Yvol = Inti.Quadrature(mesh, els_list; qorder = 7) + # TODO Need DIM corrections when X is close to Γ + Ybdry = Inti.Quadrature(mesh, bords; qorder = 8) + # TODO handle derivative case + G = SingleLayerKernel(pde) + dG = DoubleLayerKernel(pde) + Sop = IntegralOperator(G, X, Ybdry) + Dop = IntegralOperator(dG, X, Ybdry) + Vop = IntegralOperator(G, X, Yvol) + Smat = assemble_matrix(Sop) + Dmat = assemble_matrix(Dop) + Vmat = assemble_matrix(Vop) + #sleep(2) + #Inti.viz_elements_bords(neighbors, el_neighs, (Etype, el), bords, mesh; quad = Ybdry) + #qnodesx = [qnode.coords[1] for qnode in Yvol] + #qnodesy = [qnode.coords[2] for qnode in Yvol] + #Mke.scatter!(qnodesx, qnodesy) + #trgsx = [qnode.coords[1] for qnode in X] + #trgsy = [qnode.coords[2] for qnode in X] + #Mke.scatter!(trgsx, trgsy) + + num_basis = length(p) + num_targets = length(X) + b = [f(q) for q in Yvol, f in p] + γ₀B = [f(q) for q in Ybdry, f in P] + γ₁B = [f(q) for q in Ybdry, f in γ₁P] + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end + Θ = zeros(eltype(Vop), num_targets, num_basis) + # Compute Θ <-- S * γ₁B - D * γ₀B - V * b + σ * B(x) using in-place matvec + for n in 1:num_basis + @views mul!(Θ[:, n], Smat, γ₁B[:, n]) + @views mul!(Θ[:, n], Dmat, γ₀B[:, n], -1, 1) + @views mul!(Θ[:, n], Vmat, b[:, n], -1, 1) + for i in 1:num_targets + Θ[i, n] += μ[i] * P[n](X[i]) + end + end + return Θ +end + function _vdim_auxiliary_quantities( p, P, diff --git a/test/Project.toml b/test/Project.toml index 2d3ec276..dfd08431 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -5,6 +5,7 @@ FMM3D = "1e13804c-f9b7-11ea-0ef0-29f3b1745df8" GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" HMatrices = "8646bddf-ab1c-4fa7-9c51-ba187d647618" +Inti = "fb74042b-437e-4c5b-88cf-d4e2beb394d5" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/test/boundary_test.jl b/test/boundary_test.jl index 44b02ab7..f63cc3e5 100644 --- a/test/boundary_test.jl +++ b/test/boundary_test.jl @@ -3,7 +3,7 @@ using LinearAlgebra using Inti using Random using Meshes -using GLMakie +import GLMakie include("test_utils.jl") Random.seed!(1) @@ -34,33 +34,38 @@ function viz_neighbors(i, msh) return display(fig) end -function viz_elements(els, msh) - Els = [Inti.elements(msh, E)[i] for (E, i) in els] - fig, _, _ = viz(Els) - viz!(msh; color = 0, showsegments = true,alpha=0.3) - display(fig) -end - -function viz_elements_bords(Ei, els, bords, msh) - el = Inti.elements(msh, Ei[1])[Ei[2]] - fig, _, _ = viz(msh; color = 0, showsegments = true,alpha=0.3) - viz!(el; color = 0, showsegments = true,alpha=0.5) - for (E, i) in els - el = Inti.elements(msh, E)[i] - viz!(el; showsegments = true, alpha=0.7) - end - viz!(bords;color=4,showsegments = false,segmentsize=5,segmentcolor=4) - display(fig) -end +#function viz_elements(els, msh) +# Els = [Inti.elements(msh, E)[i] for (E, i) in els] +# fig, _, _ = viz(Els) +# viz!(msh; color = 0, showsegments = true,alpha=0.3) +# display(fig) +#end +# +#function viz_elements_bords(Ei, els, bords, msh) +# ell = collect(Ei[(E, 1)])[1] +# el = Inti.elements(msh, ell[1])[ell[2]] +# fig, _, _ = viz(msh; color = 0, showsegments = true,alpha=0.3) +# viz!(el; color = 0, showsegments = true,alpha=0.5) +# for (E, i) in els +# el = Inti.elements(msh, E)[i] +# viz!(el; showsegments = true, alpha=0.7) +# end +# viz!(bords;color=4,showsegments = false,segmentsize=5,segmentcolor=4) +# display(fig) +#end # el_in_set(el, set) = any(x->sort(x) == sort(el), set) -I, J = 1, 3 -test_els = union(copy(nei[(E,1)]), nei[(E,2)], nei[(E,3)], nei[(E,4)]) -viz_elements(test_els, test_msh) +I = 3 +test_els = union(copy(nei[(E,I)])) +els = Inti.elements(test_msh, E) +#test_els = union(copy(nei[(E,1)]), nei[(E,2)]) +#test_els = union(copy(nei[(E,1)]), nei[(E,2)], nei[(E,3)], nei[(E,4)]) +Inti.viz_elements(test_els, test_msh) components = Inti.connected_components(test_els, nei) +test_els = copy(nei[(E,I)]) BD = Inti.boundarynd(test_els, test_msh) # bords = [Inti.nodes(test_msh)[abs(i)] for i in BD] @@ -71,4 +76,10 @@ for idxs in BD push!(bords, bord) end -viz_elements_bords(nei[I][1], test_els, bords, test_msh) +els_idxs = [i[2] for i in collect(test_els)] +E = collect(test_els)[1][1] +els_list = test_msh.etype2els[E][els_idxs] +newquad = Inti.Quadrature(test_msh, els_list; qorder = 2) + +Inti.viz_elements_bords(nei, test_els, (E, I), bords, test_msh) +#Inti.viz_elements_bords(nei, test_els, collect(test_els)[3], bords, test_msh) diff --git a/test/convergence/vdim_potential_script.jl b/test/convergence/vdim_potential_script.jl index 496b9af1..f3e1ff6c 100644 --- a/test/convergence/vdim_potential_script.jl +++ b/test/convergence/vdim_potential_script.jl @@ -6,43 +6,53 @@ using Gmsh using LinearAlgebra using HMatrices using FMMLIB2D -using CairoMakie - -function domain_and_mesh(; meshsize, meshorder = 1) - Inti.clear_entities!() - gmsh.initialize() - gmsh.option.setNumber("Mesh.MeshSizeMax", meshsize) - gmsh.option.setNumber("Mesh.MeshSizeMin", meshsize) - gmsh.model.occ.addDisk(0, 0, 0, 1, 1) - gmsh.model.occ.synchronize() - gmsh.model.mesh.generate(2) - gmsh.model.mesh.setOrder(meshorder) - Ω, msh = Inti.import_mesh(; dim = 2) - gmsh.finalize() - return Ω, msh -end +using GLMakie -meshsize = 0.01 +meshsize = 0.1 interpolation_order = 4 VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(interpolation_order) bdry_qorder = 2 * VR_qorder -tmesh = @elapsed begin - Ω, msh = domain_and_mesh(; meshsize) +function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) + try + gmsh.initialize() + gmsh.option.setNumber("General.Terminal", 0) + gmsh.model.add("circle-mesh") + gmsh.option.setNumber("Mesh.MeshSizeMax", meshsize) + gmsh.option.setNumber("Mesh.MeshSizeMin", meshsize) + gmsh.model.occ.addDisk(center[1], center[2], 0, paxis[1], paxis[2]) + gmsh.model.occ.synchronize() + gmsh.model.mesh.generate(2) + gmsh.model.mesh.setOrder(order) + gmsh.write(name) + finally + gmsh.finalize() + end end -@info "Mesh generation time: $tmesh" -Γ = Inti.external_boundary(Ω) -Ωₕ = view(msh, Ω) -Γₕ = view(msh, Γ) +name = joinpath(@__DIR__, "disk.msh") +gmsh_disk(; meshsize, order = 2, name) + +Inti.clear_entities!() # empty the entity cache +msh = Inti.import_mesh(name; dim = 2) +Ω = Inti.Domain(e -> Inti.geometric_dimension(e) == 2, Inti.entities(msh)) +Γ = Inti.boundary(Ω) + + +Ωₕ = msh[Ω] +Γₕ = msh[Γ] +Ωₕ_sub = view(msh, Ω) +Γₕ_sub = view(msh, Γ) tquad = @elapsed begin # Use VDIM with the Vioreanu-Rokhlin quadrature rule for Ωₕ Q = Inti.VioreanuRokhlin(; domain = :triangle, order = VR_qorder) dict = Dict(E => Q for E in Inti.element_types(Ωₕ)) Ωₕ_quad = Inti.Quadrature(Ωₕ, dict) + Ωₕ_sub_quad = Inti.Quadrature(Ωₕ_sub, dict) # Ωₕ_quad = Inti.Quadrature(Ωₕ; qorder = qorders[1]) Γₕ_quad = Inti.Quadrature(Γₕ; qorder = bdry_qorder) + Γₕ_sub_quad = Inti.Quadrature(Γₕ_sub; qorder = bdry_qorder) end @info "Quadrature generation time: $tquad" @@ -66,10 +76,10 @@ pde = k == 0 ? Inti.Laplace(; dim = 2) : Inti.Helmholtz(; dim = 2, k) tbnd = @elapsed begin S_b2d, D_b2d = Inti.single_double_layer(; pde, - target = Ωₕ_quad, - source = Γₕ_quad, + target = Ωₕ_sub_quad, + source = Γₕ_sub_quad, compression = (method = :fmm, tol = 1e-14), - correction = (method = :dim, maxdist = 5 * meshsize), + correction = (method = :dim, maxdist = 5 * meshsize, target_location = :on), ) end @info "Boundary operators time: $tbnd" @@ -78,8 +88,8 @@ end tvol = @elapsed begin V_d2d = Inti.volume_potential(; pde, - target = Ωₕ_quad, - source = Ωₕ_quad, + target = Ωₕ_sub_quad, + source = Ωₕ_sub_quad, compression = (method = :fmm, tol = 1e-14), correction = ( method = :dim, diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl new file mode 100644 index 00000000..4f983ce5 --- /dev/null +++ b/test/lvdim_test.jl @@ -0,0 +1,124 @@ +# # Testing local vdim + +using Inti +using StaticArrays +using Gmsh +using LinearAlgebra +using HMatrices +using FMMLIB2D +using GLMakie +using Meshes + +meshsize = 0.01 +interpolation_order = 4 +VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(interpolation_order) +bdry_qorder = 2 * VR_qorder + +function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) + try + gmsh.initialize() + gmsh.option.setNumber("General.Terminal", 0) + gmsh.model.add("circle-mesh") + gmsh.option.setNumber("Mesh.MeshSizeMax", meshsize) + gmsh.option.setNumber("Mesh.MeshSizeMin", meshsize) + gmsh.model.occ.addDisk(center[1], center[2], 0, paxis[1], paxis[2]) + gmsh.model.occ.synchronize() + gmsh.model.mesh.generate(2) + gmsh.model.mesh.setOrder(order) + gmsh.write(name) + finally + gmsh.finalize() + end +end + +name = joinpath(@__DIR__, "disk.msh") +gmsh_disk(; meshsize, order = 2, name) + +Inti.clear_entities!() # empty the entity cache +msh = Inti.import_mesh(name; dim = 2) +Ω = Inti.Domain(e -> Inti.geometric_dimension(e) == 2, Inti.entities(msh)) +Γ = Inti.boundary(Ω) + + +Ωₕ = msh[Ω] +Γₕ = msh[Γ] +Ωₕ_Sub = view(msh, Ω) +Γₕ_Sub = view(msh, Γ) + +tquad = @elapsed begin + # Use VDIM with the Vioreanu-Rokhlin quadrature rule for Ωₕ + Q = Inti.VioreanuRokhlin(; domain = :triangle, order = VR_qorder) + dict = Dict(E => Q for E in Inti.element_types(Ωₕ)) + Ωₕ_quad = Inti.Quadrature(Ωₕ, dict) + Ωₕ_Sub_quad = Inti.Quadrature(Ωₕ_Sub, dict) + # Ωₕ_quad = Inti.Quadrature(Ωₕ; qorder = qorders[1]) + Γₕ_quad = Inti.Quadrature(Γₕ; qorder = bdry_qorder) + Γₕ_Sub_quad = Inti.Quadrature(Γₕ_Sub; qorder = bdry_qorder) +end +@info "Quadrature generation time: $tquad" + +k0 = π +k = 0 +θ = (cos(π / 3), sin(π / 3)) +#u = (x) -> exp(im * k0 * dot(x, θ)) +#du = (x,n) -> im * k0 * dot(θ, n) * exp(im * k0 * dot(x, θ)) +u = (x) -> cos(k0 * dot(x, θ)) +du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) +f = (x) -> (k^2 - k0^2) * u(x) + +u_d = map(q -> u(q.coords), Ωₕ_quad) +u_b = map(q -> u(q.coords), Γₕ_quad) +du_b = map(q -> du(q.coords, q.normal), Γₕ_quad) +f_d = map(q -> f(q.coords), Ωₕ_quad) + +pde = k == 0 ? Inti.Laplace(; dim = 2) : Inti.Helmholtz(; dim = 2, k) + +## Boundary operators +tbnd = @elapsed begin +S_b2d, D_b2d = Inti.single_double_layer(; + pde, + target = Ωₕ_quad, + source = Γₕ_quad, + compression = (method = :fmm, tol = 1e-14), + correction = (method = :dim, maxdist = 5 * meshsize, target_location = :inside), +) +end +@info "Boundary operators time: $tbnd" + +## Volume potentials +#tvol = @elapsed begin +V_d2d = Inti.volume_potential(; + pde, + target = Ωₕ_quad, + source = Ωₕ_quad, + compression = (method = :fmm, tol = 1e-14), + correction = ( + method = :ldim, + mesh = Ωₕ, + interpolation_order, + maxdist = 5 * meshsize, + ), +) +Vglob_d2d = Inti.volume_potential(; + pde, + target = Ωₕ_Sub_quad, + source = Ωₕ_Sub_quad, + compression = (method = :fmm, tol = 1e-14), + correction = ( + method = :dim, + mesh = Ωₕ_Sub, + interpolation_order, + maxdist = 5 * meshsize, + ), +) +#end +#@info "Volume potential time: $tvol" + +vref = -u_d - D_b2d * u_b + S_b2d * du_b +vapprox = V_d2d * f_d +vglobapprox = Vglob_d2d * f_d +er = vref - vapprox + +ndofs = length(er) + +@show ndofs, meshsize, norm(er, Inf) From e999092de1222d6f895d6d16cdb3a637139f227d Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 17 Aug 2024 19:59:11 -0500 Subject: [PATCH 15/56] Working Local VDIM --- src/api.jl | 3 ++- src/vdim.jl | 41 ++++++++++++++++++++++++++++++++++------- test/lvdim_test.jl | 1 + 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/api.jl b/src/api.jl index 0ab3a774..51c690c6 100644 --- a/src/api.jl +++ b/src/api.jl @@ -296,7 +296,8 @@ function volume_potential(; pde, target, source::Quadrature, compression, correc eltype(V), target, source, - correction.mesh; + correction.mesh, + correction.bdry_nodes; green_multiplier, correction.maxdist, correction.interpolation_order, diff --git a/src/vdim.jl b/src/vdim.jl index 8cc0452b..f483de8a 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -136,7 +136,8 @@ function local_vdim_correction( Eltype, target, source::Quadrature, - mesh::AbstractMesh; + mesh::AbstractMesh, + bdry_nodes; green_multiplier::Vector{<:Real}, interpolation_order = nothing, maxdist = Inf, @@ -157,6 +158,7 @@ function local_vdim_correction( center = isnothing(center) ? zero(SVector{N,Float64}) : center p, P, γ₁P, multiindices = polynomial_solutions_vdim(pde, interpolation_order, center) dict_near = etype_to_nearest_points(target, source; maxdist) + bdry_kdtree = KDTree(bdry_nodes) # compute sparse correction Is = Int[] Js = Int[] @@ -180,7 +182,8 @@ function local_vdim_correction( P, γ₁P, target[near_list[n]], - green_multiplier; + green_multiplier, + bdry_kdtree; ) jglob = @view qtags[:, n] # compute translation and scaling @@ -352,7 +355,8 @@ function _local_vdim_auxiliary_quantities( P, γ₁P, X, - μ; + μ, + bdry_kdtree; ) # construct the local region Etype = first(Inti.element_types(mesh)) @@ -367,6 +371,13 @@ function _local_vdim_auxiliary_quantities( push!(bords, bord) end + # Check if we need to do near-singular layer potential evaluation + vertices = mesh.etype2els[Etype][el].vals[1:3] + diam = max(norm(vertices[1] - vertices[2]), + norm(vertices[2] - vertices[3]), + norm(vertices[3] - vertices[1])) + need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam/2)) > 0 + # build O(h) volume neighbors els_idxs = [i[2] for i in collect(el_neighs)] els_list = mesh.etype2els[Etype][els_idxs] @@ -383,22 +394,38 @@ function _local_vdim_auxiliary_quantities( Dmat = assemble_matrix(Dop) Vmat = assemble_matrix(Vop) #sleep(2) - #Inti.viz_elements_bords(neighbors, el_neighs, (Etype, el), bords, mesh; quad = Ybdry) + #Inti.viz_elements_bords(neighbors, el_neighs, (Etype, el), bords, mesh) #qnodesx = [qnode.coords[1] for qnode in Yvol] #qnodesy = [qnode.coords[2] for qnode in Yvol] #Mke.scatter!(qnodesx, qnodesy) #trgsx = [qnode.coords[1] for qnode in X] #trgsy = [qnode.coords[2] for qnode in X] #Mke.scatter!(trgsx, trgsy) + if need_layer_corr + μloc = _green_multiplier(:inside) + green_multiplier = fill(μloc, length(X)) + δS, δD = bdim_correction( + pde, + X, + Ybdry, + Smat, + Dmat; + green_multiplier, + maxdist = diam, + derivative=false, + ) + Smat += δS + Dmat += δD + #if isdefined(Main, :Infiltrator) && need_layer_corr == true + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + #end + end num_basis = length(p) num_targets = length(X) b = [f(q) for q in Yvol, f in p] γ₀B = [f(q) for q in Ybdry, f in P] γ₁B = [f(q) for q in Ybdry, f in γ₁P] - if isdefined(Main, :Infiltrator) - Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end Θ = zeros(eltype(Vop), num_targets, num_basis) # Compute Θ <-- S * γ₁B - D * γ₀B - V * b + σ * B(x) using in-place matvec for n in 1:num_basis diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 4f983ce5..66c68dfc 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -107,6 +107,7 @@ Vglob_d2d = Inti.volume_potential(; correction = ( method = :dim, mesh = Ωₕ_Sub, + bdry_nodes = Γₕ.nodes, interpolation_order, maxdist = 5 * meshsize, ), From 44695b61b98011235ac9cfa05637446df417ee46 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 18 Aug 2024 13:23:00 -0500 Subject: [PATCH 16/56] Tweak boundary quadratures in local VDIM --- src/vdim.jl | 14 +++++++++++--- test/lvdim_test.jl | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index f483de8a..11e739bc 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -178,6 +178,7 @@ function local_vdim_correction( mesh, neighbors, n, + interpolation_order, p, P, γ₁P, @@ -351,6 +352,7 @@ function _local_vdim_auxiliary_quantities( mesh, neighbors, el, + interpolation_order, p, P, γ₁P, @@ -381,9 +383,15 @@ function _local_vdim_auxiliary_quantities( # build O(h) volume neighbors els_idxs = [i[2] for i in collect(el_neighs)] els_list = mesh.etype2els[Etype][els_idxs] - Yvol = Inti.Quadrature(mesh, els_list; qorder = 7) - # TODO Need DIM corrections when X is close to Γ - Ybdry = Inti.Quadrature(mesh, bords; qorder = 8) + qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(interpolation_order) + bdry_qorder = 2 * qorder + Yvol = Inti.Quadrature(mesh, els_list; qorder) + if need_layer_corr + Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) + else + Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) + end + #Ybdry = Inti.Quadrature(mesh, bords; qorder = 8) # TODO handle derivative case G = SingleLayerKernel(pde) dG = DoubleLayerKernel(pde) diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 66c68dfc..f1a924d1 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -9,7 +9,7 @@ using FMMLIB2D using GLMakie using Meshes -meshsize = 0.01 +meshsize = 0.1 interpolation_order = 4 VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(interpolation_order) bdry_qorder = 2 * VR_qorder @@ -96,6 +96,7 @@ V_d2d = Inti.volume_potential(; method = :ldim, mesh = Ωₕ, interpolation_order, + bdry_nodes = Γₕ.nodes, maxdist = 5 * meshsize, ), ) @@ -107,7 +108,6 @@ Vglob_d2d = Inti.volume_potential(; correction = ( method = :dim, mesh = Ωₕ_Sub, - bdry_nodes = Γₕ.nodes, interpolation_order, maxdist = 5 * meshsize, ), From aaa3514e580b9c3a9c3a8629687e12bb19a86088 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Tue, 20 Aug 2024 10:12:04 -0500 Subject: [PATCH 17/56] lvdim: possible performance tweak --- src/vdim.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 11e739bc..700aba97 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -367,7 +367,12 @@ function _local_vdim_auxiliary_quantities( loc_bdry = Inti.boundarynd(el_neighs, mesh) # TODO handle curved boundary of Γ?? bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] + # TODO possible performance improvement + #bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}, 3, SVector{N, Float64}}[] for idxs in loc_bdry + # TODO possible performance improvement + #vtxs = SVector{3, SVector{2, Float64}}(Inti.nodes(mesh)[idxs]) + #bord = Inti.LagrangeLine(vtxs) vtxs = Inti.nodes(mesh)[idxs] bord = Inti.LagrangeLine(vtxs...) push!(bords, bord) @@ -424,9 +429,6 @@ function _local_vdim_auxiliary_quantities( ) Smat += δS Dmat += δD - #if isdefined(Main, :Infiltrator) && need_layer_corr == true - # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - #end end num_basis = length(p) From ef6ef7dc612cb7b4fa7fd7062dee65cf839184bb Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 23 Aug 2024 13:35:14 -0500 Subject: [PATCH 18/56] LVDIM: Use least-squares approximating polynomial. --- src/api.jl | 2 +- src/vdim.jl | 43 +++++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/api.jl b/src/api.jl index 51c690c6..ad0ef17e 100644 --- a/src/api.jl +++ b/src/api.jl @@ -290,7 +290,7 @@ function volume_potential(; pde, target, source::Quadrature, compression, correc loc = target === source ? :inside : correction.target_location μ = _green_multiplier(loc) green_multiplier = fill(μ, length(target)) - shift = Val(false) + shift = Val(true) δV = local_vdim_correction( pde, eltype(V), diff --git a/src/vdim.jl b/src/vdim.jl index 700aba97..bb63df35 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -193,33 +193,32 @@ function local_vdim_correction( iszero(center) || error("SHIFT is not implemented for non-zero center") L̃ = [f((q.coords - c) / r) for f in p, q in view(source, jglob)] S = change_of_basis(multiindices, p, c, r) - F = lu(L̃) - @debug (vander_cond = max(vander_cond, cond(L̃))) maxlog = 0 - @debug (shift_norm = max(shift_norm, norm(S))) maxlog = 0 - @debug (vander_norm = max(vander_norm, norm(L̃))) maxlog = 0 + Linv = pinv(transpose(L̃)) + wei = R * transpose(S) * Linv else L = [f(q.coords) for f in p, q in view(source, jglob)] - F = lu(L) - @debug (vander_cond = max(vander_cond, cond(L))) maxlog = 0 - @debug (shift_norm = max(shift_norm, 1)) maxlog = 0 - @debug (vander_norm = max(vander_norm, norm(L))) maxlog = 0 + Linv = pinv(transpose(L)) + wei = R * Linv end # correct each target near the current element - for i in 1:length(near_list[n]) - b = @views R[i, :] - wei = SHIFT ? F \ (S * b) : F \ b # weights for the current element and target i - rhs_norm = max(rhs_norm, norm(b)) - res_norm = if SHIFT - max(res_norm, norm(L̃ * wei - S * b)) - else - max(res_norm, norm(L * wei - b)) - end - for k in 1:nq - push!(Is, near_list[n][i]) - push!(Js, jglob[k]) - push!(Vs, wei[k]) - end + push!(Is, repeat(near_list[n], inner = nq)...) + push!(Js, repeat(jglob, outer = length(near_list[n]))...) + push!(Vs, transpose(wei)...) + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) end + #for i in 1:length(near_list[n]) + # #for k in 1:nq + # # push!(Is, near_list[n][i]) + # # push!(Js, jglob[k]) + # # push!(Vs, wei[i, k]) + # #end + # for k in 1:nq + # push!(Is, near_list[n][i]) + # push!(Js, jglob[k]) + # push!(Vs, wei[i, k]) + # end + #end end end @debug """Condition properties of vdim correction: From d880f12e5b9c0cc51d6bc091774dd684c0d5f8fd Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 23 Aug 2024 15:07:25 -0500 Subject: [PATCH 19/56] LVDIM: Expose differing quad/interp order ability --- src/api.jl | 1 + src/vdim.jl | 32 +++++++++++--------------------- test/lvdim_test.jl | 5 +++-- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/api.jl b/src/api.jl index ad0ef17e..e473e3c7 100644 --- a/src/api.jl +++ b/src/api.jl @@ -301,6 +301,7 @@ function volume_potential(; pde, target, source::Quadrature, compression, correc green_multiplier, correction.maxdist, correction.interpolation_order, + correction.quadrature_order, shift, ) else diff --git a/src/vdim.jl b/src/vdim.jl index bb63df35..0fd769c2 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -140,6 +140,7 @@ function local_vdim_correction( bdry_nodes; green_multiplier::Vector{<:Real}, interpolation_order = nothing, + quadrature_order = nothing, maxdist = Inf, center = nothing, shift::Val{SHIFT} = Val(false), @@ -168,6 +169,9 @@ function local_vdim_correction( near_list = dict_near[E] nq, ne = size(qtags) @assert length(near_list) == ne + sizehint!(Is, ne * nq * nq) + sizehint!(Js, ne * nq * nq) + sizehint!(Vs, ne * nq * nq) topo_neighs = 1 neighbors = Inti.topological_neighbors(mesh, topo_neighs) for n in 1:ne @@ -179,6 +183,7 @@ function local_vdim_correction( neighbors, n, interpolation_order, + quadrature_order, p, P, γ₁P, @@ -201,24 +206,9 @@ function local_vdim_correction( wei = R * Linv end # correct each target near the current element - push!(Is, repeat(near_list[n], inner = nq)...) - push!(Js, repeat(jglob, outer = length(near_list[n]))...) - push!(Vs, transpose(wei)...) - if isdefined(Main, :Infiltrator) - Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end - #for i in 1:length(near_list[n]) - # #for k in 1:nq - # # push!(Is, near_list[n][i]) - # # push!(Js, jglob[k]) - # # push!(Vs, wei[i, k]) - # #end - # for k in 1:nq - # push!(Is, near_list[n][i]) - # push!(Js, jglob[k]) - # push!(Vs, wei[i, k]) - # end - #end + append!(Is, repeat(near_list[n], inner = nq)...) + append!(Js, repeat(jglob, outer = length(near_list[n]))...) + append!(Vs, transpose(wei)...) end end @debug """Condition properties of vdim correction: @@ -352,6 +342,7 @@ function _local_vdim_auxiliary_quantities( neighbors, el, interpolation_order, + quadrature_order, p, P, γ₁P, @@ -387,9 +378,8 @@ function _local_vdim_auxiliary_quantities( # build O(h) volume neighbors els_idxs = [i[2] for i in collect(el_neighs)] els_list = mesh.etype2els[Etype][els_idxs] - qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(interpolation_order) - bdry_qorder = 2 * qorder - Yvol = Inti.Quadrature(mesh, els_list; qorder) + bdry_qorder = 2 * quadrature_order + Yvol = Inti.Quadrature(mesh, els_list; qorder = quadrature_order) if need_layer_corr Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) else diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index f1a924d1..a5c545db 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -9,9 +9,9 @@ using FMMLIB2D using GLMakie using Meshes -meshsize = 0.1 +meshsize = 0.02 interpolation_order = 4 -VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(interpolation_order) +VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(5) bdry_qorder = 2 * VR_qorder function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) @@ -96,6 +96,7 @@ V_d2d = Inti.volume_potential(; method = :ldim, mesh = Ωₕ, interpolation_order, + quadrature_order = VR_qorder, bdry_nodes = Γₕ.nodes, maxdist = 5 * meshsize, ), From fd98810ef2165667f32be3a22d49b46462781e04 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 23 Aug 2024 18:20:24 -0500 Subject: [PATCH 20/56] LVDIM: cleanup --- src/vdim.jl | 13 ++----------- test/boundary_test.jl | 8 ++++++++ test/lvdim_test.jl | 15 +-------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 0fd769c2..916fabb6 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -182,7 +182,6 @@ function local_vdim_correction( mesh, neighbors, n, - interpolation_order, quadrature_order, p, P, @@ -341,7 +340,6 @@ function _local_vdim_auxiliary_quantities( mesh, neighbors, el, - interpolation_order, quadrature_order, p, P, @@ -359,6 +357,7 @@ function _local_vdim_auxiliary_quantities( bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] # TODO possible performance improvement #bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}, 3, SVector{N, Float64}}[] + for idxs in loc_bdry # TODO possible performance improvement #vtxs = SVector{3, SVector{2, Float64}}(Inti.nodes(mesh)[idxs]) @@ -385,7 +384,7 @@ function _local_vdim_auxiliary_quantities( else Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) end - #Ybdry = Inti.Quadrature(mesh, bords; qorder = 8) + # TODO handle derivative case G = SingleLayerKernel(pde) dG = DoubleLayerKernel(pde) @@ -395,14 +394,6 @@ function _local_vdim_auxiliary_quantities( Smat = assemble_matrix(Sop) Dmat = assemble_matrix(Dop) Vmat = assemble_matrix(Vop) - #sleep(2) - #Inti.viz_elements_bords(neighbors, el_neighs, (Etype, el), bords, mesh) - #qnodesx = [qnode.coords[1] for qnode in Yvol] - #qnodesy = [qnode.coords[2] for qnode in Yvol] - #Mke.scatter!(qnodesx, qnodesy) - #trgsx = [qnode.coords[1] for qnode in X] - #trgsy = [qnode.coords[2] for qnode in X] - #Mke.scatter!(trgsx, trgsy) if need_layer_corr μloc = _green_multiplier(:inside) green_multiplier = fill(μloc, length(X)) diff --git a/test/boundary_test.jl b/test/boundary_test.jl index f63cc3e5..274b196b 100644 --- a/test/boundary_test.jl +++ b/test/boundary_test.jl @@ -83,3 +83,11 @@ newquad = Inti.Quadrature(test_msh, els_list; qorder = 2) Inti.viz_elements_bords(nei, test_els, (E, I), bords, test_msh) #Inti.viz_elements_bords(nei, test_els, collect(test_els)[3], bords, test_msh) +#sleep(2) +#Inti.viz_elements_bords(neighbors, el_neighs, (Etype, el), bords, mesh) +#qnodesx = [qnode.coords[1] for qnode in Yvol] +#qnodesy = [qnode.coords[2] for qnode in Yvol] +#Mke.scatter!(qnodesx, qnodesy) +#trgsx = [qnode.coords[1] for qnode in X] +#trgsy = [qnode.coords[2] for qnode in X] +#Mke.scatter!(trgsx, trgsy) \ No newline at end of file diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index a5c545db..93a694a5 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -9,7 +9,7 @@ using FMMLIB2D using GLMakie using Meshes -meshsize = 0.02 +meshsize = 0.1 interpolation_order = 4 VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(5) bdry_qorder = 2 * VR_qorder @@ -101,24 +101,11 @@ V_d2d = Inti.volume_potential(; maxdist = 5 * meshsize, ), ) -Vglob_d2d = Inti.volume_potential(; - pde, - target = Ωₕ_Sub_quad, - source = Ωₕ_Sub_quad, - compression = (method = :fmm, tol = 1e-14), - correction = ( - method = :dim, - mesh = Ωₕ_Sub, - interpolation_order, - maxdist = 5 * meshsize, - ), -) #end #@info "Volume potential time: $tvol" vref = -u_d - D_b2d * u_b + S_b2d * du_b vapprox = V_d2d * f_d -vglobapprox = Vglob_d2d * f_d er = vref - vapprox ndofs = length(er) From 830ab774709bd333c0b4693365e704829c1dbeff Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 26 Aug 2024 21:49:24 -0500 Subject: [PATCH 21/56] lvdim: initial 3d work --- ext/IntiMeshesExt.jl | 4 +- src/nystrom.jl | 5 +- src/quadrature.jl | 5 +- src/reference_interpolation.jl | 4 ++ src/vdim.jl | 37 +++++++---- test/boundary_test.jl | 11 +--- test/lvdim_test_3d.jl | 114 +++++++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 test/lvdim_test_3d.jl diff --git a/ext/IntiMeshesExt.jl b/ext/IntiMeshesExt.jl index 6688eeff..24c58338 100644 --- a/ext/IntiMeshesExt.jl +++ b/ext/IntiMeshesExt.jl @@ -113,9 +113,11 @@ function Inti.viz_elements_bords(Ei, els, ell, bords, msh; quad = nothing) if !isnothing(quad) xs = [qnode.coords[1] for qnode in quad.qnodes] ys = [qnode.coords[2] for qnode in quad.qnodes] + zs = [qnode.coords[3] for qnode in quad.qnodes] nx = [Inti.normal(qnode)[1] for qnode in quad.qnodes] ny = [Inti.normal(qnode)[2] for qnode in quad.qnodes] - Mke.arrows!(xs, ys, nx, ny; lengthscale = 0.15) + nz = [Inti.normal(qnode)[3] for qnode in quad.qnodes] + Mke.arrows!(xs, ys, zs, nx, ny, nz; lengthscale = 0.05, linewidth = 0.01, arrowsize = 0.01) end display(fig) end diff --git a/src/nystrom.jl b/src/nystrom.jl index 0efd81bd..bb08069a 100644 --- a/src/nystrom.jl +++ b/src/nystrom.jl @@ -60,8 +60,9 @@ source(iop::IntegralOperator) = iop.source function IntegralOperator(k, X, Y::Quadrature = X) T = return_type(k, eltype(X), eltype(Y)) - msg = """IntegralOperator of nonbits being created: $T""" - isbitstype(T) || (@warn msg) + # FIXME This cripples performance for local VDIM + #msg = """IntegralOperator of nonbits being created: $T""" + #isbitstype(T) || (@warn msg) return IntegralOperator{T,typeof(k),typeof(X),typeof(Y)}(k, X, Y) end diff --git a/src/quadrature.jl b/src/quadrature.jl index b7669887..134f3e1c 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -126,9 +126,12 @@ function Quadrature(msh::AbstractMesh{N,T}, elementlist::AbstractVector{E}; qord #FIXME Ideally we would like to use Gauss quadrature on the local region (fewer points for same order of accuracy) but to do so we need to subtract away the contribution from the global quadrature rule on the local region (typically, these are VR nodes). So, for now, using the global quadrature rule on the local region gives better accuracy since it cancels these (inaccurate) computations out. #etype2qrule = # Dict(E => _qrule_for_reference_shape(domain(E), qorder)) - if domain(E) isa Inti.ReferenceSimplex + if domain(E) isa Inti.ReferenceTriangle Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) etype2qrule = Dict(E => Q) + elseif domain(E) isa Inti.ReferenceTetrahedron + Q = Inti.VioreanuRokhlin(; domain = :tetrahedron, order = qorder) + etype2qrule = Dict(E => Q) else etype2qrule = Dict(E => _qrule_for_reference_shape(domain(E), qorder)) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index f00eb358..89a22b13 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -298,6 +298,10 @@ function boundary_idxs(::Type{<:LagrangeTriangle{6}}) return (1, 2, 4), (2, 3, 5), (3, 1, 6) end +function boundary_idxs(::Type{<:LagrangeTetrahedron{4}}) + return (3, 2, 1), (1, 4, 3), (2, 3, 4), (1, 2, 4) +end + function boundary1d(els, msh) res = Set{Int}() E, _ = first(els) diff --git a/src/vdim.jl b/src/vdim.jl index 916fabb6..370baa10 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -336,7 +336,7 @@ function translation_and_scaling(el::LagrangeTetrahedron) end function _local_vdim_auxiliary_quantities( - pde, + pde::AbstractPDE{N}, mesh, neighbors, el, @@ -347,37 +347,52 @@ function _local_vdim_auxiliary_quantities( X, μ, bdry_kdtree; -) +) where {N} # construct the local region Etype = first(Inti.element_types(mesh)) el_neighs = copy(neighbors[(Etype,el)]) loc_bdry = Inti.boundarynd(el_neighs, mesh) # TODO handle curved boundary of Γ?? - bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] - # TODO possible performance improvement - #bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}, 3, SVector{N, Float64}}[] + #bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] + # TODO possible performance improvement over prev line + if N == 2 + bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}, 3, SVector{N, Float64}}[] + else + bords = Inti.LagrangeElement{Inti.ReferenceSimplex{N-1}, 3, SVector{N, Float64}}[] + end for idxs in loc_bdry # TODO possible performance improvement #vtxs = SVector{3, SVector{2, Float64}}(Inti.nodes(mesh)[idxs]) #bord = Inti.LagrangeLine(vtxs) vtxs = Inti.nodes(mesh)[idxs] - bord = Inti.LagrangeLine(vtxs...) + if N === 2 + bord = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}}(vtxs...) + else + bord = Inti.LagrangeElement{Inti.ReferenceSimplex{N-1}}(vtxs...) + end push!(bords, bord) end # Check if we need to do near-singular layer potential evaluation - vertices = mesh.etype2els[Etype][el].vals[1:3] - diam = max(norm(vertices[1] - vertices[2]), - norm(vertices[2] - vertices[3]), - norm(vertices[3] - vertices[1])) + vertices = mesh.etype2els[Etype][el].vals[vertices_idxs(Etype)] + if N == 2 + diam = max(norm(vertices[1] - vertices[2]), + norm(vertices[2] - vertices[3]), + norm(vertices[3] - vertices[1])) + else + diam = max(norm(vertices[1] - vertices[2]), + norm(vertices[2] - vertices[3]), + norm(vertices[3] - vertices[4]), + norm(vertices[4] - vertices[1])) + end need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam/2)) > 0 # build O(h) volume neighbors els_idxs = [i[2] for i in collect(el_neighs)] els_list = mesh.etype2els[Etype][els_idxs] - bdry_qorder = 2 * quadrature_order + bdry_qorder = 2 * quadrature_order + 1 Yvol = Inti.Quadrature(mesh, els_list; qorder = quadrature_order) if need_layer_corr Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) diff --git a/test/boundary_test.jl b/test/boundary_test.jl index 274b196b..026c46cd 100644 --- a/test/boundary_test.jl +++ b/test/boundary_test.jl @@ -81,13 +81,4 @@ E = collect(test_els)[1][1] els_list = test_msh.etype2els[E][els_idxs] newquad = Inti.Quadrature(test_msh, els_list; qorder = 2) -Inti.viz_elements_bords(nei, test_els, (E, I), bords, test_msh) -#Inti.viz_elements_bords(nei, test_els, collect(test_els)[3], bords, test_msh) -#sleep(2) -#Inti.viz_elements_bords(neighbors, el_neighs, (Etype, el), bords, mesh) -#qnodesx = [qnode.coords[1] for qnode in Yvol] -#qnodesy = [qnode.coords[2] for qnode in Yvol] -#Mke.scatter!(qnodesx, qnodesy) -#trgsx = [qnode.coords[1] for qnode in X] -#trgsy = [qnode.coords[2] for qnode in X] -#Mke.scatter!(trgsx, trgsy) \ No newline at end of file +Inti.viz_elements_bords(nei, test_els, (E, I), bords, test_msh) \ No newline at end of file diff --git a/test/lvdim_test_3d.jl b/test/lvdim_test_3d.jl new file mode 100644 index 00000000..4fc10384 --- /dev/null +++ b/test/lvdim_test_3d.jl @@ -0,0 +1,114 @@ +# # High-order convergence of vdim + +using Inti +using Meshes +using StaticArrays +using Gmsh +using LinearAlgebra +using HMatrices +using FMM3D +using GLMakie + +meshsize = 0.1 +interpolation_order = 2 +VR_qorder = Inti.Tetrahedron_VR_interpolation_order_to_quadrature_order(interpolation_order) +bdry_qorder = 2 * VR_qorder + +function gmsh_sphere(; order = 1, name, meshsize) + try + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end + gmsh.initialize() + gmsh.option.setNumber("General.Terminal", 0) + gmsh.model.add("sphere-mesh") + gmsh.option.setNumber("Mesh.MeshSizeMax", meshsize) + gmsh.option.setNumber("Mesh.MeshSizeMin", meshsize) + gmsh.model.occ.addSphere(0, 0, 0, 1) + gmsh.model.occ.synchronize() + gmsh.model.mesh.generate() + gmsh.model.mesh.setOrder(order) + gmsh.write(name) + finally + gmsh.finalize() + end +end + +name = joinpath(@__DIR__, "sphere.msh") +gmsh_sphere(; order = 1, name, meshsize) + +Inti.clear_entities!() # empty the entity cache +msh = Inti.import_mesh(name; dim = 3) +Ω = Inti.Domain(e -> Inti.geometric_dimension(e) == 3, Inti.entities(msh)) +Γ = Inti.boundary(Ω) + + +Ωₕ = msh[Ω] +Γₕ = msh[Γ] +Ωₕ_Sub = view(msh, Ω) +Γₕ_Sub = view(msh, Γ) + +tquad = @elapsed begin + # Use VDIM with the Vioreanu-Rokhlin quadrature rule for Ωₕ + Q = Inti.VioreanuRokhlin(; domain = :tetrahedron, order = VR_qorder) + dict = Dict(E => Q for E in Inti.element_types(Ωₕ)) + Ωₕ_quad = Inti.Quadrature(Ωₕ, dict) + # Ωₕ_quad = Inti.Quadrature(Ωₕ; qorder = qorders[1]) + Γₕ_quad = Inti.Quadrature(Γₕ; qorder = bdry_qorder) +end +@info "Quadrature generation time: $tquad" + +k0 = π +k = 0 +θ = (sin(π / 3) * cos(π / 3), sin(π / 3) * sin(π / 3), cos(π / 3)) +#u = (x) -> exp(im * k0 * dot(x, θ)) +#du = (x,n) -> im * k0 * dot(θ, n) * exp(im * k0 * dot(x, θ)) +u = (x) -> cos(k0 * dot(x, θ)) +du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) +f = (x) -> (k^2 - k0^2) * u(x) + +u_d = map(q -> u(q.coords), Ωₕ_quad) +u_b = map(q -> u(q.coords), Γₕ_quad) +du_b = map(q -> du(q.coords, q.normal), Γₕ_quad) +f_d = map(q -> f(q.coords), Ωₕ_quad) + +pde = k == 0 ? Inti.Laplace(; dim = 3) : Inti.Helmholtz(; dim = 3, k) + +## Boundary operators +tbnd = @elapsed begin + S_b2d, D_b2d = Inti.single_double_layer(; + pde, + target = Ωₕ_quad, + source = Γₕ_quad, + compression = (method = :fmm, tol = 1e-8), + correction = (method = :dim, maxdist = 5 * meshsize, target_location = :inside), + ) +end +@info "Boundary operators time: $tbnd" + +## Volume potentials +tvol = @elapsed begin + V_d2d = Inti.volume_potential(; + pde, + target = Ωₕ_quad, + source = Ωₕ_quad, + compression = (method = :fmm, tol = 1e-8), + correction = ( + method = :ldim, + interpolation_order, + quadrature_order = VR_qorder, + mesh = Ωₕ, + bdry_nodes = Γₕ.nodes, + maxdist = 5 * meshsize, + ), + ) +end +@info "Volume potential time: $tvol" + +vref = -u_d - D_b2d * u_b + S_b2d * du_b +vapprox = V_d2d * f_d +er = vref - vapprox + +ndofs = length(er) + +@show ndofs, meshsize, norm(er, Inf) From 275c27fa2052d4413e05f6ad19cb5afcfc5483f7 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Wed, 28 Aug 2024 14:10:09 +0200 Subject: [PATCH 22/56] minor doc fixes --- src/adaptive.jl | 2 +- src/reference_integration.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adaptive.jl b/src/adaptive.jl index df6b4fdf..a59712f1 100644 --- a/src/adaptive.jl +++ b/src/adaptive.jl @@ -151,7 +151,7 @@ end adaptive_integration_singular(f, τ̂, x̂ₛ; kwargs...) Similar to [`adaptive_integration`](@ref), but indicates that `f` has an -isolated (integrable) singularity at `x̂ₛ ∈ x̂ₛ`. +isolated (integrable) singularity at `x̂ₛ ∈ τ̂`. The integration is performed by splitting `τ̂` so that `x̂ₛ` is a fixed vertex, guaranteeing that `f` is never evaluated at `x̂ₛ`. Aditionally, a suitable diff --git a/src/reference_integration.jl b/src/reference_integration.jl index f5692d14..55bd9259 100644 --- a/src/reference_integration.jl +++ b/src/reference_integration.jl @@ -364,7 +364,7 @@ domain(qrule)`. The following optional keyword arguments are available: - `maxsplit::Int=1000`: maximum number of times to split the domain - `norm::Function=LinearAlgebra.norm`: norm to use for error estimates - `buffer::BinaryHeap`: a pre-allocated buffer to use for the adaptive procedure - (see [allocate_buffer](@ref)) + (see [`allocate_buffer`](@ref)) """ function adaptive_integration( f, From 756ec1c80f6ed789690e65ec64a6a8c3fa1029b1 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Wed, 28 Aug 2024 14:40:11 +0200 Subject: [PATCH 23/56] cleanup dependencies --- Project.toml | 4 ---- ext/IntiMeshesExt.jl | 45 +++++++++++++++++++++++++------------------- src/Inti.jl | 1 - src/vdim.jl | 40 ++++++++++++++++++++------------------- test/lvdim_test.jl | 15 +++++++-------- 5 files changed, 54 insertions(+), 51 deletions(-) diff --git a/Project.toml b/Project.toml index 11c6a49d..a7590627 100644 --- a/Project.toml +++ b/Project.toml @@ -7,17 +7,13 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ElementaryPDESolutions = "88a69b33-da0f-4502-8c8c-d680cf4d883b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" -Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" -QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" [weakdeps] FMM2D = "2d63477d-9690-4b75-bcc1-c3461d43fecc" diff --git a/ext/IntiMeshesExt.jl b/ext/IntiMeshesExt.jl index 24c58338..973a6c6d 100644 --- a/ext/IntiMeshesExt.jl +++ b/ext/IntiMeshesExt.jl @@ -1,7 +1,6 @@ module IntiMeshesExt using Meshes -import GLMakie as Mke import Inti using StaticArrays @@ -89,37 +88,45 @@ function Meshes.viz!(msh::Inti.AbstractMesh, args...; kwargs...) return viz!(to_meshes(msh), args...; kwargs...) end - function Inti.viz_elements(els, msh) E = first(Inti.element_types(msh)) Els = [Inti.elements(msh, E)[i] for (E, i) in els] fig, _, _ = viz(Els) - viz!(msh; color = 0, showsegments = true,alpha=0.3) - display(fig) + viz!(msh; color = 0, showsegments = true, alpha = 0.3) + return display(fig) end - function Inti.viz_elements_bords(Ei, els, ell, bords, msh; quad = nothing) E = first(Inti.element_types(msh)) #ell = collect(Ei[(E, 1)])[1] el = Inti.elements(msh, ell[1])[ell[2]] - fig, _, _ = viz(msh; color = 0, showsegments = true,alpha=0.3) - viz!(el; color = 0, showsegments = true,alpha=0.5) + fig, _, _ = viz(msh; color = 0, showsegments = true, alpha = 0.3) + viz!(el; color = 0, showsegments = true, alpha = 0.5) for (E, i) in els el = Inti.elements(msh, E)[i] - viz!(el; showsegments = true, alpha=0.7) - end - viz!(bords;color=4,showsegments = false,segmentsize=5,segmentcolor=4) - if !isnothing(quad) - xs = [qnode.coords[1] for qnode in quad.qnodes] - ys = [qnode.coords[2] for qnode in quad.qnodes] - zs = [qnode.coords[3] for qnode in quad.qnodes] - nx = [Inti.normal(qnode)[1] for qnode in quad.qnodes] - ny = [Inti.normal(qnode)[2] for qnode in quad.qnodes] - nz = [Inti.normal(qnode)[3] for qnode in quad.qnodes] - Mke.arrows!(xs, ys, zs, nx, ny, nz; lengthscale = 0.05, linewidth = 0.01, arrowsize = 0.01) + viz!(el; showsegments = true, alpha = 0.7) end - display(fig) + viz!(bords; color = 4, showsegments = false, segmentsize = 5, segmentcolor = 4) + # if !isnothing(quad) + # xs = [qnode.coords[1] for qnode in quad.qnodes] + # ys = [qnode.coords[2] for qnode in quad.qnodes] + # zs = [qnode.coords[3] for qnode in quad.qnodes] + # nx = [Inti.normal(qnode)[1] for qnode in quad.qnodes] + # ny = [Inti.normal(qnode)[2] for qnode in quad.qnodes] + # nz = [Inti.normal(qnode)[3] for qnode in quad.qnodes] + # Mke.arrows!( + # xs, + # ys, + # zs, + # nx, + # ny, + # nz; + # lengthscale = 0.05, + # linewidth = 0.01, + # arrowsize = 0.01, + # ) + # end + return display(fig) end end # module diff --git a/src/Inti.jl b/src/Inti.jl index b2321b24..55d79610 100644 --- a/src/Inti.jl +++ b/src/Inti.jl @@ -14,7 +14,6 @@ using LinearMaps using NearestNeighbors using SparseArrays using StaticArrays -using QuadGK using SpecialFunctions using Printf diff --git a/src/vdim.jl b/src/vdim.jl index 370baa10..e202758a 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -28,8 +28,6 @@ See [anderson2024fast](@cite) for more details on the method. and rescaled to each element. """ -import GLMakie as Mke - function vdim_correction( pde, target, @@ -205,8 +203,8 @@ function local_vdim_correction( wei = R * Linv end # correct each target near the current element - append!(Is, repeat(near_list[n], inner = nq)...) - append!(Js, repeat(jglob, outer = length(near_list[n]))...) + append!(Is, repeat(near_list[n]; inner = nq)...) + append!(Js, repeat(jglob; outer = length(near_list[n]))...) append!(Vs, transpose(wei)...) end end @@ -350,16 +348,16 @@ function _local_vdim_auxiliary_quantities( ) where {N} # construct the local region Etype = first(Inti.element_types(mesh)) - el_neighs = copy(neighbors[(Etype,el)]) + el_neighs = copy(neighbors[(Etype, el)]) loc_bdry = Inti.boundarynd(el_neighs, mesh) # TODO handle curved boundary of Γ?? #bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] # TODO possible performance improvement over prev line if N == 2 - bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}, 3, SVector{N, Float64}}[] + bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N - 1},3,SVector{N,Float64}}[] else - bords = Inti.LagrangeElement{Inti.ReferenceSimplex{N-1}, 3, SVector{N, Float64}}[] + bords = Inti.LagrangeElement{Inti.ReferenceSimplex{N - 1},3,SVector{N,Float64}}[] end for idxs in loc_bdry @@ -368,9 +366,9 @@ function _local_vdim_auxiliary_quantities( #bord = Inti.LagrangeLine(vtxs) vtxs = Inti.nodes(mesh)[idxs] if N === 2 - bord = Inti.LagrangeElement{Inti.ReferenceHyperCube{N-1}}(vtxs...) + bord = Inti.LagrangeElement{Inti.ReferenceHyperCube{N - 1}}(vtxs...) else - bord = Inti.LagrangeElement{Inti.ReferenceSimplex{N-1}}(vtxs...) + bord = Inti.LagrangeElement{Inti.ReferenceSimplex{N - 1}}(vtxs...) end push!(bords, bord) end @@ -378,16 +376,20 @@ function _local_vdim_auxiliary_quantities( # Check if we need to do near-singular layer potential evaluation vertices = mesh.etype2els[Etype][el].vals[vertices_idxs(Etype)] if N == 2 - diam = max(norm(vertices[1] - vertices[2]), - norm(vertices[2] - vertices[3]), - norm(vertices[3] - vertices[1])) + diam = max( + norm(vertices[1] - vertices[2]), + norm(vertices[2] - vertices[3]), + norm(vertices[3] - vertices[1]), + ) else - diam = max(norm(vertices[1] - vertices[2]), - norm(vertices[2] - vertices[3]), - norm(vertices[3] - vertices[4]), - norm(vertices[4] - vertices[1])) + diam = max( + norm(vertices[1] - vertices[2]), + norm(vertices[2] - vertices[3]), + norm(vertices[3] - vertices[4]), + norm(vertices[4] - vertices[1]), + ) end - need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam/2)) > 0 + need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam / 2)) > 0 # build O(h) volume neighbors els_idxs = [i[2] for i in collect(el_neighs)] @@ -401,7 +403,7 @@ function _local_vdim_auxiliary_quantities( end # TODO handle derivative case - G = SingleLayerKernel(pde) + G = SingleLayerKernel(pde) dG = DoubleLayerKernel(pde) Sop = IntegralOperator(G, X, Ybdry) Dop = IntegralOperator(dG, X, Ybdry) @@ -420,7 +422,7 @@ function _local_vdim_auxiliary_quantities( Dmat; green_multiplier, maxdist = diam, - derivative=false, + derivative = false, ) Smat += δS Dmat += δD diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 93a694a5..08c391d9 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -39,7 +39,6 @@ msh = Inti.import_mesh(name; dim = 2) Ω = Inti.Domain(e -> Inti.geometric_dimension(e) == 2, Inti.entities(msh)) Γ = Inti.boundary(Ω) - Ωₕ = msh[Ω] Γₕ = msh[Γ] Ωₕ_Sub = view(msh, Ω) @@ -75,13 +74,13 @@ pde = k == 0 ? Inti.Laplace(; dim = 2) : Inti.Helmholtz(; dim = 2, k) ## Boundary operators tbnd = @elapsed begin -S_b2d, D_b2d = Inti.single_double_layer(; - pde, - target = Ωₕ_quad, - source = Γₕ_quad, - compression = (method = :fmm, tol = 1e-14), - correction = (method = :dim, maxdist = 5 * meshsize, target_location = :inside), -) + S_b2d, D_b2d = Inti.single_double_layer(; + pde, + target = Ωₕ_quad, + source = Γₕ_quad, + compression = (method = :fmm, tol = 1e-14), + correction = (method = :dim, maxdist = 5 * meshsize, target_location = :inside), + ) end @info "Boundary operators time: $tbnd" From 20064558e4b242e1cb7d643e948c6508d7165063 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Wed, 28 Aug 2024 16:27:01 +0200 Subject: [PATCH 24/56] perf improvement --- src/vdim.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 2316df85..accfb2d4 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -539,7 +539,8 @@ function polynomial_solutions_vdim(pde::AbstractPDE, order::Integer, center = no return (q) -> f(coords(q) - center) end neumann_shift = map(neumann_traces) do f - return (q) -> f((coords = q.coords - center, normal = q.normal)) + # return (q) -> f((coords = q.coords - center, normal = q.normal)) + return (q) -> f(coords(q) - center, normal(q)) end return monomial_shift, dirchlet_shift, neumann_shift, multiindices # return monomials, dirchlet_traces, neumann_traces, multiindices @@ -566,7 +567,8 @@ end function _normal_derivative(P::ElementaryPDESolutions.Polynomial{N,T}) where {N,T} ∇P = ElementaryPDESolutions.gradient(P) - return (q) -> dot(normal(q), ∇P(coords(q))) + # return (q) -> dot(normal(q), ∇P(coords(q))) + return (x, n) -> dot(n, ∇P(x)) end function (∇P::NTuple{N,<:ElementaryPDESolutions.Polynomial})(x) where {N} From 117b52df10376fdadcd6c26d0faa56f7490eeab5 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Thu, 29 Aug 2024 22:54:01 -0500 Subject: [PATCH 25/56] lvdim: Initial work towards shifting/scaling every local computation for better conditioning --- src/bdim.jl | 6 +-- src/quadrature.jl | 41 +++++++++------- src/reference_interpolation.jl | 4 +- src/utils.jl | 3 +- src/vdim.jl | 59 ++++++++++++++--------- test/Project.toml | 1 - test/boundary_test.jl | 6 +-- test/convergence/vdim_potential_script.jl | 1 - test/ldim_test.jl | 16 +++--- test/lvdim_test_3d.jl | 1 - test/nearlist_test.jl | 16 +++--- test/test_utils.jl | 2 +- 12 files changed, 88 insertions(+), 68 deletions(-) diff --git a/src/bdim.jl b/src/bdim.jl index 14d6ac99..16187cb7 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -277,13 +277,13 @@ function local_bdim_correction( qnodes_nei = source.qnodes[qtags_nei] jac = jacobian(el, 0.5) ν = -_normal(jac) - h = sum(qnodes[i].weight for i in jglob)*4 + h = sum(qnodes[i].weight for i in jglob) * 4 qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) bindx = boundary1d(nei, msh) l, r = nodes(msh)[-bindx[1]], nodes(msh)[bindx[2]] Q, W = gauss(4nq, 0, h) - qnodes_l = [QuadratureNode(l.+q.*ν, w, SVector(-ν[2], ν[1])) for (q, w) in zip(Q, W)] - qnodes_r = [QuadratureNode(r.+q.*ν, w, SVector(ν[2], -ν[1])) for (q, w) in zip(Q, W)] + qnodes_l = [QuadratureNode(l .+ q .* ν, w, SVector(-ν[2], ν[1])) for (q, w) in zip(Q, W)] + qnodes_r = [QuadratureNode(r .+ q .* ν, w, SVector(ν[2], -ν[1])) for (q, w) in zip(Q, W)] qnodes_aux = append!(qnodes_nei, qnodes_op, qnodes_l, qnodes_r) # qnodes_aux = source.qnodes # this is the global dim for i in near_list[n] diff --git a/src/quadrature.jl b/src/quadrature.jl index 08deaa64..f2ca9a59 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -129,10 +129,13 @@ function Quadrature(msh::AbstractMesh{N,T}, etype2qrule::Dict) where {N,T} end # Quadrature constructor for list of volume elements for local vdim -function Quadrature(msh::AbstractMesh{N,T}, elementlist::AbstractVector{E}; qorder) where {N,T,E} - #FIXME Ideally we would like to use Gauss quadrature on the local region (fewer points for same order of accuracy) but to do so we need to subtract away the contribution from the global quadrature rule on the local region (typically, these are VR nodes). So, for now, using the global quadrature rule on the local region gives better accuracy since it cancels these (inaccurate) computations out. - #etype2qrule = - # Dict(E => _qrule_for_reference_shape(domain(E), qorder)) +function Quadrature( + msh::AbstractMesh{N,T}, + elementlist::AbstractVector{E}; + qorder, + center::SVector{N,Float64} = zero(SVector{N,Float64}), + scale::Float64 = 1.0, +) where {N,T,E} if domain(E) isa Inti.ReferenceTriangle Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) etype2qrule = Dict(E => Q) @@ -140,8 +143,7 @@ function Quadrature(msh::AbstractMesh{N,T}, elementlist::AbstractVector{E}; qord Q = Inti.VioreanuRokhlin(; domain = :tetrahedron, order = qorder) etype2qrule = Dict(E => Q) else - etype2qrule = - Dict(E => _qrule_for_reference_shape(domain(E), qorder)) + etype2qrule = Dict(E => _qrule_for_reference_shape(domain(E), qorder)) end # initialize mesh with empty fields @@ -153,7 +155,7 @@ function Quadrature(msh::AbstractMesh{N,T}, elementlist::AbstractVector{E}; qord ) # loop element types and generate quadrature for each qrule = etype2qrule[E] - _build_quadrature!(quad, elementlist, qrule) + _build_quadrature!(quad, elementlist, qrule; center, scale) # check for entities with negative orientation and flip normal vectors if # present @@ -176,11 +178,15 @@ function Quadrature(msh::AbstractMesh; qorder) end @noinline function _build_quadrature!( - quad, + quad::Quadrature{N,T}, els::AbstractVector{E}, - qrule::ReferenceQuadrature, -) where {E} - N = ambient_dimension(quad) + qrule::ReferenceQuadrature; + center::SVector{N,Float64} = zero(SVector{N,Float64}), + scale::Float64 = 1.0, +) where {E,N,T} + #if isdefined(Main, :Infiltrator) + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + #end x̂, ŵ = qrule() # nodes and weights on reference element num_nodes = length(ŵ) M = geometric_dimension(domain(E)) @@ -194,7 +200,7 @@ end μ = _integration_measure(jac) w = μ * ŵi ν = codim == 1 ? _normal(jac) : nothing - qnode = QuadratureNode(x, w, ν) + qnode = QuadratureNode((x - center) / scale, w / scale^M, ν) push!(quad.qnodes, qnode) end end @@ -331,7 +337,7 @@ end Return `Nl = [[el in Y.mesh && dist(x, el) ≤ tol] for x in X]` """ -function etype_to_near_elements(X,Y::Quadrature; tol) +function etype_to_near_elements(X, Y::Quadrature; tol) y = [coords(q) for q in Y] tree = BallTree(y) etype2nearlist = Dict{DataType,Vector{Set{Int}}}() @@ -346,7 +352,7 @@ function etype_to_near_elements(X,Y::Quadrature; tol) end for n in 1:N for i in 1:P - for l in quad2source[Q[i,n]] + for l in quad2source[Q[i, n]] push!(source2el[l], n) end end @@ -371,9 +377,9 @@ function near_elements(Y::Quadrature; tol) P, N = size(Q) for n in 1:N for i in 1:P - for q in inrange(tree, coords(qnodes(Y)[Q[i,n]]), tol) + for q in inrange(tree, coords(qnodes(Y)[Q[i, n]]), tol) push!(quad2el[q], (E, n)) - end + end end end end @@ -382,7 +388,7 @@ function near_elements(Y::Quadrature; tol) for (E, Q) in Y.etype2qtags P, N = size(Q) for n in 1:N - el2el[(E, n)] = union([quad2el[Q[i,n]] for i in 1:P]...) + el2el[(E, n)] = union([quad2el[Q[i, n]] for i in 1:P]...) end end return el2el @@ -410,7 +416,6 @@ end # return C, r # end - """ quadrature_to_node_vals(Q::Quadrature, qvals::AbstractVector) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index e8905ace..4b9d118f 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -376,7 +376,7 @@ function boundary1d(els, msh) E, _ = first(els) bdi = Inti.boundary_idxs(E) for (E, i) in els - vertices = Inti.connectivity(msh, E)[:,i] + vertices = Inti.connectivity(msh, E)[:, i] for bord in (-vertices[bdi[1]], vertices[bdi[2]]) -bord in res ? delete!(res, -bord) : push!(res, bord) end @@ -389,7 +389,7 @@ function boundarynd(els, msh) E, _ = first(els) bdi = Inti.boundary_idxs(E) for (E, i) in els - vertices = Inti.connectivity(msh, E)[:,i] + vertices = Inti.connectivity(msh, E)[:, i] bords = [[vertices[i] for i in bi] for bi in bdi] for new_bord in bords flag = true diff --git a/src/utils.jl b/src/utils.jl index 0498f70f..7b646a5c 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -253,7 +253,8 @@ function _normalize_correction(correction, target, source) target === source || @warn("missing maxdist field in correction: setting to Inf") if correction.method == :ldim - haskey(correction, :mesh) || error("missing mesh information needed for local dim") + haskey(correction, :mesh) || + error("missing mesh information needed for local dim") end correction = merge( (maxdist = Inf, interpolation_order = nothing, center = nothing), diff --git a/src/vdim.jl b/src/vdim.jl index accfb2d4..1dba46fa 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -154,14 +154,14 @@ function local_vdim_correction( isnothing(interpolation_order) && (interpolation_order = maximum(order, values(source.etype2qrule))) # by default basis centered at origin - center = isnothing(center) ? zero(SVector{N,Float64}) : center - p, P, γ₁P, multiindices = polynomial_solutions_vdim(pde, interpolation_order, center) + p, P, γ₁P, multiindices = polynomial_solutions_vdim(pde, interpolation_order) dict_near = etype_to_nearest_points(target, source; maxdist) bdry_kdtree = KDTree(bdry_nodes) # compute sparse correction Is = Int[] Js = Int[] Vs = Eltype[] + L̃ = Matrix{Float64}(undef, 21, 15) for (E, qtags) in source.etype2qtags els = elements(source.mesh, E) near_list = dict_near[E] @@ -175,11 +175,21 @@ function local_vdim_correction( for n in 1:ne # indices of nodes in element `n` isempty(near_list[n]) && continue + c, r = translation_and_scaling(els[n]) + #c = SVector{2, Float64}(0,0) + # FIXME Why does scaling the whole domain not work? + r = 1.0 + if !SHIFT + c = SVector{N,Float64}(0, 0) + r = 1.0 + end R = _local_vdim_auxiliary_quantities( pde, mesh, neighbors, n, + c, + r, quadrature_order, p, P, @@ -190,22 +200,20 @@ function local_vdim_correction( ) jglob = @view qtags[:, n] # compute translation and scaling - c, r = translation_and_scaling(els[n]) if SHIFT - iszero(center) || error("SHIFT is not implemented for non-zero center") - L̃ = [f((q.coords - c) / r) for f in p, q in view(source, jglob)] - S = change_of_basis(multiindices, p, c, r) - Linv = pinv(transpose(L̃)) - wei = R * transpose(S) * Linv + L̃ = [f((q.coords - c) / r) for q in view(source, jglob), f in p] + Linv = pinv(L̃) + wei = transpose(Linv) * transpose(R) else - L = [f(q.coords) for f in p, q in view(source, jglob)] - Linv = pinv(transpose(L)) - wei = R * Linv + error("unsupported local VDIM without shifting") + #L = [f(q.coords) for q in view(source, jglob), f in p] + #Linv = pinv(L) + #wei = transpose(Liinv) * transpose(R) end # correct each target near the current element append!(Is, repeat(near_list[n]; inner = nq)...) append!(Js, repeat(jglob; outer = length(near_list[n]))...) - append!(Vs, transpose(wei)...) + append!(Vs, wei...) end end @debug """Condition properties of vdim correction: @@ -338,6 +346,8 @@ function _local_vdim_auxiliary_quantities( mesh, neighbors, el, + center, + scale, quadrature_order, p, P, @@ -348,9 +358,9 @@ function _local_vdim_auxiliary_quantities( ) where {N} # construct the local region Etype = first(Inti.element_types(mesh)) - el_neighs = copy(neighbors[(Etype, el)]) + el_neighs = neighbors[(Etype, el)] - loc_bdry = Inti.boundarynd(el_neighs, mesh) + loc_bdry = Inti.boundarynd_new(el_neighs, mesh) # TODO handle curved boundary of Γ?? #bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] # TODO possible performance improvement over prev line @@ -395,19 +405,22 @@ function _local_vdim_auxiliary_quantities( els_idxs = [i[2] for i in collect(el_neighs)] els_list = mesh.etype2els[Etype][els_idxs] bdry_qorder = 2 * quadrature_order + 1 - Yvol = Inti.Quadrature(mesh, els_list; qorder = quadrature_order) + Yvol = + Inti.Quadrature(mesh, els_list; qorder = quadrature_order, center = center, scale) if need_layer_corr - Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) + Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder, center = center, scale) else - Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder) + Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder, center = center, scale) end # TODO handle derivative case G = SingleLayerKernel(pde) dG = DoubleLayerKernel(pde) - Sop = IntegralOperator(G, X, Ybdry) - Dop = IntegralOperator(dG, X, Ybdry) - Vop = IntegralOperator(G, X, Yvol) + Xcoords = [q.coords for q in X] + Xshift = [(q.coords - center) / scale for q in X] + Sop = IntegralOperator(G, Xshift, Ybdry) + Dop = IntegralOperator(dG, Xshift, Ybdry) + Vop = IntegralOperator(G, Xshift, Yvol) Smat = assemble_matrix(Sop) Dmat = assemble_matrix(Dop) Vmat = assemble_matrix(Vop) @@ -416,12 +429,12 @@ function _local_vdim_auxiliary_quantities( green_multiplier = fill(μloc, length(X)) δS, δD = bdim_correction( pde, - X, + Xshift, Ybdry, Smat, Dmat; green_multiplier, - maxdist = diam, + maxdist = diam / scale, derivative = false, ) Smat += δS @@ -440,7 +453,7 @@ function _local_vdim_auxiliary_quantities( @views mul!(Θ[:, n], Dmat, γ₀B[:, n], -1, 1) @views mul!(Θ[:, n], Vmat, b[:, n], -1, 1) for i in 1:num_targets - Θ[i, n] += μ[i] * P[n](X[i]) + Θ[i, n] += μ[i] * P[n](Xshift[i]) end end return Θ diff --git a/test/Project.toml b/test/Project.toml index dfd08431..1d2e8bf6 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,7 +2,6 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" FMM2D = "2d63477d-9690-4b75-bcc1-c3461d43fecc" FMM3D = "1e13804c-f9b7-11ea-0ef0-29f3b1745df8" -GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" HMatrices = "8646bddf-ab1c-4fa7-9c51-ba187d647618" Inti = "fb74042b-437e-4c5b-88cf-d4e2beb394d5" diff --git a/test/boundary_test.jl b/test/boundary_test.jl index 026c46cd..0c47cc54 100644 --- a/test/boundary_test.jl +++ b/test/boundary_test.jl @@ -57,7 +57,7 @@ end # el_in_set(el, set) = any(x->sort(x) == sort(el), set) I = 3 -test_els = union(copy(nei[(E,I)])) +test_els = union(copy(nei[(E, I)])) els = Inti.elements(test_msh, E) #test_els = union(copy(nei[(E,1)]), nei[(E,2)]) #test_els = union(copy(nei[(E,1)]), nei[(E,2)], nei[(E,3)], nei[(E,4)]) @@ -65,7 +65,7 @@ Inti.viz_elements(test_els, test_msh) components = Inti.connected_components(test_els, nei) -test_els = copy(nei[(E,I)]) +test_els = copy(nei[(E, I)]) BD = Inti.boundarynd(test_els, test_msh) # bords = [Inti.nodes(test_msh)[abs(i)] for i in BD] @@ -81,4 +81,4 @@ E = collect(test_els)[1][1] els_list = test_msh.etype2els[E][els_idxs] newquad = Inti.Quadrature(test_msh, els_list; qorder = 2) -Inti.viz_elements_bords(nei, test_els, (E, I), bords, test_msh) \ No newline at end of file +Inti.viz_elements_bords(nei, test_els, (E, I), bords, test_msh) diff --git a/test/convergence/vdim_potential_script.jl b/test/convergence/vdim_potential_script.jl index f3e1ff6c..16b7324b 100644 --- a/test/convergence/vdim_potential_script.jl +++ b/test/convergence/vdim_potential_script.jl @@ -38,7 +38,6 @@ msh = Inti.import_mesh(name; dim = 2) Ω = Inti.Domain(e -> Inti.geometric_dimension(e) == 2, Inti.entities(msh)) Γ = Inti.boundary(Ω) - Ωₕ = msh[Ω] Γₕ = msh[Γ] Ωₕ_sub = view(msh, Ω) diff --git a/test/ldim_test.jl b/test/ldim_test.jl index dcc72e94..22471257 100644 --- a/test/ldim_test.jl +++ b/test/ldim_test.jl @@ -13,13 +13,14 @@ t = :interior pde = Inti.Laplace(; dim = N) K = 5:5 -H = [0.2*2.0^(-i) for i in 0:0] -fig = plot(xscale=:log,yscale=:log,xticks=H) -for k in K +H = [0.2 * 2.0^(-i) for i in 0:0] +fig = plot(; xscale = :log, yscale = :log, xticks = H) +for k in K err = [] for h in H Inti.clear_entities!() - Ω, msh = gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = h, order = 2) + Ω, msh = + gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = h, order = 2) Γ = Inti.external_boundary(Ω) ## @@ -54,7 +55,8 @@ for k in K # arrows!(X, Y, u, v, lengthscale=0.01) # display(fig) - δS, δD = Inti.local_bdim_correction(pde, quad, quad; green_multiplier, kneighbor=k) + δS, δD = + Inti.local_bdim_correction(pde, quad, quad; green_multiplier, kneighbor = k) Sdim = Smat + δS Ddim = Dmat + δD # Sdim, Ddim = Inti.single_double_layer(; @@ -69,6 +71,6 @@ for k in K @show norm(e1, Inf) push!(err, e1) end - plot!(fig, H, err;lw=2,marker=:o,label=k) + plot!(fig, H, err; lw = 2, marker = :o, label = k) end -display(fig) \ No newline at end of file +display(fig) diff --git a/test/lvdim_test_3d.jl b/test/lvdim_test_3d.jl index 4fc10384..6cfedd94 100644 --- a/test/lvdim_test_3d.jl +++ b/test/lvdim_test_3d.jl @@ -42,7 +42,6 @@ msh = Inti.import_mesh(name; dim = 3) Ω = Inti.Domain(e -> Inti.geometric_dimension(e) == 3, Inti.entities(msh)) Γ = Inti.boundary(Ω) - Ωₕ = msh[Ω] Γₕ = msh[Γ] Ωₕ_Sub = view(msh, Ω) diff --git a/test/nearlist_test.jl b/test/nearlist_test.jl index 435f3e09..7efc2816 100644 --- a/test/nearlist_test.jl +++ b/test/nearlist_test.jl @@ -14,18 +14,20 @@ pde = Inti.Laplace(; dim = N) h = 0.2 Inti.clear_entities!() -Ω, msh = gmsh_disks([([0.0,0.0],1.0,1.0), ([-2.1,0.0],1.0,1.0)];meshsize = h, order = 2) +Ω, msh = + gmsh_disks([([0.0, 0.0], 1.0, 1.0), ([-2.1, 0.0], 1.0, 1.0)]; meshsize = h, order = 2) Γ = Inti.external_boundary(Ω) quad = Inti.Quadrature(msh[Γ]; qorder = 3) # Nl = Inti.near_elements(quad;tol=0.2) -Ncl = Inti.near_components(quad;tol=0.2) -fig, _, _ = viz(msh;showsegments = false,alpha=0.3) +Ncl = Inti.near_components(quad; tol = 0.2) +fig, _, _ = viz(msh; showsegments = false, alpha = 0.3) -E = first(keys(Ncl))[1]; i = 1 -viz!(Inti.elements(msh[Γ], E)[i];color=:red) +E = first(keys(Ncl))[1]; +i = 1; +viz!(Inti.elements(msh[Γ], E)[i]; color = :red) for (E_, j) in Ncl[(E, i)][2] - viz!(Inti.elements(msh[Γ], E_)[j];color=:blue,alpha=0.3) + viz!(Inti.elements(msh[Γ], E_)[j]; color = :blue, alpha = 0.3) end # for (E, nl) in Nl @@ -35,4 +37,4 @@ end # viz!(Inti.elements(msh[Γ], E)[j];color=:blue,alpha=0.3) # end # end -display(fig) \ No newline at end of file +display(fig) diff --git a/test/test_utils.jl b/test/test_utils.jl index ba5bcb63..a6b89371 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -23,7 +23,7 @@ function gmsh_disk(; center, rx, ry, meshsize, order = 1) return Ω, msh end -function gmsh_disks(disks;meshsize, order = 1) +function gmsh_disks(disks; meshsize, order = 1) msh = try gmsh.initialize() gmsh.option.setNumber("General.Verbosity", 2) From cdb501864d2128fac37e4ca55b8696191816be18 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 30 Aug 2024 12:06:05 -0500 Subject: [PATCH 26/56] Fix breakage --- src/vdim.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vdim.jl b/src/vdim.jl index 1dba46fa..385480fd 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -360,7 +360,7 @@ function _local_vdim_auxiliary_quantities( Etype = first(Inti.element_types(mesh)) el_neighs = neighbors[(Etype, el)] - loc_bdry = Inti.boundarynd_new(el_neighs, mesh) + loc_bdry = Inti.boundarynd(el_neighs, mesh) # TODO handle curved boundary of Γ?? #bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] # TODO possible performance improvement over prev line From 88777856d67f3c9836c9ed1b8679fb250a527d02 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 31 Aug 2024 00:08:26 -0500 Subject: [PATCH 27/56] lvdim: perf improvement for `boundarynd()` --- src/reference_interpolation.jl | 41 ++++++++++++++++++++-------------- src/vdim.jl | 8 ++++--- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 4b9d118f..4604bfb6 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -384,28 +384,35 @@ function boundary1d(els, msh) return sort([res...]) end -function boundarynd(els, msh) - res = Set() - E, _ = first(els) - bdi = Inti.boundary_idxs(E) - for (E, i) in els - vertices = Inti.connectivity(msh, E)[:, i] +function boundarynd(::Type{T}, els, msh) where {T} + bdi = Inti.boundary_idxs(T) + edgelist = Vector{Int64}[] + edgelist_unsrt = Vector{Int64}[] + for i in els + vertices = Inti.connectivity(msh, T)[:, i] bords = [[vertices[i] for i in bi] for bi in bdi] - for new_bord in bords - flag = true - for old_bord in res - if sort(new_bord) == sort(old_bord) - delete!(res, old_bord) - flag = false - end - end - flag && push!(res, new_bord) + for q in bords + push!(edgelist_unsrt, copy(q)) + push!(edgelist, sort!(q)) end end - return res + I = sortperm(edgelist) + uniqlist = Int64[] + i = 1 + while i <= length(edgelist) - 1 + if isequal(edgelist[I[i]], edgelist[I[i+1]]) + i += 1 + else + push!(uniqlist, i) + end + i += 1 + end + if !isequal(edgelist[I[end-1]], edgelist[I[end]]) + push!(uniqlist, i) + end + return edgelist_unsrt[I[uniqlist]] end -## function _dfs!(comp, el, nei, els) for el_nei in nei[el] if el_nei in els diff --git a/src/vdim.jl b/src/vdim.jl index 385480fd..9d96d40f 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -360,7 +360,11 @@ function _local_vdim_auxiliary_quantities( Etype = first(Inti.element_types(mesh)) el_neighs = neighbors[(Etype, el)] - loc_bdry = Inti.boundarynd(el_neighs, mesh) + T = first(el_neighs)[1] + els_idxs = [i[2] for i in collect(el_neighs)] + els_list = mesh.etype2els[Etype][els_idxs] + + loc_bdry = Inti.boundarynd(T, els_idxs, mesh) # TODO handle curved boundary of Γ?? #bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] # TODO possible performance improvement over prev line @@ -402,8 +406,6 @@ function _local_vdim_auxiliary_quantities( need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam / 2)) > 0 # build O(h) volume neighbors - els_idxs = [i[2] for i in collect(el_neighs)] - els_list = mesh.etype2els[Etype][els_idxs] bdry_qorder = 2 * quadrature_order + 1 Yvol = Inti.Quadrature(mesh, els_list; qorder = quadrature_order, center = center, scale) From fd49860a0d44ddca3433539f0eeb33eeee954b48 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 31 Aug 2024 12:31:39 -0500 Subject: [PATCH 28/56] lvdim: perf improvements --- src/vdim.jl | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 9d96d40f..7b037e91 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -129,9 +129,14 @@ function vdim_correction( return δV end +# function barrier for type stability purposes +function build_vander(pts, p, c, r) + return [f((q.coords - c) / r) for q in pts, f in p] +end + function local_vdim_correction( pde, - Eltype, + ::Type{Eltype}, target, source::Quadrature, mesh::AbstractMesh, @@ -142,7 +147,7 @@ function local_vdim_correction( maxdist = Inf, center = nothing, shift::Val{SHIFT} = Val(false), -) where {SHIFT} +) where {SHIFT,Eltype} # variables for debugging the condition properties of the method vander_cond = vander_norm = rhs_norm = res_norm = shift_norm = -Inf # figure out if we are dealing with a scalar or vector PDE @@ -201,19 +206,16 @@ function local_vdim_correction( jglob = @view qtags[:, n] # compute translation and scaling if SHIFT - L̃ = [f((q.coords - c) / r) for q in view(source, jglob), f in p] + L̃ = build_vander(view(source, jglob), p, c, r) Linv = pinv(L̃) wei = transpose(Linv) * transpose(R) else error("unsupported local VDIM without shifting") - #L = [f(q.coords) for q in view(source, jglob), f in p] - #Linv = pinv(L) - #wei = transpose(Liinv) * transpose(R) end # correct each target near the current element - append!(Is, repeat(near_list[n]; inner = nq)...) - append!(Js, repeat(jglob; outer = length(near_list[n]))...) - append!(Vs, wei...) + append!(Is, repeat(near_list[n]; inner = nq)) + append!(Js, repeat(jglob; outer = length(near_list[n]))) + append!(Vs, wei) end end @debug """Condition properties of vdim correction: @@ -380,7 +382,7 @@ function _local_vdim_auxiliary_quantities( #bord = Inti.LagrangeLine(vtxs) vtxs = Inti.nodes(mesh)[idxs] if N === 2 - bord = Inti.LagrangeElement{Inti.ReferenceHyperCube{N - 1}}(vtxs...) + bord = Inti.LagrangeLine(SVector{3}(vtxs)) else bord = Inti.LagrangeElement{Inti.ReferenceSimplex{N - 1}}(vtxs...) end From 9ea2b6ea91caf7a0da8483695a20170fa90a8c56 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 31 Aug 2024 14:39:27 -0500 Subject: [PATCH 29/56] lvdim: various performance improvements --- src/quadrature.jl | 1 + src/reference_integration.jl | 10 ++++++---- src/reference_interpolation.jl | 26 ++++++++++++++++---------- src/vdim.jl | 6 +++++- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index f2ca9a59..159bed87 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -192,6 +192,7 @@ end M = geometric_dimension(domain(E)) codim = N - M istart = length(quad.qnodes) + 1 + sizehint!(quad.qnodes, length(els) * length(x̂)) for el in els # and all qnodes for that element for (x̂i, ŵi) in zip(x̂, ŵ) diff --git a/src/reference_integration.jl b/src/reference_integration.jl index 32bcee8c..27f23f8c 100644 --- a/src/reference_integration.jl +++ b/src/reference_integration.jl @@ -226,12 +226,14 @@ struct VioreanuRokhlin{D,N} <: ReferenceQuadrature{D} domain == :triangle && (domain = ReferenceTriangle()) domain == :tetrahedron && (domain = ReferenceTetrahedron()) if domain isa ReferenceTriangle - msg = "VioreanuRokhlin quadrature of order $order not available for ReferenceTriangle" - haskey(TRIANGLE_VR_ORDER_TO_NPTS, order) || error(msg) + if !haskey(TRIANGLE_VR_ORDER_TO_NPTS, order) + error("VioreanuRokhlin quadrature of order $order not available for ReferenceTriangle") + end n = TRIANGLE_VR_ORDER_TO_NPTS[order] elseif domain isa ReferenceTetrahedron - msg = "VioreanuRokhlin quadrature of order $order not available for ReferenceTetrahedron" - haskey(TETRAHEDRON_VR_ORDER_TO_NPTS, order) || error(msg) + if !haskey(TETRAHEDRON_VR_ORDER_TO_NPTS, order) + error("VioreanuRokhlin quadrature of order $order not available for ReferenceTetrahedron") + end n = TETRAHEDRON_VR_ORDER_TO_NPTS[order] else error( diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 4604bfb6..b7917aa7 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -386,31 +386,37 @@ end function boundarynd(::Type{T}, els, msh) where {T} bdi = Inti.boundary_idxs(T) - edgelist = Vector{Int64}[] - edgelist_unsrt = Vector{Int64}[] - for i in els - vertices = Inti.connectivity(msh, T)[:, i] - bords = [[vertices[i] for i in bi] for bi in bdi] + nedges = length(els)*length(bdi[1]) + edgelist = Vector{SVector{3, Int64}}(undef, nedges) + edgelist_unsrt = Vector{SVector{3,Int64}}(undef, nedges) + bords = Vector{MVector{3,Int64}}(undef, length(bdi[1])) + j = 1 + for ii in els + for k in 1:length(bdi[1]) + bords[k] = [Inti.connectivity(msh, T)[i, ii] for i in bdi[k]] + end for q in bords - push!(edgelist_unsrt, copy(q)) - push!(edgelist, sort!(q)) + edgelist_unsrt[j] = q[:] + edgelist[j] = sort!(q) + j += 1 end end I = sortperm(edgelist) uniqlist = Int64[] + sizehint!(uniqlist, length(els)) i = 1 while i <= length(edgelist) - 1 if isequal(edgelist[I[i]], edgelist[I[i+1]]) i += 1 else - push!(uniqlist, i) + push!(uniqlist, I[i]) end i += 1 end if !isequal(edgelist[I[end-1]], edgelist[I[end]]) - push!(uniqlist, i) + push!(uniqlist, I[i]) end - return edgelist_unsrt[I[uniqlist]] + return edgelist_unsrt[uniqlist] end function _dfs!(comp, el, nei, els) diff --git a/src/vdim.jl b/src/vdim.jl index 7b037e91..4d5a930e 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -343,6 +343,10 @@ function translation_and_scaling(el::LagrangeTetrahedron) return center, R end +function newbord_line(vtxs) + return Inti.LagrangeLine(SVector{3}(vtxs)) +end + function _local_vdim_auxiliary_quantities( pde::AbstractPDE{N}, mesh, @@ -382,7 +386,7 @@ function _local_vdim_auxiliary_quantities( #bord = Inti.LagrangeLine(vtxs) vtxs = Inti.nodes(mesh)[idxs] if N === 2 - bord = Inti.LagrangeLine(SVector{3}(vtxs)) + bord = newbord_line(vtxs) else bord = Inti.LagrangeElement{Inti.ReferenceSimplex{N - 1}}(vtxs...) end From 58b8f854ffe9bb3fb48ce1f0a3a62cc73b75f48f Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 31 Aug 2024 19:21:12 -0500 Subject: [PATCH 30/56] lvdim: fix 3D regression from perf tuning --- src/reference_interpolation.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index b7917aa7..87203b0d 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -386,13 +386,13 @@ end function boundarynd(::Type{T}, els, msh) where {T} bdi = Inti.boundary_idxs(T) - nedges = length(els)*length(bdi[1]) - edgelist = Vector{SVector{3, Int64}}(undef, nedges) - edgelist_unsrt = Vector{SVector{3,Int64}}(undef, nedges) - bords = Vector{MVector{3,Int64}}(undef, length(bdi[1])) + nedges = length(els)*length(bdi) + edgelist = Vector{SVector{length(bdi[1]), Int64}}(undef, nedges) + edgelist_unsrt = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) + bords = Vector{MVector{length(bdi[1]),Int64}}(undef, length(bdi)) j = 1 for ii in els - for k in 1:length(bdi[1]) + for k in 1:length(bdi) bords[k] = [Inti.connectivity(msh, T)[i, ii] for i in bdi[k]] end for q in bords @@ -414,7 +414,7 @@ function boundarynd(::Type{T}, els, msh) where {T} i += 1 end if !isequal(edgelist[I[end-1]], edgelist[I[end]]) - push!(uniqlist, I[i]) + push!(uniqlist, I[end]) end return edgelist_unsrt[uniqlist] end From 21727be2cb662c01434e4c1a9ab100a539bf02c2 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 31 Aug 2024 19:41:26 -0500 Subject: [PATCH 31/56] lvdim: fix type instability for 3D --- src/reference_integration.jl | 8 ++++++-- src/reference_interpolation.jl | 4 ++-- src/vdim.jl | 17 +++++++++-------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/reference_integration.jl b/src/reference_integration.jl index 27f23f8c..435f98df 100644 --- a/src/reference_integration.jl +++ b/src/reference_integration.jl @@ -227,12 +227,16 @@ struct VioreanuRokhlin{D,N} <: ReferenceQuadrature{D} domain == :tetrahedron && (domain = ReferenceTetrahedron()) if domain isa ReferenceTriangle if !haskey(TRIANGLE_VR_ORDER_TO_NPTS, order) - error("VioreanuRokhlin quadrature of order $order not available for ReferenceTriangle") + error( + "VioreanuRokhlin quadrature of order $order not available for ReferenceTriangle", + ) end n = TRIANGLE_VR_ORDER_TO_NPTS[order] elseif domain isa ReferenceTetrahedron if !haskey(TETRAHEDRON_VR_ORDER_TO_NPTS, order) - error("VioreanuRokhlin quadrature of order $order not available for ReferenceTetrahedron") + error( + "VioreanuRokhlin quadrature of order $order not available for ReferenceTetrahedron", + ) end n = TETRAHEDRON_VR_ORDER_TO_NPTS[order] else diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 87203b0d..47d86888 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -386,8 +386,8 @@ end function boundarynd(::Type{T}, els, msh) where {T} bdi = Inti.boundary_idxs(T) - nedges = length(els)*length(bdi) - edgelist = Vector{SVector{length(bdi[1]), Int64}}(undef, nedges) + nedges = length(els) * length(bdi) + edgelist = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) edgelist_unsrt = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) bords = Vector{MVector{length(bdi[1]),Int64}}(undef, length(bdi)) j = 1 diff --git a/src/vdim.jl b/src/vdim.jl index 4d5a930e..c1a669bd 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -343,8 +343,14 @@ function translation_and_scaling(el::LagrangeTetrahedron) return center, R end +# function barrier for type stability purposes function newbord_line(vtxs) - return Inti.LagrangeLine(SVector{3}(vtxs)) + return LagrangeLine(SVector{3}(vtxs)) +end + +# function barrier for type stability purposes +function newbord_tri(vtxs) + return LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) end function _local_vdim_auxiliary_quantities( @@ -372,8 +378,6 @@ function _local_vdim_auxiliary_quantities( loc_bdry = Inti.boundarynd(T, els_idxs, mesh) # TODO handle curved boundary of Γ?? - #bords = typeof(Inti.LagrangeLine(Inti.nodes(mesh)[first(loc_bdry)]...))[] - # TODO possible performance improvement over prev line if N == 2 bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N - 1},3,SVector{N,Float64}}[] else @@ -381,14 +385,11 @@ function _local_vdim_auxiliary_quantities( end for idxs in loc_bdry - # TODO possible performance improvement - #vtxs = SVector{3, SVector{2, Float64}}(Inti.nodes(mesh)[idxs]) - #bord = Inti.LagrangeLine(vtxs) vtxs = Inti.nodes(mesh)[idxs] - if N === 2 + if N == 2 bord = newbord_line(vtxs) else - bord = Inti.LagrangeElement{Inti.ReferenceSimplex{N - 1}}(vtxs...) + bord = newbord_tri(vtxs) end push!(bords, bord) end From f5df86d755600d683e688967dc53dcb9cf3abb1e Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 31 Aug 2024 22:39:21 -0500 Subject: [PATCH 32/56] lvdim: only use VR if we are doing volume stuff, not boundary quadrature --- src/quadrature.jl | 6 +++++- src/vdim.jl | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index 159bed87..8491e30e 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -137,7 +137,11 @@ function Quadrature( scale::Float64 = 1.0, ) where {N,T,E} if domain(E) isa Inti.ReferenceTriangle - Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) + if N == 2 + Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) + else + Q = Inti.Gauss(; domain = :triangle, order = qorder) + end etype2qrule = Dict(E => Q) elseif domain(E) isa Inti.ReferenceTetrahedron Q = Inti.VioreanuRokhlin(; domain = :tetrahedron, order = qorder) diff --git a/src/vdim.jl b/src/vdim.jl index c1a669bd..c91a511a 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -413,7 +413,7 @@ function _local_vdim_auxiliary_quantities( need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam / 2)) > 0 # build O(h) volume neighbors - bdry_qorder = 2 * quadrature_order + 1 + bdry_qorder = 2 * quadrature_order Yvol = Inti.Quadrature(mesh, els_list; qorder = quadrature_order, center = center, scale) if need_layer_corr From 8471cdb8fdf656a312727ab5db9db9dcb61145cb Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 2 Sep 2024 11:25:14 -0500 Subject: [PATCH 33/56] lvdim Polynomial evaluator test --- test/Project.toml | 2 ++ test/poly_test.jl | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 test/poly_test.jl diff --git a/test/Project.toml b/test/Project.toml index 1d2e8bf6..f0a1d34b 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,7 +1,9 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07" FMM2D = "2d63477d-9690-4b75-bcc1-c3461d43fecc" FMM3D = "1e13804c-f9b7-11ea-0ef0-29f3b1745df8" +FixedPolynomials = "3dd14ad9-0029-526e-86e9-8aa0bdd2ab0d" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" HMatrices = "8646bddf-ab1c-4fa7-9c51-ba187d647618" Inti = "fb74042b-437e-4c5b-88cf-d4e2beb394d5" diff --git a/test/poly_test.jl b/test/poly_test.jl new file mode 100644 index 00000000..1d1ce99d --- /dev/null +++ b/test/poly_test.jl @@ -0,0 +1,76 @@ +using Inti +using StaticArrays +using LinearAlgebra +import DynamicPolynomials: @polyvar +using FixedPolynomials + +#Run with npts = 1000000 +function poly_test(npts) + #npts = 10000 + pde = Inti.Laplace(; dim = 2) + interpolation_order = 4 + p, P, γ₁P, multiindices = Inti.polynomial_solutions_vdim(pde, interpolation_order) + + @polyvar x y + + N = 2 + PolArray = Array{Polynomial{Float64}}(undef, length(P)) + + tsetup = @elapsed begin + for (polind, ElemPolySolsPols) in enumerate(P) + pp = ElemPolySolsPols.f.order2coeff + exp_data = Matrix{Int64}(undef, N, length(pp)) + coeff_data = Vector{Float64}(undef, length(pp)) + for (i, pol) in enumerate(pp) + exp_data[:, i] = [q for q in pol[1]] + coeff_data[i] = pol[2] + end + PolArray[polind] = Polynomial(exp_data, coeff_data, [:x, :y]) + end + PolSystem = System(PolArray) + pts = Vector{Vector{Float64}}(undef, npts) + pts[1] = [0, 0] + cfg = JacobianConfig(PolSystem, pts[1]) + end + @info "FixedPolynomials.jl setup time: $tsetup" + + pts = Vector{Vector{Float64}}(undef, npts) + for i in 1:npts + pts[i] = rand(2) + end + cfg = JacobianConfig(PolSystem, pts[1]) + res1 = Matrix{Float64}(undef, length(PolArray), length(pts)) + res2 = Matrix{Float64}(undef, length(PolArray), length(pts)) + res3 = Vector{MVector{length(PolArray),Float64}}(undef, npts) + + cfg = JacobianConfig(PolSystem, pts[1]) + u = Vector{Float64}(undef, length(PolArray)) + tfixed = @elapsed begin + for i in 1:npts + evaluate!(view(res1, :, i), PolSystem, pts[i], cfg) + end + end + @info "FixedPolynomials.jl time: $tfixed" + evaluator = (xx) -> evaluate(PolSystem, xx, cfg) + tbroadcast = @elapsed begin + res3 .= evaluator.(pts) + end + @info "FixedPolynomials.jl w/ broadcast time: $tbroadcast" + tregular = @elapsed begin + for i in 1:length(P) + res2[i, :] .= P[i].f.(pts) + end + end + @info "ElementaryPDESolutions.jl time: $tregular" + + # Evaluate Jacobian + u = Vector{Float64}(undef, length(P)) + U = Matrix{Float64}(undef, length(P), 2) + tjacob = @elapsed begin + for i in 1:npts + evaluate_and_jacobian!(u, U, PolSystem, pts[i], cfg) + end + end + @info "FixedPolynomials.jl Jacobian+Eval time: $tjacob" + return res1, res2, res3, tfixed, tregular, tbroadcast, tjacob +end From 78d6b048ef86368bbf77bf9d6a9c983f2065583f Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 2 Sep 2024 11:28:17 -0500 Subject: [PATCH 34/56] cleanup --- src/quadrature.jl | 2 ++ src/vdim.jl | 13 ++++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index 8491e30e..f245ddad 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -137,8 +137,10 @@ function Quadrature( scale::Float64 = 1.0, ) where {N,T,E} if domain(E) isa Inti.ReferenceTriangle + # Local VDIM volume quad if N == 2 Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) + # layer potential quadrature in 3D else Q = Inti.Gauss(; domain = :triangle, order = qorder) end diff --git a/src/vdim.jl b/src/vdim.jl index c91a511a..2028bc51 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -344,14 +344,10 @@ function translation_and_scaling(el::LagrangeTetrahedron) end # function barrier for type stability purposes -function newbord_line(vtxs) - return LagrangeLine(SVector{3}(vtxs)) -end +_newbord_line(vtxs) = LagrangeLine(SVector{3}(vtxs)) # function barrier for type stability purposes -function newbord_tri(vtxs) - return LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) -end +_newbord_tri(vtxs) = LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) function _local_vdim_auxiliary_quantities( pde::AbstractPDE{N}, @@ -387,9 +383,9 @@ function _local_vdim_auxiliary_quantities( for idxs in loc_bdry vtxs = Inti.nodes(mesh)[idxs] if N == 2 - bord = newbord_line(vtxs) + bord = _newbord_line(vtxs) else - bord = newbord_tri(vtxs) + bord = _newbord_tri(vtxs) end push!(bords, bord) end @@ -425,7 +421,6 @@ function _local_vdim_auxiliary_quantities( # TODO handle derivative case G = SingleLayerKernel(pde) dG = DoubleLayerKernel(pde) - Xcoords = [q.coords for q in X] Xshift = [(q.coords - center) / scale for q in X] Sop = IntegralOperator(G, Xshift, Ybdry) Dop = IntegralOperator(dG, Xshift, Ybdry) From ca342a3c0fce2d3a69ccd7f45791077a2295bc77 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 6 Sep 2024 18:23:41 -0500 Subject: [PATCH 35/56] Use `Fastpolynomials.jl` extension in `ElementaryPDESolutions.jl` for efficient Polynomial evaluation --- src/reference_integration.jl | 11 +++-- src/vdim.jl | 95 ++++++++++++++++++++++++++++-------- test/lvdim_test.jl | 3 ++ test/poly_test.jl | 10 ++-- 4 files changed, 91 insertions(+), 28 deletions(-) diff --git a/src/reference_integration.jl b/src/reference_integration.jl index 435f98df..cb486ce3 100644 --- a/src/reference_integration.jl +++ b/src/reference_integration.jl @@ -124,16 +124,19 @@ struct Gauss{D,N} <: ReferenceQuadrature{D} domain == :segment && (domain = ReferenceLine()) domain == :triangle && (domain = ReferenceTriangle()) domain == :tetrehedron && (domain = ReferenceTetrahedron()) - msg = "quadrature of order $order not available for $domain" if domain isa ReferenceLine # TODO: support Gauss-Legendre quadratures of arbitrary order - order == 13 || error(msg) + order == 13 || error("quadrature of order $order not available for $domain") n = 7 elseif domain isa ReferenceTriangle - haskey(TRIANGLE_GAUSS_ORDER_TO_NPTS, order) || error(msg) + if !haskey(TRIANGLE_GAUSS_ORDER_TO_NPTS, order) + error("quadrature of order $order not available for $domain") + end n = TRIANGLE_GAUSS_ORDER_TO_NPTS[order] elseif domain isa ReferenceTetrahedron - haskey(TETRAHEDRON_GAUSS_ORDER_TO_NPTS, order) || error(msg) + if !haskey(TETRAHEDRON_GAUSS_ORDER_TO_NPTS, order) + error("quadrature of order $order not available for $domain") + end n = TETRAHEDRON_GAUSS_ORDER_TO_NPTS[order] else error( diff --git a/src/vdim.jl b/src/vdim.jl index 2028bc51..b66bbce1 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -130,8 +130,11 @@ function vdim_correction( end # function barrier for type stability purposes -function build_vander(pts, p, c, r) - return [f((q.coords - c) / r) for q in pts, f in p] +function build_vander(vals_trg, pts, PFE_p, c, r) + coords_trg_vec = collect((Vector((q.coords - c) / r)) for q in pts) + ElementaryPDESolutions.fast_evaluate!(vals_trg, coords_trg_vec, PFE_p) + return vals_trg + #return [f((q.coords - c) / r) for q in pts, f in p] end function local_vdim_correction( @@ -158,15 +161,13 @@ function local_vdim_correction( # a reasonable interpolation_order if not provided isnothing(interpolation_order) && (interpolation_order = maximum(order, values(source.etype2qrule))) - # by default basis centered at origin - p, P, γ₁P, multiindices = polynomial_solutions_vdim(pde, interpolation_order) + PFE_p, PFE_P = polynomial_solutions_local_vdim(pde, interpolation_order) dict_near = etype_to_nearest_points(target, source; maxdist) bdry_kdtree = KDTree(bdry_nodes) # compute sparse correction Is = Int[] Js = Int[] Vs = Eltype[] - L̃ = Matrix{Float64}(undef, 21, 15) for (E, qtags) in source.etype2qtags els = elements(source.mesh, E) near_list = dict_near[E] @@ -175,8 +176,13 @@ function local_vdim_correction( sizehint!(Is, ne * nq * nq) sizehint!(Js, ne * nq * nq) sizehint!(Vs, ne * nq * nq) + num_basis = binomial(interpolation_order + N, N) + L̃ = Matrix{Float64}(undef, nq, num_basis) + vals_trg = Matrix{Float64}(undef, num_basis, nq) + topo_neighs = 1 neighbors = Inti.topological_neighbors(mesh, topo_neighs) + for n in 1:ne # indices of nodes in element `n` isempty(near_list[n]) && continue @@ -196,9 +202,8 @@ function local_vdim_correction( c, r, quadrature_order, - p, - P, - γ₁P, + PFE_p, + PFE_P, target[near_list[n]], green_multiplier, bdry_kdtree; @@ -206,7 +211,7 @@ function local_vdim_correction( jglob = @view qtags[:, n] # compute translation and scaling if SHIFT - L̃ = build_vander(view(source, jglob), p, c, r) + L̃ .= transpose(build_vander(vals_trg, view(source, jglob), PFE_p, c, r)) Linv = pinv(L̃) wei = transpose(Linv) * transpose(R) else @@ -357,9 +362,8 @@ function _local_vdim_auxiliary_quantities( center, scale, quadrature_order, - p, - P, - γ₁P, + PFE_p, + PFE_P, X, μ, bdry_kdtree; @@ -445,19 +449,39 @@ function _local_vdim_auxiliary_quantities( Dmat += δD end - num_basis = length(p) + num_basis = length(PFE_P) num_targets = length(X) - b = [f(q) for q in Yvol, f in p] - γ₀B = [f(q) for q in Ybdry, f in P] - γ₁B = [f(q) for q in Ybdry, f in γ₁P] + #b = [f(q) for q in Yvol, f in p] + b = Matrix{Float64}(undef, num_basis, length(Yvol)) + γ₁B = Matrix{Float64}(undef, length(Ybdry), num_basis) + γ₀B = Matrix{Float64}(undef, num_basis, length(Ybdry)) + P = Matrix{Float64}(undef, num_basis, length(X)) + grad = Array{Float64}(undef, num_basis, N, length(Ybdry)) + coords_trg_vec = collect((Vector(q) for q in Xshift)) + coords_bdry_vec = collect((Vector(q.coords) for q in Ybdry)) + coords_vol_vec = collect((Vector(q.coords) for q in Yvol)) + nrml_bdry_vec = collect(Vector(q.normal) for q in Ybdry) + + ElementaryPDESolutions.fast_evaluate!(b, coords_vol_vec, PFE_p) + ElementaryPDESolutions.fast_evaluate!(P, coords_trg_vec, PFE_P) + ElementaryPDESolutions.fast_evaluate_with_jacobian!(γ₀B, grad, coords_bdry_vec, PFE_P) + for i in 1:length(Ybdry) + for j in 1:num_basis + γ₁B[i, j] = 0 + for k = 1:N + γ₁B[i, j] += grad[j, k, i] * nrml_bdry_vec[i][k] + end + end + end + Θ = zeros(eltype(Vop), num_targets, num_basis) # Compute Θ <-- S * γ₁B - D * γ₀B - V * b + σ * B(x) using in-place matvec for n in 1:num_basis @views mul!(Θ[:, n], Smat, γ₁B[:, n]) - @views mul!(Θ[:, n], Dmat, γ₀B[:, n], -1, 1) - @views mul!(Θ[:, n], Vmat, b[:, n], -1, 1) + @views mul!(Θ[:, n], Dmat, γ₀B[n, :], -1, 1) + @views mul!(Θ[:, n], Vmat, b[n, :], -1, 1) for i in 1:num_targets - Θ[i, n] += μ[i] * P[n](Xshift[i]) + Θ[i, n] += μ[i] * P[n,i] end end return Θ @@ -514,6 +538,39 @@ function vdim_mesh_center(msh::AbstractMesh) end return xc / M end +""" + polynomial_solutions_local_vdim(pde, order) + +For every monomial term `pₙ` of degree `order`, compute a polynomial `Pₙ` such +that `ℒ[Pₙ] = pₙ`, where `ℒ` is the differential operator associated with `pde`. +This function returns `{pₙ,Pₙ,γ₁Pₙ}`, where `γ₁Pₙ` is the generalized Neumann +trace of `Pₙ`. +""" +function polynomial_solutions_local_vdim(pde::AbstractPDE, order::Integer) + N = ambient_dimension(pde) + # create empty arrays to store the monomials, solutions, and traces. For the + # neumann trace, we try to infer the concrete return type instead of simply + # having a vector of `Function`. + monomials = Vector{ElementaryPDESolutions.Polynomial{N,Float64}}() + poly_solutions = Vector{ElementaryPDESolutions.Polynomial{N,Float64}}() + multiindices = Vector{MultiIndex{N}}() + # iterate over N-tuples going from 0 to order + for I in Iterators.product(ntuple(i -> 0:order, N)...) + sum(I) > order && continue + # define the monomial basis functions, and the corresponding solutions. + # TODO: adapt this to vectorial case + p = ElementaryPDESolutions.Polynomial(I => 1 / factorial(MultiIndex(I))) + P = polynomial_solution(pde, p) + push!(multiindices, MultiIndex(I)) + push!(monomials, p) + push!(poly_solutions, P) + end + + PFE_monomials = ElementaryPDESolutions.assemble_fastevaluator(monomials, Float64) + PFE_polysolutions = ElementaryPDESolutions.assemble_fastevaluator(poly_solutions, Float64) + + return PFE_monomials, PFE_polysolutions +end """ polynomial_solutions_vdim(pde, order[, center]) diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 08c391d9..f06efc8d 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -1,5 +1,8 @@ # # Testing local vdim +using DynamicPolynomials +using FixedPolynomials +using ElementaryPDESolutions using Inti using StaticArrays using Gmsh diff --git a/test/poly_test.jl b/test/poly_test.jl index 1d1ce99d..e0034d2f 100644 --- a/test/poly_test.jl +++ b/test/poly_test.jl @@ -14,7 +14,7 @@ function poly_test(npts) @polyvar x y N = 2 - PolArray = Array{Polynomial{Float64}}(undef, length(P)) + PolArray = Array{FixedPolynomials.Polynomial{Float64}}(undef, length(P)) tsetup = @elapsed begin for (polind, ElemPolySolsPols) in enumerate(P) @@ -25,7 +25,7 @@ function poly_test(npts) exp_data[:, i] = [q for q in pol[1]] coeff_data[i] = pol[2] end - PolArray[polind] = Polynomial(exp_data, coeff_data, [:x, :y]) + PolArray[polind] = FixedPolynomials.Polynomial(exp_data, coeff_data, [:x, :y]) end PolSystem = System(PolArray) pts = Vector{Vector{Float64}}(undef, npts) @@ -64,11 +64,11 @@ function poly_test(npts) @info "ElementaryPDESolutions.jl time: $tregular" # Evaluate Jacobian - u = Vector{Float64}(undef, length(P)) - U = Matrix{Float64}(undef, length(P), 2) + u = Matrix{Float64}(undef, length(P), npts) + U = Array{Float64}(undef, length(P), 2, npts) tjacob = @elapsed begin for i in 1:npts - evaluate_and_jacobian!(u, U, PolSystem, pts[i], cfg) + evaluate_and_jacobian!(view(u, :, i), view(U, :, :, i), PolSystem, pts[i], cfg) end end @info "FixedPolynomials.jl Jacobian+Eval time: $tjacob" From 1657ff23026110659d6b7e46fff41f3b83138fcb Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 6 Sep 2024 18:56:23 -0500 Subject: [PATCH 36/56] tweak problematic bits to comprehensions --- src/vdim.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index b66bbce1..f77fc805 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -131,7 +131,7 @@ end # function barrier for type stability purposes function build_vander(vals_trg, pts, PFE_p, c, r) - coords_trg_vec = collect((Vector((q.coords - c) / r)) for q in pts) + coords_trg_vec = [Vector((q.coords - c) / r) for q in pts] ElementaryPDESolutions.fast_evaluate!(vals_trg, coords_trg_vec, PFE_p) return vals_trg #return [f((q.coords - c) / r) for q in pts, f in p] @@ -457,10 +457,10 @@ function _local_vdim_auxiliary_quantities( γ₀B = Matrix{Float64}(undef, num_basis, length(Ybdry)) P = Matrix{Float64}(undef, num_basis, length(X)) grad = Array{Float64}(undef, num_basis, N, length(Ybdry)) - coords_trg_vec = collect((Vector(q) for q in Xshift)) - coords_bdry_vec = collect((Vector(q.coords) for q in Ybdry)) - coords_vol_vec = collect((Vector(q.coords) for q in Yvol)) - nrml_bdry_vec = collect(Vector(q.normal) for q in Ybdry) + coords_trg_vec = [Vector(q) for q in Xshift] + coords_bdry_vec = [Vector(q.coords) for q in Ybdry] + coords_vol_vec = [Vector(q.coords) for q in Yvol] + nrml_bdry_vec = [Vector(q.normal) for q in Ybdry] ElementaryPDESolutions.fast_evaluate!(b, coords_vol_vec, PFE_p) ElementaryPDESolutions.fast_evaluate!(P, coords_trg_vec, PFE_P) From 801e2c5e1ff1996042705887779d7cb9bbf46292 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 7 Sep 2024 20:09:14 -0500 Subject: [PATCH 37/56] perf tweaks --- src/vdim.jl | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index f77fc805..2c091344 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -131,8 +131,13 @@ end # function barrier for type stability purposes function build_vander(vals_trg, pts, PFE_p, c, r) - coords_trg_vec = [Vector((q.coords - c) / r) for q in pts] - ElementaryPDESolutions.fast_evaluate!(vals_trg, coords_trg_vec, PFE_p) + #coords_trg_vec = [Vector((q.coords - c) / r) for q in pts] + #ElementaryPDESolutions.fast_evaluate!(vals_trg, coords_trg_vec, PFE_p) + tmp = Vector{Float64}(undef, length(c)) + for i in 1:length(pts) + tmp .= (pts[i].coords - c ) / r + ElementaryPDESolutions.fast_evaluate!(view(vals_trg, :, i), tmp, PFE_p) + end return vals_trg #return [f((q.coords - c) / r) for q in pts, f in p] end @@ -452,24 +457,30 @@ function _local_vdim_auxiliary_quantities( num_basis = length(PFE_P) num_targets = length(X) #b = [f(q) for q in Yvol, f in p] - b = Matrix{Float64}(undef, num_basis, length(Yvol)) + b = Matrix{Float64}(undef, length(Yvol), num_basis) γ₁B = Matrix{Float64}(undef, length(Ybdry), num_basis) - γ₀B = Matrix{Float64}(undef, num_basis, length(Ybdry)) - P = Matrix{Float64}(undef, num_basis, length(X)) + γ₀B = Matrix{Float64}(undef, length(Ybdry), num_basis) + P = Matrix{Float64}(undef, length(X), num_basis) grad = Array{Float64}(undef, num_basis, N, length(Ybdry)) - coords_trg_vec = [Vector(q) for q in Xshift] - coords_bdry_vec = [Vector(q.coords) for q in Ybdry] - coords_vol_vec = [Vector(q.coords) for q in Yvol] - nrml_bdry_vec = [Vector(q.normal) for q in Ybdry] - - ElementaryPDESolutions.fast_evaluate!(b, coords_vol_vec, PFE_p) - ElementaryPDESolutions.fast_evaluate!(P, coords_trg_vec, PFE_P) - ElementaryPDESolutions.fast_evaluate_with_jacobian!(γ₀B, grad, coords_bdry_vec, PFE_P) + #coords_trg_vec = [Vector(q) for q in Xshift] + #coords_bdry_vec = [Vector(q.coords) for q in Ybdry] + #coords_vol_vec = [Vector(q.coords) for q in Yvol] + #nrml_bdry_vec = [Vector(q.normal) for q in Ybdry] + + for i in 1:length(Yvol) + ElementaryPDESolutions.fast_evaluate!(view(b, i, :), PFEYvol[i].coords,_p) + end + for i in 1:length(X) + ElementaryPDESolutions.fast_evaluate!(view(P, i, :), Xshift[i], PFE_P) + end + for i in 1:length(Ybdry) + ElementaryPDESolutions.fast_evaluate_with_jacobian!(view(γ₀B, i, :), view(grad, :, :, i), Ybdry[i].coords, PFE_P) + end for i in 1:length(Ybdry) for j in 1:num_basis γ₁B[i, j] = 0 for k = 1:N - γ₁B[i, j] += grad[j, k, i] * nrml_bdry_vec[i][k] + γ₁B[i, j] += grad[j, k, i] * Ybdry[i].normal[k]#nrml_bdry_vec[i][k] end end end @@ -478,10 +489,10 @@ function _local_vdim_auxiliary_quantities( # Compute Θ <-- S * γ₁B - D * γ₀B - V * b + σ * B(x) using in-place matvec for n in 1:num_basis @views mul!(Θ[:, n], Smat, γ₁B[:, n]) - @views mul!(Θ[:, n], Dmat, γ₀B[n, :], -1, 1) - @views mul!(Θ[:, n], Vmat, b[n, :], -1, 1) + @views mul!(Θ[:, n], Dmat, γ₀B[:, n], -1, 1) + @views mul!(Θ[:, n], Vmat, b[:, n], -1, 1) for i in 1:num_targets - Θ[i, n] += μ[i] * P[n,i] + Θ[i, n] += μ[i] * P[i,n] end end return Θ From 49fb9136a80f792a20bfad61d950b88fe922b198 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 8 Sep 2024 15:08:55 -0500 Subject: [PATCH 38/56] Fix blooper --- src/vdim.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 2c091344..ba09dd11 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -131,15 +131,12 @@ end # function barrier for type stability purposes function build_vander(vals_trg, pts, PFE_p, c, r) - #coords_trg_vec = [Vector((q.coords - c) / r) for q in pts] - #ElementaryPDESolutions.fast_evaluate!(vals_trg, coords_trg_vec, PFE_p) tmp = Vector{Float64}(undef, length(c)) for i in 1:length(pts) tmp .= (pts[i].coords - c ) / r ElementaryPDESolutions.fast_evaluate!(view(vals_trg, :, i), tmp, PFE_p) end return vals_trg - #return [f((q.coords - c) / r) for q in pts, f in p] end function local_vdim_correction( @@ -468,7 +465,7 @@ function _local_vdim_auxiliary_quantities( #nrml_bdry_vec = [Vector(q.normal) for q in Ybdry] for i in 1:length(Yvol) - ElementaryPDESolutions.fast_evaluate!(view(b, i, :), PFEYvol[i].coords,_p) + ElementaryPDESolutions.fast_evaluate!(view(b, i, :), Yvol[i].coords, PFE_p) end for i in 1:length(X) ElementaryPDESolutions.fast_evaluate!(view(P, i, :), Xshift[i], PFE_P) From 7e9915896b00c0d95210d7a359c933477a4bc55a Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Tue, 10 Sep 2024 10:39:17 -0500 Subject: [PATCH 39/56] `Bessels.jl` provides faster routines than `SpecialFunctions.jl` --- Project.toml | 1 + src/Inti.jl | 1 + src/kernels.jl | 10 +++++----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index a5655c27..a1b0e5fb 100644 --- a/Project.toml +++ b/Project.toml @@ -3,6 +3,7 @@ uuid = "fb74042b-437e-4c5b-88cf-d4e2beb394d5" version = "0.1.1" [deps] +Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ElementaryPDESolutions = "88a69b33-da0f-4502-8c8c-d680cf4d883b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" diff --git a/src/Inti.jl b/src/Inti.jl index b0f936e4..8ab3b14a 100644 --- a/src/Inti.jl +++ b/src/Inti.jl @@ -17,6 +17,7 @@ using Scratch using SparseArrays using StaticArrays using SpecialFunctions +using Bessels using Printf using TOML diff --git a/src/kernels.jl b/src/kernels.jl index fa3ede3b..78431248 100644 --- a/src/kernels.jl +++ b/src/kernels.jl @@ -224,7 +224,7 @@ function (SL::SingleLayerKernel{T,<:Helmholtz{N}})(target, source)::T where {N,T d = norm(r) filter = !(d ≤ SAME_POINT_TOLERANCE) if N == 2 - return filter * (im / 4 * hankelh1(0, k * d)) + return filter * (im / 4 * Bessels.hankelh1(0, k * d)) elseif N == 3 return filter * (1 / (4π) / d * exp(im * k * d)) end @@ -238,7 +238,7 @@ function (DL::DoubleLayerKernel{T,<:Helmholtz{N}})(target, source)::T where {N,T d = norm(r) filter = !(d ≤ SAME_POINT_TOLERANCE) if N == 2 - val = im * k / 4 / d * hankelh1(1, k * d) .* dot(r, ny) + val = im * k / 4 / d * Bessels.hankelh1(1, k * d) .* dot(r, ny) return filter * val elseif N == 3 val = 1 / (4π) / d^2 * exp(im * k * d) * (-im * k + 1 / d) * dot(r, ny) @@ -254,7 +254,7 @@ function (ADL::AdjointDoubleLayerKernel{T,<:Helmholtz{N}})(target, source)::T wh d = norm(r) filter = !(d ≤ SAME_POINT_TOLERANCE) if N == 2 - val = -im * k / 4 / d * hankelh1(1, k * d) .* dot(r, nx) + val = -im * k / 4 / d * Bessels.hankelh1(1, k * d) .* dot(r, nx) return filter * val elseif N == 3 val = -1 / (4π) / d^2 * exp(im * k * d) * (-im * k + 1 / d) * dot(r, nx) @@ -276,8 +276,8 @@ function (HS::HyperSingularKernel{T,S})(target, source)::T where {T,S<:Helmholtz val = transpose(nx) * ( ( - -im * k^2 / 4 / d^2 * hankelh1(2, k * d) * RRT + - im * k / 4 / d * hankelh1(1, k * d) * I + -im * k^2 / 4 / d^2 * Bessels.hankelh1(2, k * d) * RRT + + im * k / 4 / d * Bessels.hankelh1(1, k * d) * I ) * ny ) return filter * val From 354d71a805312ef1f5c2b020efb113ffae207704 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Wed, 11 Sep 2024 15:43:55 -0500 Subject: [PATCH 40/56] lvdim: perf tweaks --- src/mesh.jl | 6 +++++- src/nystrom.jl | 2 +- src/reference_interpolation.jl | 7 ++++++- src/vdim.jl | 5 ----- test/lvdim_test.jl | 1 - test/lvdim_test_3d.jl | 9 ++++----- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/mesh.jl b/src/mesh.jl index 36d9d901..da359bc0 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -677,7 +677,11 @@ function topological_neighbors(msh::AbstractMesh, k = 1) end end # Recursively compute the neighbors from the one-neighbors - k_neighbors = deepcopy(one_neighbors) + if k > 1 + k_neighbors = deepcopy(one_neighbors) + else + k_neighbors = one_neighbors + end while k > 1 # update neighborhood of each element for el in keys(one_neighbors) diff --git a/src/nystrom.jl b/src/nystrom.jl index e80fc337..b6ccb4fa 100644 --- a/src/nystrom.jl +++ b/src/nystrom.jl @@ -95,7 +95,7 @@ end @noinline function _assemble_matrix!(out, K, X, Y::Quadrature, threads) @usethreads threads for j in 1:length(Y) for i in 1:length(X) - out[i, j] = K(X[i], Y[j]) * weight(Y[j]) + @inbounds out[i, j] = K(X[i], Y[j]) * weight(Y[j]) end end return out diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 47d86888..681ac639 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -390,10 +390,15 @@ function boundarynd(::Type{T}, els, msh) where {T} edgelist = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) edgelist_unsrt = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) bords = Vector{MVector{length(bdi[1]),Int64}}(undef, length(bdi)) + for i in 1:length(bdi) + bords[i] = MVector{length(bdi[1]), Int64}(undef) + end j = 1 for ii in els for k in 1:length(bdi) - bords[k] = [Inti.connectivity(msh, T)[i, ii] for i in bdi[k]] + for jjj in 1:length(bdi[k]) + bords[k][jjj] = Inti.connectivity(msh, T)[bdi[k][jjj], ii] + end end for q in bords edgelist_unsrt[j] = q[:] diff --git a/src/vdim.jl b/src/vdim.jl index ba09dd11..d62875a8 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -453,16 +453,11 @@ function _local_vdim_auxiliary_quantities( num_basis = length(PFE_P) num_targets = length(X) - #b = [f(q) for q in Yvol, f in p] b = Matrix{Float64}(undef, length(Yvol), num_basis) γ₁B = Matrix{Float64}(undef, length(Ybdry), num_basis) γ₀B = Matrix{Float64}(undef, length(Ybdry), num_basis) P = Matrix{Float64}(undef, length(X), num_basis) grad = Array{Float64}(undef, num_basis, N, length(Ybdry)) - #coords_trg_vec = [Vector(q) for q in Xshift] - #coords_bdry_vec = [Vector(q.coords) for q in Ybdry] - #coords_vol_vec = [Vector(q.coords) for q in Yvol] - #nrml_bdry_vec = [Vector(q.normal) for q in Ybdry] for i in 1:length(Yvol) ElementaryPDESolutions.fast_evaluate!(view(b, i, :), Yvol[i].coords, PFE_p) diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index f06efc8d..80729c62 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -2,7 +2,6 @@ using DynamicPolynomials using FixedPolynomials -using ElementaryPDESolutions using Inti using StaticArrays using Gmsh diff --git a/test/lvdim_test_3d.jl b/test/lvdim_test_3d.jl index 6cfedd94..30e89d61 100644 --- a/test/lvdim_test_3d.jl +++ b/test/lvdim_test_3d.jl @@ -1,5 +1,7 @@ # # High-order convergence of vdim +using DynamicPolynomials +using FixedPolynomials using Inti using Meshes using StaticArrays @@ -9,16 +11,13 @@ using HMatrices using FMM3D using GLMakie -meshsize = 0.1 -interpolation_order = 2 +meshsize = 0.05 +interpolation_order = 4 VR_qorder = Inti.Tetrahedron_VR_interpolation_order_to_quadrature_order(interpolation_order) bdry_qorder = 2 * VR_qorder function gmsh_sphere(; order = 1, name, meshsize) try - if isdefined(Main, :Infiltrator) - Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end gmsh.initialize() gmsh.option.setNumber("General.Terminal", 0) gmsh.model.add("sphere-mesh") From 2dde6746e8f5967204c04d1592021ff4ae58f48e Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 21 Sep 2024 17:46:26 -0500 Subject: [PATCH 41/56] Fix bug in triangle center/scale calculation --- src/vdim.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vdim.jl b/src/vdim.jl index d62875a8..bee21ea7 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -271,7 +271,7 @@ function translation_and_scaling(el::LagrangeTriangle) Cp = vertices[3] - vertices[1] Dp = 2 * (Bp[1] * Cp[2] - Bp[2] * Cp[1]) Upx = 1 / Dp * (Cp[2] * (Bp[1]^2 + Bp[2]^2) - Bp[2] * (Cp[1]^2 + Cp[2]^2)) - Upy = 1 / Dp * (Bp[1] * (Cp[1]^2 + Cp[2]^2) - Cp[2] * (Bp[1]^2 + Bp[2]^2)) + Upy = 1 / Dp * (Bp[1] * (Cp[1]^2 + Cp[2]^2) - Cp[1] * (Bp[1]^2 + Bp[2]^2)) Up = SVector{2}(Upx, Upy) r = norm(Up) c = Up + vertices[1] From 9a2eb153658046ad33c4316b1c96ccf5fca9d645 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 21 Sep 2024 18:54:45 -0500 Subject: [PATCH 42/56] lvdim: temporary switch to scaling polynomials --- src/quadrature.jl | 7 ++++--- src/vdim.jl | 11 +++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/quadrature.jl b/src/quadrature.jl index f245ddad..26b60db7 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -190,9 +190,10 @@ end center::SVector{N,Float64} = zero(SVector{N,Float64}), scale::Float64 = 1.0, ) where {E,N,T} - #if isdefined(Main, :Infiltrator) - # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - #end + + # FIXME !! + scale = 1.0 + x̂, ŵ = qrule() # nodes and weights on reference element num_nodes = length(ŵ) M = geometric_dimension(domain(E)) diff --git a/src/vdim.jl b/src/vdim.jl index bee21ea7..6d7a1169 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -163,7 +163,7 @@ function local_vdim_correction( # a reasonable interpolation_order if not provided isnothing(interpolation_order) && (interpolation_order = maximum(order, values(source.etype2qrule))) - PFE_p, PFE_P = polynomial_solutions_local_vdim(pde, interpolation_order) + PFE_p, PFE_P, multiindices = polynomial_solutions_local_vdim(pde, interpolation_order) dict_near = etype_to_nearest_points(target, source; maxdist) bdry_kdtree = KDTree(bdry_nodes) # compute sparse correction @@ -189,9 +189,6 @@ function local_vdim_correction( # indices of nodes in element `n` isempty(near_list[n]) && continue c, r = translation_and_scaling(els[n]) - #c = SVector{2, Float64}(0,0) - # FIXME Why does scaling the whole domain not work? - r = 1.0 if !SHIFT c = SVector{N,Float64}(0, 0) r = 1.0 @@ -215,7 +212,8 @@ function local_vdim_correction( if SHIFT L̃ .= transpose(build_vander(vals_trg, view(source, jglob), PFE_p, c, r)) Linv = pinv(L̃) - wei = transpose(Linv) * transpose(R) + S = Diagonal(1.0./r.^(abs.(multiindices))) + wei = transpose(Linv) * S * transpose(R) else error("unsupported local VDIM without shifting") end @@ -370,6 +368,7 @@ function _local_vdim_auxiliary_quantities( μ, bdry_kdtree; ) where {N} + scale = 1.0 # construct the local region Etype = first(Inti.element_types(mesh)) el_neighs = neighbors[(Etype, el)] @@ -572,7 +571,7 @@ function polynomial_solutions_local_vdim(pde::AbstractPDE, order::Integer) PFE_monomials = ElementaryPDESolutions.assemble_fastevaluator(monomials, Float64) PFE_polysolutions = ElementaryPDESolutions.assemble_fastevaluator(poly_solutions, Float64) - return PFE_monomials, PFE_polysolutions + return PFE_monomials, PFE_polysolutions, multiindices end """ From 7772ca1f014febf14fcf6ebc5c7d15395b2a0044 Mon Sep 17 00:00:00 2001 From: maltezfaria Date: Thu, 17 Oct 2024 11:23:45 +0200 Subject: [PATCH 43/56] add more methods to work on `EntityKey` --- src/entities.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/entities.jl b/src/entities.jl index c26ad7d9..0003d3d4 100644 --- a/src/entities.jl +++ b/src/entities.jl @@ -20,7 +20,14 @@ Base.hash(ent::EntityKey, h::UInt) = hash((ent.dim, abs(ent.tag)), h) Base.:(==)(e1::EntityKey, e2::EntityKey) = e1.dim == e2.dim && abs(e1.tag) == abs(e2.tag) # defer some functions on EntityKey to the corresponding GeometricEntity -for f in (:labels, :boundary, :pushforward, :ambient_dimension) +for f in ( + :labels, + :boundary, + :pushforward, + :ambient_dimension, + :hasparametrization, + :parametrization, +) @eval $f(k::EntityKey) = $f(global_get_entity(k)) end From 0090520c9b96d74a25938dd79e4f425d9f417c0d Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 20 Oct 2024 08:25:39 -0500 Subject: [PATCH 44/56] Use lightweight Quadrature objects (no mesh) for local vdim --- src/nystrom.jl | 6 +++--- src/quadrature.jl | 55 +++++++++++++++++++---------------------------- src/vdim.jl | 33 +++++++++++++++++++--------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/nystrom.jl b/src/nystrom.jl index b6ccb4fa..9fad1835 100644 --- a/src/nystrom.jl +++ b/src/nystrom.jl @@ -46,7 +46,7 @@ I[u](x) = \\int_{\\Gamma\\_s} K(x,y)u(y) ds_y, x \\in \\Gamma_{t} ``` where ``\\Gamma_s`` and ``\\Gamma_t`` are the source and target domains, respectively. """ -struct IntegralOperator{V,K,T,S<:Quadrature} <: AbstractMatrix{V} +struct IntegralOperator{V,K,T,S} <: AbstractMatrix{V} kernel::K # since the target can be as simple as a vector of points, leave it untyped target::T @@ -58,7 +58,7 @@ kernel(iop::IntegralOperator) = iop.kernel target(iop::IntegralOperator) = iop.target source(iop::IntegralOperator) = iop.source -function IntegralOperator(k, X, Y::Quadrature = X) +function IntegralOperator(k, X, Y = X) T = return_type(k, eltype(X), eltype(Y)) # FIXME This cripples performance for local VDIM #msg = """IntegralOperator of nonbits being created: $T""" @@ -92,7 +92,7 @@ function assemble_matrix(iop::IntegralOperator; threads = true) return out end -@noinline function _assemble_matrix!(out, K, X, Y::Quadrature, threads) +@noinline function _assemble_matrix!(out, K, X, Y, threads) @usethreads threads for j in 1:length(Y) for i in 1:length(X) @inbounds out[i, j] = K(X[i], Y[j]) * weight(Y[j]) diff --git a/src/quadrature.jl b/src/quadrature.jl index 26b60db7..5b7fe222 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -58,6 +58,8 @@ function Base.show(io::IO, q::QuadratureNode) return print(io, "-- weight: $(q.weight)") end +const Maybe{T} = Union{T,Nothing} + """ struct Quadrature{N,T} <: AbstractVector{QuadratureNode{N,T}} @@ -65,7 +67,7 @@ A collection of [`QuadratureNode`](@ref)s used to integrate over an [`AbstractMesh`](@ref). """ struct Quadrature{N,T} <: AbstractVector{QuadratureNode{N,T}} - mesh::AbstractMesh{N,T} + mesh::Maybe{AbstractMesh{N,T}} etype2qrule::Dict{DataType,ReferenceQuadrature} qnodes::Vector{QuadratureNode{N,T}} etype2qtags::Dict{DataType,Matrix{Int}} @@ -77,7 +79,10 @@ Base.getindex(quad::Quadrature, i) = quad.qnodes[i] Base.setindex!(quad::Quadrature, q, i) = (quad.qnodes[i] = q) qnodes(quad::Quadrature) = quad.qnodes -mesh(quad::Quadrature) = quad.mesh +function mesh(quad::Quadrature) + isnothing(quad.mesh) && error("The Quadrature has no mesh!") + return quad.mesh +end etype2qtags(quad::Quadrature, E) = quad.etype2qtags[E] quadrature_rule(quad::Quadrature, E) = quad.etype2qrule[E] @@ -130,50 +135,34 @@ end # Quadrature constructor for list of volume elements for local vdim function Quadrature( - msh::AbstractMesh{N,T}, - elementlist::AbstractVector{E}; - qorder, + ::Type{T}, + elementlist::AbstractVector{E}, + etype2qrule::Dict{DataType, Q}, + qrule::Q; center::SVector{N,Float64} = zero(SVector{N,Float64}), scale::Float64 = 1.0, -) where {N,T,E} - if domain(E) isa Inti.ReferenceTriangle - # Local VDIM volume quad - if N == 2 - Q = Inti.VioreanuRokhlin(; domain = :triangle, order = qorder) - # layer potential quadrature in 3D - else - Q = Inti.Gauss(; domain = :triangle, order = qorder) - end - etype2qrule = Dict(E => Q) - elseif domain(E) isa Inti.ReferenceTetrahedron - Q = Inti.VioreanuRokhlin(; domain = :tetrahedron, order = qorder) - etype2qrule = Dict(E => Q) - else - etype2qrule = Dict(E => _qrule_for_reference_shape(domain(E), qorder)) - end - +) where {N,T,E,Q} # initialize mesh with empty fields quad = Quadrature{N,T}( - msh, + nothing, etype2qrule, QuadratureNode{N,T}[], Dict{DataType,Matrix{Int}}(), ) # loop element types and generate quadrature for each - qrule = etype2qrule[E] _build_quadrature!(quad, elementlist, qrule; center, scale) # check for entities with negative orientation and flip normal vectors if # present - for ent in entities(msh) - if (sign(tag(ent)) < 0) && (N - geometric_dimension(ent) == 1) - @debug "Flipping normals of $ent" - tags = dom2qtags(quad, Domain(ent)) - for i in tags - quad[i] = flip_normal(quad[i]) - end - end - end + #for ent in entities(msh) + # if (sign(tag(ent)) < 0) && (N - geometric_dimension(ent) == 1) + # @debug "Flipping normals of $ent" + # tags = dom2qtags(quad, Domain(ent)) + # for i in tags + # quad[i] = flip_normal(quad[i]) + # end + # end + #end return quad end diff --git a/src/vdim.jl b/src/vdim.jl index 6d7a1169..5d86ed15 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -182,6 +182,17 @@ function local_vdim_correction( L̃ = Matrix{Float64}(undef, nq, num_basis) vals_trg = Matrix{Float64}(undef, num_basis, nq) + bdry_qorder = 2 * quadrature_order + if N == 3 + bdry_qrule = _qrule_for_reference_shape(Inti.ReferenceSimplex{2}(), bdry_qorder) + bdry_etype2qrule = Dict(Inti.ReferenceSimplex{2} => bdry_qrule) + else + bdry_qrule = _qrule_for_reference_shape(Inti.ReferenceHyperCube{1}(), bdry_qorder) + bdry_etype2qrule = Dict(Inti.ReferenceHyperCube{1} => bdry_qrule) + end + vol_qrule = VioreanuRokhlin(; domain = domain(E), order = quadrature_order) + vol_etype2qrule = Dict(E => vol_qrule) + topo_neighs = 1 neighbors = Inti.topological_neighbors(mesh, topo_neighs) @@ -205,7 +216,11 @@ function local_vdim_correction( PFE_P, target[near_list[n]], green_multiplier, - bdry_kdtree; + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule; ) jglob = @view qtags[:, n] # compute translation and scaling @@ -366,7 +381,11 @@ function _local_vdim_auxiliary_quantities( PFE_P, X, μ, - bdry_kdtree; + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule; ) where {N} scale = 1.0 # construct the local region @@ -414,14 +433,8 @@ function _local_vdim_auxiliary_quantities( need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam / 2)) > 0 # build O(h) volume neighbors - bdry_qorder = 2 * quadrature_order - Yvol = - Inti.Quadrature(mesh, els_list; qorder = quadrature_order, center = center, scale) - if need_layer_corr - Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder, center = center, scale) - else - Ybdry = Inti.Quadrature(mesh, bords; qorder = bdry_qorder, center = center, scale) - end + Yvol = Inti.Quadrature(Float64, els_list, vol_etype2qrule, vol_qrule; center, scale) + Ybdry = Inti.Quadrature(Float64, bords, bdry_etype2qrule, bdry_qrule; center, scale) # TODO handle derivative case G = SingleLayerKernel(pde) From e45bc1fe5b9aaaef4b9103dca70377e0465890c5 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 20 Oct 2024 08:59:13 -0500 Subject: [PATCH 45/56] Remove ldim code from local-vdim branch --- src/adaptive.jl | 10 +-- src/bdim.jl | 148 --------------------------------- src/mesh.jl | 65 --------------- src/quadrature.jl | 86 ------------------- src/reference_interpolation.jl | 36 -------- test/ldim_test.jl | 76 ----------------- test/nearlist_test.jl | 40 --------- 7 files changed, 5 insertions(+), 456 deletions(-) delete mode 100644 test/ldim_test.jl delete mode 100644 test/nearlist_test.jl diff --git a/src/adaptive.jl b/src/adaptive.jl index a59712f1..558e7970 100644 --- a/src/adaptive.jl +++ b/src/adaptive.jl @@ -42,7 +42,7 @@ function adaptive_correction(iop::IntegralOperator; tol, maxdist = nothing, maxs else maxdist end - dict_near = elements_to_near_targets(X, Y; tol = maxdist) + dict_near = near_interaction_list(X, Y; tol = maxdist) T = eltype(iop) msh = mesh(Y) correction = (I = Int[], J = Int[], V = T[]) @@ -166,14 +166,14 @@ function adaptive_integration_singular(f, τ̂::ReferenceLine, x̂ₛ; kwargs... end end -function elements_to_near_targets(QX::Quadrature, QY::Quadrature; tol) +function near_interaction_list(QX::Quadrature, QY::Quadrature; tol) X = [coords(q) for q in QX] msh = mesh(QY) - return elements_to_near_targets(X, msh; tol) + return near_interaction_list(X, msh; tol) end -function elements_to_near_targets(X, QY::Quadrature; tol) +function near_interaction_list(X, QY::Quadrature; tol) msh = mesh(QY) - return elements_to_near_targets(X, msh; tol) + return near_interaction_list(X, msh; tol) end """ diff --git a/src/bdim.jl b/src/bdim.jl index c1ecdaec..449ca7e8 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -178,151 +178,3 @@ function bdim_correction( return δS, δD end -function local_bdim_correction( - pde, - target, - source::Quadrature; - green_multiplier::Vector{<:Real}, - parameters = DimParameters(), - derivative::Bool = false, - maxdist = Inf, - kneighbor = 1, -) - imat_cond = imat_norm = res_norm = rhs_norm = -Inf - T = default_kernel_eltype(pde) # Float64 - N = ambient_dimension(source) - m, n = length(target), length(source) - msh = source.mesh - qnodes = source.qnodes - neighbors = topological_neighbors(msh, kneighbor) - dict_near = etype_to_nearest_points(target, source; maxdist) - # find first an appropriate set of source points to center the monopoles - qmax = sum(size(mat, 1) for mat in values(source.etype2qtags)) # max number of qnodes per el - ns = ceil(Int, parameters.sources_oversample_factor * qmax) - # compute a bounding box for source points - low_corner = reduce((p, q) -> min.(coords(p), coords(q)), source) - high_corner = reduce((p, q) -> max.(coords(p), coords(q)), source) - xc = (low_corner + high_corner) / 2 - R = parameters.sources_radius_multiplier * norm(high_corner - low_corner) / 2 - xs = if N === 2 - uniform_points_circle(ns, R, xc) - elseif N === 3 - fibonnaci_points_sphere(ns, R, xc) - else - error("only 2D and 3D supported") - end - # figure out if we are dealing with a scalar or vector PDE - σ = if T <: Number - 1 - else - @assert allequal(size(T)) - size(T, 1) - end - # compute traces of monopoles on the source mesh - G = SingleLayerKernel(pde, T) - γ₁G = DoubleLayerKernel(pde, T) - γ₁ₓG = AdjointDoubleLayerKernel(pde, T) - γ₀B = Matrix{T}(undef, length(source), ns) - γ₁B = Matrix{T}(undef, length(source), ns) - for k in 1:ns - for j in 1:length(source) - γ₀B[j, k] = G(source[j], xs[k]) - γ₁B[j, k] = γ₁ₓG(source[j], xs[k]) - end - end - Is, Js, Ss, Ds = Int[], Int[], T[], T[] - for (E, qtags) in source.etype2qtags - els = elements(msh, E) - near_list = dict_near[E] - nq, ne = size(qtags) - @assert length(near_list) == ne - # preallocate a local matrix to store interpolant values resulting - # weights. To benefit from Lapack, we must convert everything to - # matrices of scalars, so when `T` is an `SMatrix` we are careful to - # convert between the `Matrix{<:SMatrix}` and `Matrix{<:Number}` formats - # by viewing the elements of type `T` as `σ × σ` matrices of - # `eltype(T)`. - M_ = Matrix{eltype(T)}(undef, 2 * nq * σ, ns * σ) - W_ = Matrix{eltype(T)}(undef, 2 * nq * σ, σ) - W = T <: Number ? W_ : Matrix{T}(undef, 2 * nq, 1) - Θi_ = Matrix{eltype(T)}(undef, σ, ns * σ) - Θi = T <: Number ? Θi_ : Matrix{T}(undef, 1, ns) - K = derivative ? γ₁ₓG : G - # for each element, we will solve Mᵀ W = Θiᵀ, where W is a vector of - # size 2nq, and Θi is a row vector of length(ns) - for n in 1:ne - # if there is nothing near, skip immediately to next element - isempty(near_list[n]) && continue - el = els[n] - # copy the monopoles/dipoles for the current element - jglob = @view qtags[:, n] - M0 = @view γ₀B[jglob, :] - M1 = @view γ₁B[jglob, :] - _copyto!(view(M_, 1:(nq*σ), :), M0) - _copyto!(view(M_, (nq*σ+1):2*nq*σ, :), M1) - F_ = qr!(transpose(M_)) - @debug (imat_cond = max(cond(M_), imat_cond)) maxlog = 0 - @debug (imat_norm = max(norm(M_), imat_norm)) maxlog = 0 - # quadrature for auxiliary surface. In global dim, this is the same - # as the source quadrature, and independent of element. In local - # dim, this is constructed for each element using its neighbors. - function translate(q::QuadratureNode, x, s) - return QuadratureNode(coords(q) + x, weight(q), s * normal(q)) - end - nei = neighbors[(E, n)] - qtags_nei = Int[] - for (E, m) in nei - append!(qtags_nei, source.etype2qtags[E][:, m]) - end - qnodes_nei = source.qnodes[qtags_nei] - jac = jacobian(el, 0.5) - ν = -_normal(jac) - h = sum(qnodes[i].weight for i in jglob) * 4 - qnodes_op = map(q -> translate(q, h * ν, -1), qnodes_nei) - bindx = boundary1d(nei, msh) - l, r = nodes(msh)[-bindx[1]], nodes(msh)[bindx[2]] - Q, W = gauss(4nq, 0, h) - qnodes_l = [QuadratureNode(l .+ q .* ν, w, SVector(-ν[2], ν[1])) for (q, w) in zip(Q, W)] - qnodes_r = [QuadratureNode(r .+ q .* ν, w, SVector(ν[2], -ν[1])) for (q, w) in zip(Q, W)] - qnodes_aux = append!(qnodes_nei, qnodes_op, qnodes_l, qnodes_r) - # qnodes_aux = source.qnodes # this is the global dim - for i in near_list[n] - # integrate the monopoles/dipoles over the auxiliary surface - # with target x: Θₖ <-- S[γ₁Bₖ](x) - D[γ₀Bₖ](x) + μ * Bₖ(x) - x = target[i] - μ = green_multiplier[i] # - 1/2 - for k in 1:ns - Θi[k] = μ * K(x, xs[k]) - end - for q in qnodes_aux - SK = G(x, q) - DK = γ₁G(x, q) - for k in 1:ns - Θi[k] += (SK * γ₁ₓG(q, xs[k]) - DK * G(q, xs[k])) * weight(q) - end - end - Θi_ = _copyto!(Θi_, Θi) - @debug (rhs_norm = max(rhs_norm, norm(Θi))) maxlog = 0 - W_ = ldiv!(W_, F_, transpose(Θi_)) - @debug (res_norm = max(norm(Matrix(F_) * W_ - transpose(Θi_)), res_norm)) maxlog = - 0 - W = T <: Number ? W_ : _copyto!(W, W_) - for k in 1:nq - push!(Is, i) - push!(Js, jglob[k]) - push!(Ss, -W[nq+k]) # single layer corresponds to α=0,β=-1 - push!(Ds, W[k]) # double layer corresponds to α=1,β=0 - end - end - end - end - @debug """Condition properties of bdim correction: - |-- max interp. matrix cond.: $imat_cond - |-- max interp. matrix norm : $imat_norm - |-- max residual error: $res_norm - |-- max norm of source term: $rhs_norm - """ - δS = sparse(Is, Js, Ss, m, n) - δD = sparse(Is, Js, Ds, m, n) - return δS, δD -end diff --git a/src/mesh.jl b/src/mesh.jl index aa6ee4f7..58858632 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -564,41 +564,6 @@ function connectivity(msh::SubMesh, E::DataType) return map(t -> g2l[t], view(msh.parent.etype2mat[E], :, eltags)) end -""" - elements_to_near_targets(X,Y::AbstractMesh; tol) - -For each element `el` of type `E` in `Y`, return the indices of the points in -`X` which are closer than `tol` to the `center` of `el`. - -This function returns a dictionary where e.g. `dict[E][5] --> Vector{Int}` gives -the indices of points in `X` which are closer than `tol` to the center of the -fifth element of type `E`. - -If `tol` is a `Dict`, then `tol[E]` is the tolerance for elements of type `E`. -""" -function elements_to_near_targets( - X::AbstractVector{<:SVector{N}}, - Y::AbstractMesh{N}; - tol, -) where {N} - @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" - # for each element type, build the list of targets close to a given element - dict = Dict{DataType,Vector{Vector{Int}}}() - balltree = BallTree(X) - for E in element_types(Y) - els = elements(Y, E) - tol_ = isa(tol, Number) ? tol : tol[E] - idxs = _elements_to_near_targets(balltree, els, tol_) - dict[E] = idxs - end - return dict -end - -@noinline function _elements_to_near_targets(balltree, els, tol) - centers = map(center, els) - return inrange(balltree, centers, tol) -end - """ Domain(f::Function, msh::AbstractMesh) @@ -606,36 +571,6 @@ Call `Domain(f, ents)` on `ents = entities(msh).` """ Domain(f::Function, msh::AbstractMesh) = Domain(f, entities(msh)) -""" - target_to_near_elements(X::AbstractVector{<:SVector{N}}, Y::AbstractMesh{N}; - tol) - -For each target `x` in `X`, return a vector of tuples `(E, i)` where `E` is the -type of the element in `Y` and `i` is the index of the element in `Y` such that -`x` is closer than `tol` to the center of the element. -""" -function target_to_near_elements( - X::AbstractVector{<:SVector{N}}, - Y::AbstractMesh{N}; - tol, -) where {N} - @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" - dict = Dict{Int,Vector{Tuple{DataType,Int}}}() - balltree = BallTree(X) - for E in element_types(Y) - els = elements(Y, E) - tol_ = isa(tol, Number) ? tol : tol[E] - idxs = _target_to_near_elements(balltree, els, tol_) - for (i, idx) in enumerate(idxs) - dict[i] = get!(dict, i, Vector{Tuple{DataType,Int}}()) - for j in idx - push!(dict[i], (E, j)) - end - end - end - return dict -end - """ topological_neighbors(msh::LagrangeMesh, k=1) diff --git a/src/quadrature.jl b/src/quadrature.jl index 6f83e4bd..56675660 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -45,8 +45,6 @@ flip_normal(q::QuadratureNode) = QuadratureNode(q.coords, q.weight, -q.normal) weight(q::QuadratureNode) = q.weight -translate(q::QuadratureNode, x) = QuadratureNode(coords(q) + x, weight(q), normal(q)) - # useful for using either a quadrature node or a just a simple point in # `IntegralOperators`. coords(x::Union{SVector,Tuple}) = SVector(x) @@ -339,90 +337,6 @@ function _etype_to_nearest_points(X, Y::Quadrature, maxdist) return etype2nearlist end -""" - etype_to_near_elements(X,Y::Quadrature; tol) - -Return `Nl = [[el in Y.mesh && dist(x, el) ≤ tol] for x in X]` -""" -function etype_to_near_elements(X, Y::Quadrature; tol) - y = [coords(q) for q in Y] - tree = BallTree(y) - etype2nearlist = Dict{DataType,Vector{Set{Int}}}() - for (E, Q) in Y.etype2qtags - P, N = size(Q) - etype2nearlist[E] = source2el = [Set{Int}() for _ in 1:length(X)] - quad2source = [Set{Int}() for _ in 1:length(y)] - for (l, x) in enumerate(X) - for q in inrange(tree, x, tol) - push!(quad2source[q], l) - end - end - for n in 1:N - for i in 1:P - for l in quad2source[Q[i, n]] - push!(source2el[l], n) - end - end - end - end - return etype2nearlist -end - -""" - near_elements(Y::Quadrature; tol) - -Return `Nl = [[el_j in Y.mesh && dist(el_j, el_i) ≤ tol] for el_i in Y.mesh]` -""" -function near_elements(Y::Quadrature; tol) - y = [coords(q) for q in Y] - tree = BallTree(y) - el2el = Dict{Tuple{DataType,Int},Set{Tuple{DataType,Int}}}() - quad2el = [Set{Tuple{DataType,Int}}() for _ in 1:length(y)] - # for each element, loop over its qnodes, find q∈y close to one of its qnodes, add the element to quad2el[q] - # quad2el[q] is the set of elements whose qnodes are close to q - for (E, Q) in Y.etype2qtags - P, N = size(Q) - for n in 1:N - for i in 1:P - for q in inrange(tree, coords(qnodes(Y)[Q[i, n]]), tol) - push!(quad2el[q], (E, n)) - end - end - end - end - # for each element, the set of elements close to it is the - # union of the sets of elements close to its qnodes - for (E, Q) in Y.etype2qtags - P, N = size(Q) - for n in 1:N - el2el[(E, n)] = union([quad2el[Q[i, n]] for i in 1:P]...) - end - end - return el2el -end - -""" - near_components(Y::Quadrature; tol) - -Calculate the connected components of each near_elements -""" -function near_components(Y::Quadrature; tol) - topo_neighbor = topological_neighbors(Y.mesh) - dist_neighbor = near_elements(Y; tol) - return Dict(E => connected_components(nl, topo_neighbor) for (E, nl) in dist_neighbor) -end - -# function _geometric_center_circum_radius(Y::Quadrature, E, Q, P, N) -# C = [(sum(1:P) do i -# coords(qnodes(Y)[Q[i,n]]) .* weight(qnodes(Y)[Q[i,n]]) -# end) / -# (sum(1:P) do i -# weight(qnodes(Y)[Q[i,n]]) -# end) for n in 1:N] -# r = [maximum(i->norm(C[n]-nodes(mesh(Y))[i]), connectivity(mesh(Y), E)[:,n]) for n in 1:N] -# return C, r -# end - """ quadrature_to_node_vals(Q::Quadrature, qvals::AbstractVector) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 412fe8ec..f8ca3524 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -376,19 +376,6 @@ function boundary_idxs(::Type{<:LagrangeTetrahedron{4}}) return (3, 2, 1), (1, 4, 3), (2, 3, 4), (1, 2, 4) end -function boundary1d(els, msh) - res = Set{Int}() - E, _ = first(els) - bdi = Inti.boundary_idxs(E) - for (E, i) in els - vertices = Inti.connectivity(msh, E)[:, i] - for bord in (-vertices[bdi[1]], vertices[bdi[2]]) - -bord in res ? delete!(res, -bord) : push!(res, bord) - end - end - return sort([res...]) -end - function boundarynd(::Type{T}, els, msh) where {T} bdi = Inti.boundary_idxs(T) nedges = length(els) * length(bdi) @@ -429,29 +416,6 @@ function boundarynd(::Type{T}, els, msh) where {T} return edgelist_unsrt[uniqlist] end -function _dfs!(comp, el, nei, els) - for el_nei in nei[el] - if el_nei in els - push!(comp, el_nei) - delete!(els, el_nei) - _dfs!(comp, el_nei, nei, els) - end - end -end - -function connected_components(els, nei) - components = Set{Tuple{DataType,Int}}[] - while !isempty(els) - el = pop!(els) - comp = Set{Tuple{DataType,Int}}() - push!(comp, el) - _dfs!(comp, el, nei, els) - push!(components, comp) - end - return components -end -## - #= Hardcode some basic elements. TODO: Eventually this could/should be automated. diff --git a/test/ldim_test.jl b/test/ldim_test.jl deleted file mode 100644 index 22471257..00000000 --- a/test/ldim_test.jl +++ /dev/null @@ -1,76 +0,0 @@ -using Test -using LinearAlgebra -using Inti -using Random -using Meshes -using Plots - -include("test_utils.jl") -Random.seed!(1) - -N = 2 -t = :interior -pde = Inti.Laplace(; dim = N) - -K = 5:5 -H = [0.2 * 2.0^(-i) for i in 0:0] -fig = plot(; xscale = :log, yscale = :log, xticks = H) -for k in K - err = [] - for h in H - Inti.clear_entities!() - Ω, msh = - gmsh_disk(; center = [0.0, 0.0], rx = 1.0, ry = 1.0, meshsize = h, order = 2) - Γ = Inti.external_boundary(Ω) - - ## - - quad = Inti.Quadrature(msh[Γ]; qorder = 3) - σ = t == :interior ? 1 / 2 : -1 / 2 - xs = t == :interior ? ntuple(i -> 3, N) : ntuple(i -> 0.1, N) - T = Inti.default_density_eltype(pde) - c = rand(T) - u = (qnode) -> Inti.SingleLayerKernel(pde)(qnode, xs) * c - dudn = (qnode) -> Inti.AdjointDoubleLayerKernel(pde)(qnode, xs) * c - γ₀u = map(u, quad) - γ₁u = map(dudn, quad) - γ₀u_norm = norm(norm.(γ₀u, Inf), Inf) - γ₁u_norm = norm(norm.(γ₁u, Inf), Inf) - # single and double layer - G = Inti.SingleLayerKernel(pde) - S = Inti.IntegralOperator(G, quad) - Smat = Inti.assemble_matrix(S) - dG = Inti.DoubleLayerKernel(pde) - D = Inti.IntegralOperator(dG, quad) - Dmat = Inti.assemble_matrix(D) - e0 = norm(Smat * γ₁u - Dmat * γ₀u - σ * γ₀u, Inf) / γ₀u_norm - - green_multiplier = fill(-0.5, length(quad)) - # δS, δD = Inti.bdim_correction(pde, quad, quad, Smat, Dmat; green_multiplier) - - # qnodes = Inti.local_bdim_correction(pde, quad, quad; green_multiplier) - # X = [q.coords[1] for q in qnodes]; Y = [q.coords[2] for q in qnodes] - # u = [q.normal[1] for q in qnodes]; v = [q.normal[2] for q in qnodes] - # fig, _, _ = scatter(X, Y) - # arrows!(X, Y, u, v, lengthscale=0.01) - # display(fig) - - δS, δD = - Inti.local_bdim_correction(pde, quad, quad; green_multiplier, kneighbor = k) - Sdim = Smat + δS - Ddim = Dmat + δD - # Sdim, Ddim = Inti.single_double_layer(; - # pde, - # target = quad, - # source = quad, - # compression = (method = :none,), - # correction = (method = :ldim,), - # ) - e1 = norm(Sdim * γ₁u - Ddim * γ₀u - σ * γ₀u, Inf) / γ₀u_norm - # @show norm(e0, Inf) - @show norm(e1, Inf) - push!(err, e1) - end - plot!(fig, H, err; lw = 2, marker = :o, label = k) -end -display(fig) diff --git a/test/nearlist_test.jl b/test/nearlist_test.jl deleted file mode 100644 index 7efc2816..00000000 --- a/test/nearlist_test.jl +++ /dev/null @@ -1,40 +0,0 @@ -using Test -using LinearAlgebra -using Inti -using Random -using Meshes -using GLMakie - -include("test_utils.jl") -Random.seed!(1) - -N = 2 -t = :interior -pde = Inti.Laplace(; dim = N) -h = 0.2 - -Inti.clear_entities!() -Ω, msh = - gmsh_disks([([0.0, 0.0], 1.0, 1.0), ([-2.1, 0.0], 1.0, 1.0)]; meshsize = h, order = 2) -Γ = Inti.external_boundary(Ω) -quad = Inti.Quadrature(msh[Γ]; qorder = 3) - -# Nl = Inti.near_elements(quad;tol=0.2) -Ncl = Inti.near_components(quad; tol = 0.2) -fig, _, _ = viz(msh; showsegments = false, alpha = 0.3) - -E = first(keys(Ncl))[1]; -i = 1; -viz!(Inti.elements(msh[Γ], E)[i]; color = :red) -for (E_, j) in Ncl[(E, i)][2] - viz!(Inti.elements(msh[Γ], E_)[j]; color = :blue, alpha = 0.3) -end - -# for (E, nl) in Nl -# i = 1 -# viz!(Inti.elements(msh[Γ], E)[i];color=:red) -# for j in nl[i] -# viz!(Inti.elements(msh[Γ], E)[j];color=:blue,alpha=0.3) -# end -# end -display(fig) From 2c993a877ac06a0a1c7be82b77aad414a7c14101 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 20 Oct 2024 09:01:10 -0500 Subject: [PATCH 46/56] format --- src/bdim.jl | 1 - src/quadrature.jl | 2 +- src/reference_interpolation.jl | 2 +- src/vdim.jl | 27 +++++++++++++++++---------- test/poly_test.jl | 3 ++- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/bdim.jl b/src/bdim.jl index 449ca7e8..fac21c40 100644 --- a/src/bdim.jl +++ b/src/bdim.jl @@ -177,4 +177,3 @@ function bdim_correction( δD = sparse(Is, Js, Ds, num_trgs, n) return δS, δD end - diff --git a/src/quadrature.jl b/src/quadrature.jl index 56675660..fe81ca45 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -138,7 +138,7 @@ end function Quadrature( ::Type{T}, elementlist::AbstractVector{E}, - etype2qrule::Dict{DataType, Q}, + etype2qrule::Dict{DataType,Q}, qrule::Q; center::SVector{N,Float64} = zero(SVector{N,Float64}), scale::Float64 = 1.0, diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index f8ca3524..885b6780 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -383,7 +383,7 @@ function boundarynd(::Type{T}, els, msh) where {T} edgelist_unsrt = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) bords = Vector{MVector{length(bdi[1]),Int64}}(undef, length(bdi)) for i in 1:length(bdi) - bords[i] = MVector{length(bdi[1]), Int64}(undef) + bords[i] = MVector{length(bdi[1]),Int64}(undef) end j = 1 for ii in els diff --git a/src/vdim.jl b/src/vdim.jl index 76080507..a09d01ee 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -133,7 +133,7 @@ end function build_vander(vals_trg, pts, PFE_p, c, r) tmp = Vector{Float64}(undef, length(c)) for i in 1:length(pts) - tmp .= (pts[i].coords - c ) / r + tmp .= (pts[i].coords - c) / r ElementaryPDESolutions.fast_evaluate!(view(vals_trg, :, i), tmp, PFE_p) end return vals_trg @@ -187,7 +187,8 @@ function local_vdim_correction( bdry_qrule = _qrule_for_reference_shape(Inti.ReferenceSimplex{2}(), bdry_qorder) bdry_etype2qrule = Dict(Inti.ReferenceSimplex{2} => bdry_qrule) else - bdry_qrule = _qrule_for_reference_shape(Inti.ReferenceHyperCube{1}(), bdry_qorder) + bdry_qrule = + _qrule_for_reference_shape(Inti.ReferenceHyperCube{1}(), bdry_qorder) bdry_etype2qrule = Dict(Inti.ReferenceHyperCube{1} => bdry_qrule) end vol_qrule = VioreanuRokhlin(; domain = domain(E), order = quadrature_order) @@ -227,7 +228,7 @@ function local_vdim_correction( if SHIFT L̃ .= transpose(build_vander(vals_trg, view(source, jglob), PFE_p, c, r)) Linv = pinv(L̃) - S = Diagonal(1.0./r.^(abs.(multiindices))) + S = Diagonal(1.0 ./ r .^ (abs.(multiindices))) wei = transpose(Linv) * S * transpose(R) else error("unsupported local VDIM without shifting") @@ -478,12 +479,17 @@ function _local_vdim_auxiliary_quantities( ElementaryPDESolutions.fast_evaluate!(view(P, i, :), Xshift[i], PFE_P) end for i in 1:length(Ybdry) - ElementaryPDESolutions.fast_evaluate_with_jacobian!(view(γ₀B, i, :), view(grad, :, :, i), Ybdry[i].coords, PFE_P) + ElementaryPDESolutions.fast_evaluate_with_jacobian!( + view(γ₀B, i, :), + view(grad, :, :, i), + Ybdry[i].coords, + PFE_P, + ) end for i in 1:length(Ybdry) for j in 1:num_basis - γ₁B[i, j] = 0 - for k = 1:N + γ₁B[i, j] = 0 + for k in 1:N γ₁B[i, j] += grad[j, k, i] * Ybdry[i].normal[k]#nrml_bdry_vec[i][k] end end @@ -496,7 +502,7 @@ function _local_vdim_auxiliary_quantities( @views mul!(Θ[:, n], Dmat, γ₀B[:, n], -1, 1) @views mul!(Θ[:, n], Vmat, b[:, n], -1, 1) for i in 1:num_targets - Θ[i, n] += μ[i] * P[i,n] + Θ[i, n] += μ[i] * P[i, n] end end return Θ @@ -574,15 +580,16 @@ function polynomial_solutions_local_vdim(pde::AbstractPDE, order::Integer) sum(I) > order && continue # define the monomial basis functions, and the corresponding solutions. # TODO: adapt this to vectorial case - p = ElementaryPDESolutions.Polynomial(I => 1 / factorial(MultiIndex(I))) - P = polynomial_solution(pde, p) + p = ElementaryPDESolutions.Polynomial(I => 1 / factorial(MultiIndex(I))) + P = polynomial_solution(pde, p) push!(multiindices, MultiIndex(I)) push!(monomials, p) push!(poly_solutions, P) end PFE_monomials = ElementaryPDESolutions.assemble_fastevaluator(monomials, Float64) - PFE_polysolutions = ElementaryPDESolutions.assemble_fastevaluator(poly_solutions, Float64) + PFE_polysolutions = + ElementaryPDESolutions.assemble_fastevaluator(poly_solutions, Float64) return PFE_monomials, PFE_polysolutions, multiindices end diff --git a/test/poly_test.jl b/test/poly_test.jl index e0034d2f..d4049ba8 100644 --- a/test/poly_test.jl +++ b/test/poly_test.jl @@ -25,7 +25,8 @@ function poly_test(npts) exp_data[:, i] = [q for q in pol[1]] coeff_data[i] = pol[2] end - PolArray[polind] = FixedPolynomials.Polynomial(exp_data, coeff_data, [:x, :y]) + PolArray[polind] = + FixedPolynomials.Polynomial(exp_data, coeff_data, [:x, :y]) end PolSystem = System(PolArray) pts = Vector{Vector{Float64}}(undef, npts) From 1d65331ec7404cceff02841c3dde19fadbd12943 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 21 Oct 2024 08:17:13 -0500 Subject: [PATCH 47/56] Update lvdim for changes in main --- src/api.jl | 3 ++- src/nystrom.jl | 11 ++++++----- src/vdim.jl | 34 ++++++++++++++++++---------------- test/lvdim_test.jl | 28 +++++++++++++++++----------- 4 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/api.jl b/src/api.jl index 44cc7aaf..f1b7594f 100644 --- a/src/api.jl +++ b/src/api.jl @@ -341,7 +341,7 @@ function volume_potential(; op, target, source::Quadrature, compression, correct green_multiplier = fill(μ, length(target)) shift = Val(true) δV = local_vdim_correction( - pde, + op, eltype(V), target, source, @@ -351,6 +351,7 @@ function volume_potential(; op, target, source::Quadrature, compression, correct correction.maxdist, correction.interpolation_order, correction.quadrature_order, + correction.meshsize, shift, ) else diff --git a/src/nystrom.jl b/src/nystrom.jl index 6c4b18f7..9b92f71a 100644 --- a/src/nystrom.jl +++ b/src/nystrom.jl @@ -59,11 +59,12 @@ target(iop::IntegralOperator) = iop.target source(iop::IntegralOperator) = iop.source function IntegralOperator(k, X, Y = X) + # TODO: Breaks if Y is no a ::Quadrature # check that all entities in the quadrature are of the same dimension - if !allequal(geometric_dimension(ent) for ent in entities(Y)) - msg = "entities in the target quadrature have different geometric dimensions" - throw(ArgumentError(msg)) - end + #if !allequal(geometric_dimension(ent) for ent in entities(Y)) + # msg = "entities in the target quadrature have different geometric dimensions" + # throw(ArgumentError(msg)) + #end T = return_type(k, eltype(X), eltype(Y)) # FIXME This cripples performance for local VDIM #msg = """IntegralOperator of nonbits being created: $T""" @@ -97,7 +98,7 @@ function assemble_matrix(iop::IntegralOperator; threads = true) return out end -@noinline function _assemble_matrix!(out, K, X, Y, threads) +@noinline function _assemble_matrix!(out, K, X, Y::Quadrature, threads) @usethreads threads for j in 1:length(Y) for i in 1:length(X) @inbounds out[i, j] = K(X[i], Y[j]) * weight(Y[j]) diff --git a/src/vdim.jl b/src/vdim.jl index a09d01ee..0797c926 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -140,7 +140,7 @@ function build_vander(vals_trg, pts, PFE_p, c, r) end function local_vdim_correction( - pde, + op, ::Type{Eltype}, target, source::Quadrature, @@ -149,6 +149,7 @@ function local_vdim_correction( green_multiplier::Vector{<:Real}, interpolation_order = nothing, quadrature_order = nothing, + meshsize = 1.0, maxdist = Inf, center = nothing, shift::Val{SHIFT} = Val(false), @@ -157,13 +158,13 @@ function local_vdim_correction( vander_cond = vander_norm = rhs_norm = res_norm = shift_norm = -Inf # figure out if we are dealing with a scalar or vector PDE m, n = length(target), length(source) - N = ambient_dimension(pde) + N = ambient_dimension(op) @assert ambient_dimension(source) == N "vdim only works for volume potentials" m, n = length(target), length(source) # a reasonable interpolation_order if not provided isnothing(interpolation_order) && (interpolation_order = maximum(order, values(source.etype2qrule))) - PFE_p, PFE_P, multiindices = polynomial_solutions_local_vdim(pde, interpolation_order) + PFE_p, PFE_P, multiindices = polynomial_solutions_local_vdim(op, interpolation_order) dict_near = etype_to_nearest_points(target, source; maxdist) bdry_kdtree = KDTree(bdry_nodes) # compute sparse correction @@ -206,13 +207,12 @@ function local_vdim_correction( r = 1.0 end R = _local_vdim_auxiliary_quantities( - pde, + op, mesh, neighbors, n, c, r, - quadrature_order, PFE_p, PFE_P, target[near_list[n]], @@ -228,7 +228,10 @@ function local_vdim_correction( if SHIFT L̃ .= transpose(build_vander(vals_trg, view(source, jglob), PFE_p, c, r)) Linv = pinv(L̃) - S = Diagonal(1.0 ./ r .^ (abs.(multiindices))) + S = Diagonal(1.0./r.^(abs.(multiindices))) + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end wei = transpose(Linv) * S * transpose(R) else error("unsupported local VDIM without shifting") @@ -371,13 +374,12 @@ _newbord_line(vtxs) = LagrangeLine(SVector{3}(vtxs)) _newbord_tri(vtxs) = LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) function _local_vdim_auxiliary_quantities( - pde::AbstractPDE{N}, + op::AbstractDifferentialOperator{N}, mesh, neighbors, el, center, scale, - quadrature_order, PFE_p, PFE_P, X, @@ -438,8 +440,8 @@ function _local_vdim_auxiliary_quantities( Ybdry = Inti.Quadrature(Float64, bords, bdry_etype2qrule, bdry_qrule; center, scale) # TODO handle derivative case - G = SingleLayerKernel(pde) - dG = DoubleLayerKernel(pde) + G = SingleLayerKernel(op) + dG = DoubleLayerKernel(op) Xshift = [(q.coords - center) / scale for q in X] Sop = IntegralOperator(G, Xshift, Ybdry) Dop = IntegralOperator(dG, Xshift, Ybdry) @@ -451,7 +453,7 @@ function _local_vdim_auxiliary_quantities( μloc = _green_multiplier(:inside) green_multiplier = fill(μloc, length(X)) δS, δD = bdim_correction( - pde, + op, Xshift, Ybdry, Smat, @@ -560,15 +562,15 @@ function vdim_mesh_center(msh::AbstractMesh) return xc / M end """ - polynomial_solutions_local_vdim(pde, order) + polynomial_solutions_local_vdim(op, order) For every monomial term `pₙ` of degree `order`, compute a polynomial `Pₙ` such -that `ℒ[Pₙ] = pₙ`, where `ℒ` is the differential operator associated with `pde`. +that `ℒ[Pₙ] = pₙ`, where `ℒ` is the differential operator `op`. This function returns `{pₙ,Pₙ,γ₁Pₙ}`, where `γ₁Pₙ` is the generalized Neumann trace of `Pₙ`. """ -function polynomial_solutions_local_vdim(pde::AbstractPDE, order::Integer) - N = ambient_dimension(pde) +function polynomial_solutions_local_vdim(op::AbstractDifferentialOperator, order::Integer) + N = ambient_dimension(op) # create empty arrays to store the monomials, solutions, and traces. For the # neumann trace, we try to infer the concrete return type instead of simply # having a vector of `Function`. @@ -581,7 +583,7 @@ function polynomial_solutions_local_vdim(pde::AbstractPDE, order::Integer) # define the monomial basis functions, and the corresponding solutions. # TODO: adapt this to vectorial case p = ElementaryPDESolutions.Polynomial(I => 1 / factorial(MultiIndex(I))) - P = polynomial_solution(pde, p) + P = polynomial_solution(op, p) push!(multiindices, MultiIndex(I)) push!(monomials, p) push!(poly_solutions, P) diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 80729c62..85cd384b 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -11,9 +11,10 @@ using FMMLIB2D using GLMakie using Meshes -meshsize = 0.1 +meshsize = 0.00125 +#meshsize = 0.0125/4096/2 interpolation_order = 4 -VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(5) +VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(10) bdry_qorder = 2 * VR_qorder function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) @@ -34,7 +35,7 @@ function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) end name = joinpath(@__DIR__, "disk.msh") -gmsh_disk(; meshsize, order = 2, name) +gmsh_disk(; meshsize, order = 2, name, paxis = (meshsize * 20, meshsize * 10)) Inti.clear_entities!() # empty the entity cache msh = Inti.import_mesh(name; dim = 2) @@ -58,26 +59,30 @@ tquad = @elapsed begin end @info "Quadrature generation time: $tquad" -k0 = π -k = 0 +k0 = 3π +k = 2π θ = (cos(π / 3), sin(π / 3)) #u = (x) -> exp(im * k0 * dot(x, θ)) #du = (x,n) -> im * k0 * dot(θ, n) * exp(im * k0 * dot(x, θ)) -u = (x) -> cos(k0 * dot(x, θ)) -du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) -f = (x) -> (k^2 - k0^2) * u(x) +#u = (x) -> cos(k0 * dot(x, θ)) +#du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) +#f = (x) -> (k^2 - k0^2) * u(x) +s = 4000 +u = (x) -> 1/(k^2 - k0^2) * exp(im * k0 * dot(x, θ)) + 1/(k^2 - 4*s)*exp(-s*norm(x)^2) +du = (x, n) -> im*k0 * dot(θ, n) / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) - 2*s/(k^2 - 4*s) * dot(x, n) * exp(-s*norm(x)^2) +f = (x) -> exp(im*k0*dot(x, θ)) + 1/(k^2 - 4*s) * (4*s^2*norm(x)^2 - 4*s + k^2) * exp(-s * norm(x)^2) u_d = map(q -> u(q.coords), Ωₕ_quad) u_b = map(q -> u(q.coords), Γₕ_quad) du_b = map(q -> du(q.coords, q.normal), Γₕ_quad) f_d = map(q -> f(q.coords), Ωₕ_quad) -pde = k == 0 ? Inti.Laplace(; dim = 2) : Inti.Helmholtz(; dim = 2, k) +op = k == 0 ? Inti.Laplace(; dim = 2) : Inti.Helmholtz(; dim = 2, k) ## Boundary operators tbnd = @elapsed begin S_b2d, D_b2d = Inti.single_double_layer(; - pde, + op, target = Ωₕ_quad, source = Γₕ_quad, compression = (method = :fmm, tol = 1e-14), @@ -89,7 +94,7 @@ end ## Volume potentials #tvol = @elapsed begin V_d2d = Inti.volume_potential(; - pde, + op, target = Ωₕ_quad, source = Ωₕ_quad, compression = (method = :fmm, tol = 1e-14), @@ -100,6 +105,7 @@ V_d2d = Inti.volume_potential(; quadrature_order = VR_qorder, bdry_nodes = Γₕ.nodes, maxdist = 5 * meshsize, + meshsize = meshsize, ), ) #end From 4ec345033e0dc03383a74431ef79170611f937a8 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 21 Oct 2024 13:39:04 -0500 Subject: [PATCH 48/56] lvdim: [WIP] Stabilize Helmholtz. This stabilizes VDIM in the regime when hk is bounded strictly away from zero. --- src/nystrom.jl | 11 ++++++----- src/quadrature.jl | 4 ---- src/vdim.jl | 29 ++++++++++++++++------------- test/lvdim_test.jl | 8 ++++---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/nystrom.jl b/src/nystrom.jl index 9b92f71a..9e9d31c6 100644 --- a/src/nystrom.jl +++ b/src/nystrom.jl @@ -59,12 +59,13 @@ target(iop::IntegralOperator) = iop.target source(iop::IntegralOperator) = iop.source function IntegralOperator(k, X, Y = X) - # TODO: Breaks if Y is no a ::Quadrature # check that all entities in the quadrature are of the same dimension - #if !allequal(geometric_dimension(ent) for ent in entities(Y)) - # msg = "entities in the target quadrature have different geometric dimensions" - # throw(ArgumentError(msg)) - #end + if Y isa Quadrature && !isnothing(Y.mesh) + if !allequal(geometric_dimension(ent) for ent in entities(Y)) + msg = "entities in the target quadrature have different geometric dimensions" + throw(ArgumentError(msg)) + end + end T = return_type(k, eltype(X), eltype(Y)) # FIXME This cripples performance for local VDIM #msg = """IntegralOperator of nonbits being created: $T""" diff --git a/src/quadrature.jl b/src/quadrature.jl index fe81ca45..15e32467 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -185,10 +185,6 @@ end center::SVector{N,Float64} = zero(SVector{N,Float64}), scale::Float64 = 1.0, ) where {E,N,T} - - # FIXME !! - scale = 1.0 - x̂, ŵ = qrule() # nodes and weights on reference element num_nodes = length(ŵ) M = geometric_dimension(domain(E)) diff --git a/src/vdim.jl b/src/vdim.jl index 0797c926..9fa07991 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -164,7 +164,13 @@ function local_vdim_correction( # a reasonable interpolation_order if not provided isnothing(interpolation_order) && (interpolation_order = maximum(order, values(source.etype2qrule))) - PFE_p, PFE_P, multiindices = polynomial_solutions_local_vdim(op, interpolation_order) + + # Helmholtz PDE operator in x̂ coordinates where x = scale * x̂ + s = meshsize + op_hat = Inti.Helmholtz(; dim = ambient_dimension(op), k = s * op.k) + PFE_p, PFE_P, multiindices = + polynomial_solutions_local_vdim(op_hat, interpolation_order) + dict_near = etype_to_nearest_points(target, source; maxdist) bdry_kdtree = KDTree(bdry_nodes) # compute sparse correction @@ -202,17 +208,13 @@ function local_vdim_correction( # indices of nodes in element `n` isempty(near_list[n]) && continue c, r = translation_and_scaling(els[n]) - if !SHIFT - c = SVector{N,Float64}(0, 0) - r = 1.0 - end R = _local_vdim_auxiliary_quantities( - op, + op_hat, mesh, neighbors, n, c, - r, + s, PFE_p, PFE_P, target[near_list[n]], @@ -228,7 +230,7 @@ function local_vdim_correction( if SHIFT L̃ .= transpose(build_vander(vals_trg, view(source, jglob), PFE_p, c, r)) Linv = pinv(L̃) - S = Diagonal(1.0./r.^(abs.(multiindices))) + S = s^2 * Diagonal((s / r) .^ (abs.(multiindices))) if isdefined(Main, :Infiltrator) Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) end @@ -374,7 +376,7 @@ _newbord_line(vtxs) = LagrangeLine(SVector{3}(vtxs)) _newbord_tri(vtxs) = LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) function _local_vdim_auxiliary_quantities( - op::AbstractDifferentialOperator{N}, + op_hat::AbstractDifferentialOperator{N}, mesh, neighbors, el, @@ -390,7 +392,6 @@ function _local_vdim_auxiliary_quantities( bdry_qrule, vol_qrule; ) where {N} - scale = 1.0 # construct the local region Etype = first(Inti.element_types(mesh)) el_neighs = neighbors[(Etype, el)] @@ -435,13 +436,15 @@ function _local_vdim_auxiliary_quantities( end need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam / 2)) > 0 + # Now begin working in x̂ coordinates where x = scale * x̂ + # build O(h) volume neighbors Yvol = Inti.Quadrature(Float64, els_list, vol_etype2qrule, vol_qrule; center, scale) Ybdry = Inti.Quadrature(Float64, bords, bdry_etype2qrule, bdry_qrule; center, scale) # TODO handle derivative case - G = SingleLayerKernel(op) - dG = DoubleLayerKernel(op) + G = SingleLayerKernel(op_hat) + dG = DoubleLayerKernel(op_hat) Xshift = [(q.coords - center) / scale for q in X] Sop = IntegralOperator(G, Xshift, Ybdry) Dop = IntegralOperator(dG, Xshift, Ybdry) @@ -453,7 +456,7 @@ function _local_vdim_auxiliary_quantities( μloc = _green_multiplier(:inside) green_multiplier = fill(μloc, length(X)) δS, δD = bdim_correction( - op, + op_hat, Xshift, Ybdry, Smat, diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 85cd384b..4370a2c0 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -67,10 +67,10 @@ k = 2π #u = (x) -> cos(k0 * dot(x, θ)) #du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) #f = (x) -> (k^2 - k0^2) * u(x) -s = 4000 -u = (x) -> 1/(k^2 - k0^2) * exp(im * k0 * dot(x, θ)) + 1/(k^2 - 4*s)*exp(-s*norm(x)^2) -du = (x, n) -> im*k0 * dot(θ, n) / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) - 2*s/(k^2 - 4*s) * dot(x, n) * exp(-s*norm(x)^2) -f = (x) -> exp(im*k0*dot(x, θ)) + 1/(k^2 - 4*s) * (4*s^2*norm(x)^2 - 4*s + k^2) * exp(-s * norm(x)^2) +s = 4000 +u = (x) -> 1 / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * exp(-s * norm(x)^2) +du = (x, n) -> im * k0 * dot(θ, n) / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) - 2 * s / (k^2 - 4 * s) * dot(x, n) * exp(-s * norm(x)^2) +f = (x) -> exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * (4 * s^2 * norm(x)^2 - 4 * s + k^2) * exp(-s * norm(x)^2) u_d = map(q -> u(q.coords), Ωₕ_quad) u_b = map(q -> u(q.coords), Γₕ_quad) From 4028967209280367edac84d45dda81a1a63cafc1 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 21 Oct 2024 21:12:43 -0500 Subject: [PATCH 49/56] lvdim: Refactor to prepare for low freq. stabilization --- src/vdim.jl | 139 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 111 insertions(+), 28 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 9fa07991..ec79bbde 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -139,6 +139,24 @@ function build_vander(vals_trg, pts, PFE_p, c, r) return vals_trg end +function _scaled_operator(op::AbstractDifferentialOperator, scale) + if op isa Helmholtz + return Helmholtz(; k = scale * op.k, dim = ambient_dimension(op)) + elseif op isa Laplace + return op + else + error("Unsupported operator for stabilized Local VDIM") + end +end + +function _lowfreq_operator(op::AbstractDifferentialOperator{N}) where {N} + if op isa Helmholtz + return Laplace(; dim = N) + else + error("Unsupported operator for stabilized Local VDIM") + end +end + function local_vdim_correction( op, ::Type{Eltype}, @@ -167,7 +185,10 @@ function local_vdim_correction( # Helmholtz PDE operator in x̂ coordinates where x = scale * x̂ s = meshsize - op_hat = Inti.Helmholtz(; dim = ambient_dimension(op), k = s * op.k) + op_hat = _scaled_operator(op, s) + op_lowfreq = _lowfreq_operator(op) + PFE_p_lowfreq, PFE_p_lowfreq, multiindices = + polynomial_solutions_local_vdim(op_lowfreq, interpolation_order) PFE_p, PFE_P, multiindices = polynomial_solutions_local_vdim(op_hat, interpolation_order) @@ -191,40 +212,70 @@ function local_vdim_correction( bdry_qorder = 2 * quadrature_order if N == 3 - bdry_qrule = _qrule_for_reference_shape(Inti.ReferenceSimplex{2}(), bdry_qorder) - bdry_etype2qrule = Dict(Inti.ReferenceSimplex{2} => bdry_qrule) + bdry_qrule = _qrule_for_reference_shape(ReferenceSimplex{2}(), bdry_qorder) + bdry_etype2qrule = Dict(ReferenceSimplex{2} => bdry_qrule) else - bdry_qrule = - _qrule_for_reference_shape(Inti.ReferenceHyperCube{1}(), bdry_qorder) - bdry_etype2qrule = Dict(Inti.ReferenceHyperCube{1} => bdry_qrule) + bdry_qrule = _qrule_for_reference_shape(ReferenceHyperCube{1}(), bdry_qorder) + bdry_etype2qrule = Dict(ReferenceHyperCube{1} => bdry_qrule) end vol_qrule = VioreanuRokhlin(; domain = domain(E), order = quadrature_order) vol_etype2qrule = Dict(E => vol_qrule) topo_neighs = 1 - neighbors = Inti.topological_neighbors(mesh, topo_neighs) + neighbors = topological_neighbors(mesh, topo_neighs) for n in 1:ne # indices of nodes in element `n` isempty(near_list[n]) && continue c, r = translation_and_scaling(els[n]) - R = _local_vdim_auxiliary_quantities( - op_hat, + Yvol, Ybdry, diam, need_layer_corr = _local_vdim_construct_local_quadratures( + N, mesh, neighbors, n, c, s, - PFE_p, - PFE_P, - target[near_list[n]], - green_multiplier, bdry_kdtree, bdry_etype2qrule, vol_etype2qrule, bdry_qrule, - vol_qrule; + vol_qrule, ) + if r * op.k < 10^(-2) + lowfreq = true + R = _lowfreq_vdim_auxiliary_quantities( + op_hat, + mesh, + neighbors, + n, + c, + s, + PFE_p, + PFE_P, + target[near_list[n]], + green_multiplier, + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule; + ) + else + lowfreq = false + R = _local_vdim_auxiliary_quantities( + op_hat, + c, + s, + PFE_p, + PFE_P, + target[near_list[n]], + green_multiplier, + Yvol, + Ybdry, + diam, + need_layer_corr; + ) + end jglob = @view qtags[:, n] # compute translation and scaling if SHIFT @@ -375,41 +426,37 @@ _newbord_line(vtxs) = LagrangeLine(SVector{3}(vtxs)) # function barrier for type stability purposes _newbord_tri(vtxs) = LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) -function _local_vdim_auxiliary_quantities( - op_hat::AbstractDifferentialOperator{N}, +function _local_vdim_construct_local_quadratures( + N, mesh, neighbors, el, center, scale, - PFE_p, - PFE_P, - X, - μ, bdry_kdtree, bdry_etype2qrule, vol_etype2qrule, bdry_qrule, vol_qrule; -) where {N} +) # construct the local region - Etype = first(Inti.element_types(mesh)) + Etype = first(element_types(mesh)) el_neighs = neighbors[(Etype, el)] T = first(el_neighs)[1] els_idxs = [i[2] for i in collect(el_neighs)] els_list = mesh.etype2els[Etype][els_idxs] - loc_bdry = Inti.boundarynd(T, els_idxs, mesh) + loc_bdry = boundarynd(T, els_idxs, mesh) # TODO handle curved boundary of Γ?? if N == 2 - bords = Inti.LagrangeElement{Inti.ReferenceHyperCube{N - 1},3,SVector{N,Float64}}[] + bords = LagrangeElement{ReferenceHyperCube{N - 1},3,SVector{N,Float64}}[] else - bords = Inti.LagrangeElement{Inti.ReferenceSimplex{N - 1},3,SVector{N,Float64}}[] + bords = LagrangeElement{ReferenceSimplex{N - 1},3,SVector{N,Float64}}[] end for idxs in loc_bdry - vtxs = Inti.nodes(mesh)[idxs] + vtxs = nodes(mesh)[idxs] if N == 2 bord = _newbord_line(vtxs) else @@ -439,9 +486,25 @@ function _local_vdim_auxiliary_quantities( # Now begin working in x̂ coordinates where x = scale * x̂ # build O(h) volume neighbors - Yvol = Inti.Quadrature(Float64, els_list, vol_etype2qrule, vol_qrule; center, scale) - Ybdry = Inti.Quadrature(Float64, bords, bdry_etype2qrule, bdry_qrule; center, scale) + Yvol = Quadrature(Float64, els_list, vol_etype2qrule, vol_qrule; center, scale) + Ybdry = Quadrature(Float64, bords, bdry_etype2qrule, bdry_qrule; center, scale) + return Yvol, Ybdry, diam, need_layer_corr +end + +function _local_vdim_auxiliary_quantities( + op_hat::AbstractDifferentialOperator{N}, + center, + scale, + PFE_p, + PFE_P, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr; +) where {N} # TODO handle derivative case G = SingleLayerKernel(op_hat) dG = DoubleLayerKernel(op_hat) @@ -513,6 +576,26 @@ function _local_vdim_auxiliary_quantities( return Θ end +function _lowfreq_vdim_auxiliary_quantities( + op_hat::Helmholtz{2}, + mesh, + neighbors, + el, + center, + scale, + PFE_p, + PFE_P, + X, + μ, + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule; +) + error("not yet implemented") +end + function _vdim_auxiliary_quantities( p, P, From 2c5026e07ff632e76aec9e262d45a2932472cc45 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Mon, 25 Nov 2024 17:45:05 -0600 Subject: [PATCH 50/56] lvdim: WIP low-freq stabilization --- src/vdim.jl | 295 +++++++++++++++++++++++++++++++++++---------- test/lvdim_test.jl | 25 ++-- 2 files changed, 242 insertions(+), 78 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index ec79bbde..45b24db6 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -152,6 +152,8 @@ end function _lowfreq_operator(op::AbstractDifferentialOperator{N}) where {N} if op isa Helmholtz return Laplace(; dim = N) + elseif op isa Laplace + return Laplace(; dim = N) else error("Unsupported operator for stabilized Local VDIM") end @@ -187,9 +189,9 @@ function local_vdim_correction( s = meshsize op_hat = _scaled_operator(op, s) op_lowfreq = _lowfreq_operator(op) - PFE_p_lowfreq, PFE_p_lowfreq, multiindices = - polynomial_solutions_local_vdim(op_lowfreq, interpolation_order) - PFE_p, PFE_P, multiindices = + PFE_p_lowfreq, PFE_P_lowfreq, multiindices_lowfreq, monomials_indices_lowfreq = + polynomial_solutions_local_vdim(op_lowfreq, interpolation_order + 2) + PFE_p, PFE_P, multiindices, monomials_indices = polynomial_solutions_local_vdim(op_hat, interpolation_order) dict_near = etype_to_nearest_points(target, source; maxdist) @@ -227,42 +229,61 @@ function local_vdim_correction( for n in 1:ne # indices of nodes in element `n` isempty(near_list[n]) && continue - c, r = translation_and_scaling(els[n]) - Yvol, Ybdry, diam, need_layer_corr = _local_vdim_construct_local_quadratures( - N, - mesh, - neighbors, - n, - c, - s, - bdry_kdtree, - bdry_etype2qrule, - vol_etype2qrule, - bdry_qrule, - vol_qrule, - ) - if r * op.k < 10^(-2) + c, r, diam = translation_and_scaling(els[n]) + if true + #if r * op.k < 10^(-3) lowfreq = true - R = _lowfreq_vdim_auxiliary_quantities( - op_hat, + Yvol, Ybdry, need_layer_corr = _local_vdim_construct_local_quadratures( + N, mesh, neighbors, n, c, - s, - PFE_p, - PFE_P, - target[near_list[n]], - green_multiplier, + r, + diam, bdry_kdtree, bdry_etype2qrule, vol_etype2qrule, bdry_qrule, - vol_qrule; + vol_qrule, + ) + R = _lowfreq_vdim_auxiliary_quantities( + op, + op_lowfreq, + c, + r, + num_basis, + PFE_p_lowfreq, + PFE_P_lowfreq, + multiindices, + multiindices_lowfreq, + monomials_indices, + monomials_indices_lowfreq, + target[near_list[n]], + green_multiplier, + Yvol, + Ybdry, + diam, + need_layer_corr; ) else + @show "used highfreq" lowfreq = false - R = _local_vdim_auxiliary_quantities( + Yvol, Ybdry, need_layer_corr = _local_vdim_construct_local_quadratures( + N, + mesh, + neighbors, + n, + c, + s, + diam, + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule, + ) + R, _ = _local_vdim_auxiliary_quantities( op_hat, c, s, @@ -279,13 +300,15 @@ function local_vdim_correction( jglob = @view qtags[:, n] # compute translation and scaling if SHIFT + # TODO copy this from (part of) the output of _local_vdim_auxiliary_quantities ? L̃ .= transpose(build_vander(vals_trg, view(source, jglob), PFE_p, c, r)) Linv = pinv(L̃) - S = s^2 * Diagonal((s / r) .^ (abs.(multiindices))) - if isdefined(Main, :Infiltrator) - Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + if !lowfreq + S = s^2 * Diagonal((s / r) .^ (abs.(multiindices))) + wei = transpose(Linv) * S * transpose(R) + else + wei = transpose(Linv) * transpose(R) end - wei = transpose(Linv) * S * transpose(R) else error("unsupported local VDIM without shifting") end @@ -303,6 +326,9 @@ function local_vdim_correction( |-- max shift norm : $shift_norm """ δV = sparse(Is, Js, Vs, m, n) + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end return δV end @@ -329,6 +355,7 @@ function translation_and_scaling(el::LagrangeTriangle) l1 = norm(vertices[1] - vertices[2]) l2 = norm(vertices[2] - vertices[3]) l3 = norm(vertices[3] - vertices[1]) + diam = max(l1, l2, l3) if ((l1^2 + l2^2 >= l3^2) && (l2^2 + l3^2 >= l1^2) && (l3^2 + l1^2 > l2^2)) acuteright = true else @@ -357,7 +384,7 @@ function translation_and_scaling(el::LagrangeTriangle) r = l3 / 2 end end - return c, r + return c, r, diam end function translation_and_scaling(el::LagrangeTetrahedron) @@ -370,6 +397,7 @@ function translation_and_scaling(el::LagrangeTetrahedron) d = norm(vertices[3] - vertices[2]) e = norm(vertices[3] - vertices[1]) f = norm(vertices[2] - vertices[1]) + diam = max(a, b, c, d, e, f) f² = f^2 a² = a^2 b² = b^2 @@ -417,7 +445,7 @@ function translation_and_scaling(el::LagrangeTetrahedron) R = f / 2 end end - return center, R + return center, R, diam end # function barrier for type stability purposes @@ -433,6 +461,7 @@ function _local_vdim_construct_local_quadratures( el, center, scale, + diam, bdry_kdtree, bdry_etype2qrule, vol_etype2qrule, @@ -467,20 +496,6 @@ function _local_vdim_construct_local_quadratures( # Check if we need to do near-singular layer potential evaluation vertices = mesh.etype2els[Etype][el].vals[vertices_idxs(Etype)] - if N == 2 - diam = max( - norm(vertices[1] - vertices[2]), - norm(vertices[2] - vertices[3]), - norm(vertices[3] - vertices[1]), - ) - else - diam = max( - norm(vertices[1] - vertices[2]), - norm(vertices[2] - vertices[3]), - norm(vertices[3] - vertices[4]), - norm(vertices[4] - vertices[1]), - ) - end need_layer_corr = sum(inrangecount(bdry_kdtree, vertices, diam / 2)) > 0 # Now begin working in x̂ coordinates where x = scale * x̂ @@ -489,11 +504,11 @@ function _local_vdim_construct_local_quadratures( Yvol = Quadrature(Float64, els_list, vol_etype2qrule, vol_qrule; center, scale) Ybdry = Quadrature(Float64, bords, bdry_etype2qrule, bdry_qrule; center, scale) - return Yvol, Ybdry, diam, need_layer_corr + return Yvol, Ybdry, need_layer_corr end function _local_vdim_auxiliary_quantities( - op_hat::AbstractDifferentialOperator{N}, + op::AbstractDifferentialOperator{N}, center, scale, PFE_p, @@ -506,8 +521,8 @@ function _local_vdim_auxiliary_quantities( need_layer_corr; ) where {N} # TODO handle derivative case - G = SingleLayerKernel(op_hat) - dG = DoubleLayerKernel(op_hat) + G = SingleLayerKernel(op) + dG = DoubleLayerKernel(op) Xshift = [(q.coords - center) / scale for q in X] Sop = IntegralOperator(G, Xshift, Ybdry) Dop = IntegralOperator(dG, Xshift, Ybdry) @@ -519,7 +534,7 @@ function _local_vdim_auxiliary_quantities( μloc = _green_multiplier(:inside) green_multiplier = fill(μloc, length(X)) δS, δD = bdim_correction( - op_hat, + op, Xshift, Ybdry, Smat, @@ -528,6 +543,7 @@ function _local_vdim_auxiliary_quantities( maxdist = diam / scale, derivative = false, ) + Smat += δS Dmat += δD end @@ -573,27 +589,173 @@ function _local_vdim_auxiliary_quantities( Θ[i, n] += μ[i] * P[i, n] end end - return Θ + area = 0 + for i in 1:length(Yvol) + area += Yvol[i].weight + end + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end + return Θ, b end function _lowfreq_vdim_auxiliary_quantities( - op_hat::Helmholtz{2}, - mesh, - neighbors, - el, + op::Laplace{2}, + op_lowfreq::Laplace{2}, center, scale, - PFE_p, - PFE_P, + num_basis, + PFE_p_lowfreq, + PFE_P_lowfreq, + multiindices, + multiindices_lowfreq, + monomials_indices, + monomials_indices_lowfreq, X, μ, - bdry_kdtree, - bdry_etype2qrule, - vol_etype2qrule, - bdry_qrule, - vol_qrule; + Yvol, + Ybdry, + diam, + need_layer_corr; +) + Xshift = [(q.coords - center) / scale for q in X] + # Laplace + θ, b = _local_vdim_auxiliary_quantities( + op_lowfreq, + center, + scale, + PFE_p_lowfreq, + PFE_P_lowfreq, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr, + ) + + num_targets = length(X) + + # Set up integral operators just to find the element type of the correction matrix R + G = SingleLayerKernel(op) + Vop = IntegralOperator(G, Xshift, Yvol) + R = zeros(eltype(Vop), num_targets, num_basis) + + Hmat = Matrix{ComplexF64}(undef, length(X), length(Yvol)) + #Hmat .= 0 + #@show scale * op.k + for i in 1:num_targets + for j in 1:length(Yvol) + Hmat[i, j] = Yvol[j].weight + end + end + + for n in 1:num_basis + beta = multiindices[n] + for j in 1:num_targets + R[j, n] = θ[j, monomials_indices_lowfreq[beta]] + end + R[:, n] .*= scale^2 + # Commenting this yields more accuracy + R[:, n] += scale^2 * log(scale) * Hmat * b[:, monomials_indices_lowfreq[beta]] + #if isdefined(Main, :Infiltrator) + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + # end + end + # beware! the `b` here is that of PFE_p_lowfreq. + return R +end + +function _lowfreq_vdim_auxiliary_quantities( + op::Helmholtz{2}, + op_lowfreq::Laplace{2}, + center, + scale, + num_basis, + PFE_p_lowfreq, + PFE_P_lowfreq, + multiindices, + multiindices_lowfreq, + monomials_indices, + monomials_indices_lowfreq, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr; ) - error("not yet implemented") + Xshift = [(q.coords - center) / scale for q in X] + # Laplace + θ, b = _local_vdim_auxiliary_quantities( + op_lowfreq, + center, + scale, + PFE_p_lowfreq, + PFE_P_lowfreq, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr, + ) + + num_targets = length(X) + + # Set up integral operators just to find the element type of the correction matrix R + G = SingleLayerKernel(op) + Vop = IntegralOperator(G, Xshift, Yvol) + R = zeros(eltype(Vop), num_targets, num_basis) + kr2 = (op.k * scale)^2 + γ = 0.57721566490153286060 + + Hmat = Matrix{ComplexF64}(undef, length(X), length(Yvol)) + #Hmat .= 0 + #@show scale * op.k + for i in 1:num_targets + for j in 1:length(Yvol) + z2 = + kr2 * ( + (Xshift[i][1] - Yvol[j].coords[1])^2 + + (Xshift[i][2] - Yvol[j].coords[2])^2 + ) + bessj0 = 0 + for k in 0:3 + bessj0 += (-1)^k * (1 / 4 * z2)^k / (factorial(k)^2) + end + Hmat[i, j] = + ( + (1 + 2 * im / pi * (γ + 1 / 2 * log(kr2 / 4))) * bessj0 + + 2 * im / pi * + (1 / 4 * z2 - 3 / 2 * (1 / 4 * z2)^2 / 4 + 11 / 3456 * z2^3) + ) * + Yvol[j].weight * + scale^2 + end + end + + for n in 1:num_basis + beta = multiindices[n] + beta10 = beta + Inti.MultiIndex((1, 0)) + beta01 = beta + Inti.MultiIndex((0, 1)) + beta20 = beta + Inti.MultiIndex((2, 0)) + beta02 = beta + Inti.MultiIndex((0, 2)) + for j in 1:num_targets + x1t = Xshift[j][1] + x2t = Xshift[j][2] + R[j, n] = + (1 - 1 / 4 * kr2 * (x1t^2 + x2t^2)) * θ[j, monomials_indices_lowfreq[beta]] + +1 / 2 * kr2 * x1t * θ[j, monomials_indices_lowfreq[beta10]] + +1 / 2 * kr2 * x2t * θ[j, monomials_indices_lowfreq[beta01]] + -1 / 4 * kr2 * θ[j, monomials_indices_lowfreq[beta20]] + -1 / 4 * kr2 * θ[j, monomials_indices_lowfreq[beta02]] + end + R[:, n] .*= 2 * im / pi * scale^2 + # Commenting this yields more accuracy + R[:, n] += Hmat * b[:, monomials_indices_lowfreq[beta]] + end + return R, b end function _vdim_auxiliary_quantities( @@ -674,12 +836,13 @@ function polynomial_solutions_local_vdim(op::AbstractDifferentialOperator, order push!(monomials, p) push!(poly_solutions, P) end + monomials_indices = Dict(multiindices .=> 1:length(multiindices)) PFE_monomials = ElementaryPDESolutions.assemble_fastevaluator(monomials, Float64) PFE_polysolutions = ElementaryPDESolutions.assemble_fastevaluator(poly_solutions, Float64) - return PFE_monomials, PFE_polysolutions, multiindices + return PFE_monomials, PFE_polysolutions, multiindices, monomials_indices end """ diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 4370a2c0..e443c28a 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -11,10 +11,10 @@ using FMMLIB2D using GLMakie using Meshes -meshsize = 0.00125 -#meshsize = 0.0125/4096/2 +#meshsize = 0.001/8 +meshsize = 0.000125 interpolation_order = 4 -VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(10) +VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(4) bdry_qorder = 2 * VR_qorder function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) @@ -59,18 +59,19 @@ tquad = @elapsed begin end @info "Quadrature generation time: $tquad" -k0 = 3π -k = 2π +k0 = 1 +k = 0 θ = (cos(π / 3), sin(π / 3)) #u = (x) -> exp(im * k0 * dot(x, θ)) #du = (x,n) -> im * k0 * dot(θ, n) * exp(im * k0 * dot(x, θ)) -#u = (x) -> cos(k0 * dot(x, θ)) -#du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) -#f = (x) -> (k^2 - k0^2) * u(x) -s = 4000 -u = (x) -> 1 / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * exp(-s * norm(x)^2) -du = (x, n) -> im * k0 * dot(θ, n) / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) - 2 * s / (k^2 - 4 * s) * dot(x, n) * exp(-s * norm(x)^2) -f = (x) -> exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * (4 * s^2 * norm(x)^2 - 4 * s + k^2) * exp(-s * norm(x)^2) +u = (x) -> cos(k0 * dot(x, θ)) +du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) +f = (x) -> (k^2 - k0^2) * u(x) + +#s = 4 +#u = (x) -> 1 / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * exp(-s * norm(x)^2) +#du = (x, n) -> im * k0 * dot(θ, n) / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) - 2 * s / (k^2 - 4 * s) * dot(x, n) * exp(-s * norm(x)^2) +#f = (x) -> exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * (4 * s^2 * norm(x)^2 - 4 * s + k^2) * exp(-s * norm(x)^2) u_d = map(q -> u(q.coords), Ωₕ_quad) u_b = map(q -> u(q.coords), Γₕ_quad) From d5985f51e9e48e332c5fee09c0aef67ca323d4bb Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Tue, 14 Jan 2025 07:58:26 -0600 Subject: [PATCH 51/56] simplify stab-lvdim --- src/vdim.jl | 67 ++++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index 45b24db6..18431e82 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -230,7 +230,7 @@ function local_vdim_correction( # indices of nodes in element `n` isempty(near_list[n]) && continue c, r, diam = translation_and_scaling(els[n]) - if true + if false #if r * op.k < 10^(-3) lowfreq = true Yvol, Ybdry, need_layer_corr = _local_vdim_construct_local_quadratures( @@ -283,7 +283,10 @@ function local_vdim_correction( bdry_qrule, vol_qrule, ) - R, _ = _local_vdim_auxiliary_quantities( + #if isdefined(Main, :Infiltrator) + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + # end + R, b = _local_vdim_auxiliary_quantities( op_hat, c, s, @@ -296,6 +299,9 @@ function local_vdim_correction( diam, need_layer_corr; ) + #if isdefined(Main, :Infiltrator) + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + # end end jglob = @view qtags[:, n] # compute translation and scaling @@ -306,8 +312,17 @@ function local_vdim_correction( if !lowfreq S = s^2 * Diagonal((s / r) .^ (abs.(multiindices))) wei = transpose(Linv) * S * transpose(R) + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end else - wei = transpose(Linv) * transpose(R) + D = Vector{Float64}(undef, num_basis) + D .= r^2 + D = Diagonal(D) + wei = transpose(Linv) * D * transpose(R) + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end end else error("unsupported local VDIM without shifting") @@ -326,9 +341,6 @@ function local_vdim_correction( |-- max shift norm : $shift_norm """ δV = sparse(Is, Js, Vs, m, n) - if isdefined(Main, :Infiltrator) - Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end return δV end @@ -589,13 +601,6 @@ function _local_vdim_auxiliary_quantities( Θ[i, n] += μ[i] * P[i, n] end end - area = 0 - for i in 1:length(Yvol) - area += Yvol[i].weight - end - if isdefined(Main, :Infiltrator) - Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end return Θ, b end @@ -618,8 +623,6 @@ function _lowfreq_vdim_auxiliary_quantities( diam, need_layer_corr; ) - Xshift = [(q.coords - center) / scale for q in X] - # Laplace θ, b = _local_vdim_auxiliary_quantities( op_lowfreq, center, @@ -631,38 +634,24 @@ function _lowfreq_vdim_auxiliary_quantities( Yvol, Ybdry, diam, - need_layer_corr, + need_layer_corr; ) + Xshift = [(q.coords - center) / scale for q in X] + num_targets = length(Xshift) - num_targets = length(X) - - # Set up integral operators just to find the element type of the correction matrix R - G = SingleLayerKernel(op) - Vop = IntegralOperator(G, Xshift, Yvol) - R = zeros(eltype(Vop), num_targets, num_basis) - - Hmat = Matrix{ComplexF64}(undef, length(X), length(Yvol)) - #Hmat .= 0 - #@show scale * op.k - for i in 1:num_targets + Hmat = Matrix{eltype(θ)}(undef, num_targets, length(Yvol)) + for n in 1:num_targets for j in 1:length(Yvol) - Hmat[i, j] = Yvol[j].weight + Hmat[n, j] = Yvol[j].weight end end + R = Matrix{eltype(θ)}(undef, num_targets, num_basis) for n in 1:num_basis - beta = multiindices[n] - for j in 1:num_targets - R[j, n] = θ[j, monomials_indices_lowfreq[beta]] - end - R[:, n] .*= scale^2 - # Commenting this yields more accuracy - R[:, n] += scale^2 * log(scale) * Hmat * b[:, monomials_indices_lowfreq[beta]] - #if isdefined(Main, :Infiltrator) - # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - # end + β = multiindices[n] + R[:, n] = θ[:, monomials_indices_lowfreq[β]] + R[:, n] += log(scale) * Hmat * b[:, monomials_indices_lowfreq[β]] end - # beware! the `b` here is that of PFE_p_lowfreq. return R end From a703b057a2c84d37ab32bb31fb49519bc159cbd2 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Wed, 22 Jan 2025 12:36:48 -0600 Subject: [PATCH 52/56] some more scaled quadrature tests --- src/vdim.jl | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/src/vdim.jl b/src/vdim.jl index 18431e82..e8321f45 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -230,6 +230,7 @@ function local_vdim_correction( # indices of nodes in element `n` isempty(near_list[n]) && continue c, r, diam = translation_and_scaling(els[n]) + #s=1.0 if false #if r * op.k < 10^(-3) lowfreq = true @@ -266,8 +267,34 @@ function local_vdim_correction( diam, need_layer_corr; ) + Yvol_s1, Ybdry_s1, need_layer_corr_s1 = _local_vdim_construct_local_quadratures( + N, + mesh, + neighbors, + n, + c, + 1.0, + diam, + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule, + ) + R_s1, b_s1 = _local_vdim_auxiliary_quantities( + op_hat, + c, + 1.0, + PFE_p, + PFE_P, + target[near_list[n]], + green_multiplier, + Yvol_s1, + Ybdry_s1, + diam, + need_layer_corr_s1; + ) else - @show "used highfreq" lowfreq = false Yvol, Ybdry, need_layer_corr = _local_vdim_construct_local_quadratures( N, @@ -299,6 +326,33 @@ function local_vdim_correction( diam, need_layer_corr; ) + Yvol_s1, Ybdry_s1, need_layer_corr_s1 = _local_vdim_construct_local_quadratures( + N, + mesh, + neighbors, + n, + c, + 1.0, + diam, + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule, + ) + R_s1, b_s1 = _local_vdim_auxiliary_quantities( + op_hat, + c, + 1.0, + PFE_p, + PFE_P, + target[near_list[n]], + green_multiplier, + Yvol_s1, + Ybdry_s1, + diam, + need_layer_corr_s1; + ) #if isdefined(Main, :Infiltrator) # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) # end @@ -311,7 +365,63 @@ function local_vdim_correction( Linv = pinv(L̃) if !lowfreq S = s^2 * Diagonal((s / r) .^ (abs.(multiindices))) + S_s1 = Diagonal((1 / r) .^ (abs.(multiindices))) wei = transpose(Linv) * S * transpose(R) + wei_s1 = transpose(Linv) * S_s1 * transpose(R_s1) + + area = 0.0 + for i in 1:length(Yvol) + area += Yvol[i].weight + end + area_s1 = 0.0 + for i in 1:length(Yvol_s1) + area_s1 += Yvol_s1[i].weight + end + area_contour = 0.0 + for i in 1:length(Ybdry) + x = Ybdry[i].coords[1] + y = Ybdry[i].coords[2] + nx = Ybdry[i].normal[1] + ny = Ybdry[i].normal[2] + area_contour += (x/2 * nx + y/2*ny) * Ybdry[i].weight + end + area_contour_s1 = 0.0 + for i in 1:length(Ybdry_s1) + x = Ybdry_s1[i].coords[1] + y = Ybdry_s1[i].coords[2] + nx = Ybdry_s1[i].normal[1] + ny = Ybdry_s1[i].normal[2] + area_contour_s1 += (x/2 * nx + y/2*ny) * Ybdry_s1[i].weight + end + + Vint = 0.0 + for i in 1:length(Yvol) + x = Yvol[i].coords[1] + y = Yvol[i].coords[2] + Vint += (3*x^2 + 3*y^2) * Yvol[i].weight + end + Vint_s1 = 0.0 + for i in 1:length(Yvol) + x = Yvol_s1[i].coords[1] + y = Yvol_s1[i].coords[2] + Vint_s1 += (3*x^2 + 3*y^2) * Yvol_s1[i].weight + end + Vcontour = 0.0 + for i in 1:length(Ybdry) + x = Ybdry[i].coords[1] + y = Ybdry[i].coords[2] + nx = Ybdry[i].normal[1] + ny = Ybdry[i].normal[2] + Vcontour += (x^3 * nx + y^3*ny) * Ybdry[i].weight + end + Vcontour_s1 = 0.0 + for i in 1:length(Ybdry_s1) + x = Ybdry_s1[i].coords[1] + y = Ybdry_s1[i].coords[2] + nx = Ybdry_s1[i].normal[1] + ny = Ybdry_s1[i].normal[2] + Vcontour_s1 += (x^3 * nx + y^3*ny) * Ybdry_s1[i].weight + end if isdefined(Main, :Infiltrator) Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) end @@ -320,6 +430,9 @@ function local_vdim_correction( D .= r^2 D = Diagonal(D) wei = transpose(Linv) * D * transpose(R) + + S_s1 = Diagonal((1 / r) .^ (abs.(multiindices))) + wei_s1 = transpose(Linv) * S_s1 * transpose(R_s1) if isdefined(Main, :Infiltrator) Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) end From fd66abe9b18da1d668ebe9cf6c2f9123ace93016 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 7 Mar 2025 10:30:58 -0600 Subject: [PATCH 53/56] tmp --- src/vdim.jl | 166 +++++++++++++++++++++++++++------------------ test/lvdim_test.jl | 3 +- 2 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src/vdim.jl b/src/vdim.jl index e8321f45..254587de 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -230,7 +230,7 @@ function local_vdim_correction( # indices of nodes in element `n` isempty(near_list[n]) && continue c, r, diam = translation_and_scaling(els[n]) - #s=1.0 + s=1.0 if false #if r * op.k < 10^(-3) lowfreq = true @@ -326,13 +326,13 @@ function local_vdim_correction( diam, need_layer_corr; ) - Yvol_s1, Ybdry_s1, need_layer_corr_s1 = _local_vdim_construct_local_quadratures( + Yvol_lowfreq, Ybdry_lowfreq, need_layer_corr_lowfreq = _local_vdim_construct_local_quadratures( N, mesh, neighbors, n, c, - 1.0, + r, diam, bdry_kdtree, bdry_etype2qrule, @@ -340,19 +340,52 @@ function local_vdim_correction( bdry_qrule, vol_qrule, ) - R_s1, b_s1 = _local_vdim_auxiliary_quantities( - op_hat, + R_lowfreq = _lowfreq_vdim_auxiliary_quantities( + op, + op_lowfreq, c, - 1.0, - PFE_p, - PFE_P, + r, + num_basis, + PFE_p_lowfreq, + PFE_P_lowfreq, + multiindices, + multiindices_lowfreq, + monomials_indices, + monomials_indices_lowfreq, target[near_list[n]], green_multiplier, - Yvol_s1, - Ybdry_s1, + Yvol_lowfreq, + Ybdry_lowfreq, diam, - need_layer_corr_s1; + need_layer_corr_lowfreq; ) + #Yvol_s1, Ybdry_s1, need_layer_corr_s1 = _local_vdim_construct_local_quadratures( + # N, + # mesh, + # neighbors, + # n, + # c, + # 1.0, + # diam, + # bdry_kdtree, + # bdry_etype2qrule, + # vol_etype2qrule, + # bdry_qrule, + # vol_qrule, + #) + #R_s1, b_s1 = _local_vdim_auxiliary_quantities( + # op_hat, + # c, + # 1.0, + # PFE_p, + # PFE_P, + # target[near_list[n]], + # green_multiplier, + # Yvol_s1, + # Ybdry_s1, + # diam, + # need_layer_corr_s1; + #) #if isdefined(Main, :Infiltrator) # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) # end @@ -365,63 +398,63 @@ function local_vdim_correction( Linv = pinv(L̃) if !lowfreq S = s^2 * Diagonal((s / r) .^ (abs.(multiindices))) - S_s1 = Diagonal((1 / r) .^ (abs.(multiindices))) + #S_s1 = Diagonal((1 / r) .^ (abs.(multiindices))) wei = transpose(Linv) * S * transpose(R) - wei_s1 = transpose(Linv) * S_s1 * transpose(R_s1) + #wei_s1 = transpose(Linv) * S_s1 * transpose(R_s1) - area = 0.0 - for i in 1:length(Yvol) - area += Yvol[i].weight - end - area_s1 = 0.0 - for i in 1:length(Yvol_s1) - area_s1 += Yvol_s1[i].weight - end - area_contour = 0.0 - for i in 1:length(Ybdry) - x = Ybdry[i].coords[1] - y = Ybdry[i].coords[2] - nx = Ybdry[i].normal[1] - ny = Ybdry[i].normal[2] - area_contour += (x/2 * nx + y/2*ny) * Ybdry[i].weight - end - area_contour_s1 = 0.0 - for i in 1:length(Ybdry_s1) - x = Ybdry_s1[i].coords[1] - y = Ybdry_s1[i].coords[2] - nx = Ybdry_s1[i].normal[1] - ny = Ybdry_s1[i].normal[2] - area_contour_s1 += (x/2 * nx + y/2*ny) * Ybdry_s1[i].weight - end + #area = 0.0 + #for i in 1:length(Yvol) + # area += Yvol[i].weight + #end + #area_s1 = 0.0 + #for i in 1:length(Yvol_s1) + # area_s1 += Yvol_s1[i].weight + #end + #area_contour = 0.0 + #for i in 1:length(Ybdry) + # x = Ybdry[i].coords[1] + # y = Ybdry[i].coords[2] + # nx = Ybdry[i].normal[1] + # ny = Ybdry[i].normal[2] + # area_contour += (x/2 * nx + y/2*ny) * Ybdry[i].weight + #end + #area_contour_s1 = 0.0 + #for i in 1:length(Ybdry_s1) + # x = Ybdry_s1[i].coords[1] + # y = Ybdry_s1[i].coords[2] + # nx = Ybdry_s1[i].normal[1] + # ny = Ybdry_s1[i].normal[2] + # area_contour_s1 += (x/2 * nx + y/2*ny) * Ybdry_s1[i].weight + #end - Vint = 0.0 - for i in 1:length(Yvol) - x = Yvol[i].coords[1] - y = Yvol[i].coords[2] - Vint += (3*x^2 + 3*y^2) * Yvol[i].weight - end - Vint_s1 = 0.0 - for i in 1:length(Yvol) - x = Yvol_s1[i].coords[1] - y = Yvol_s1[i].coords[2] - Vint_s1 += (3*x^2 + 3*y^2) * Yvol_s1[i].weight - end - Vcontour = 0.0 - for i in 1:length(Ybdry) - x = Ybdry[i].coords[1] - y = Ybdry[i].coords[2] - nx = Ybdry[i].normal[1] - ny = Ybdry[i].normal[2] - Vcontour += (x^3 * nx + y^3*ny) * Ybdry[i].weight - end - Vcontour_s1 = 0.0 - for i in 1:length(Ybdry_s1) - x = Ybdry_s1[i].coords[1] - y = Ybdry_s1[i].coords[2] - nx = Ybdry_s1[i].normal[1] - ny = Ybdry_s1[i].normal[2] - Vcontour_s1 += (x^3 * nx + y^3*ny) * Ybdry_s1[i].weight - end + #Vint = 0.0 + #for i in 1:length(Yvol) + # x = Yvol[i].coords[1] + # y = Yvol[i].coords[2] + # Vint += (3*x^2 + 3*y^2) * Yvol[i].weight + #end + #Vint_s1 = 0.0 + #for i in 1:length(Yvol) + # x = Yvol_s1[i].coords[1] + # y = Yvol_s1[i].coords[2] + # Vint_s1 += (3*x^2 + 3*y^2) * Yvol_s1[i].weight + #end + #Vcontour = 0.0 + #for i in 1:length(Ybdry) + # x = Ybdry[i].coords[1] + # y = Ybdry[i].coords[2] + # nx = Ybdry[i].normal[1] + # ny = Ybdry[i].normal[2] + # Vcontour += (x^3 * nx + y^3*ny) * Ybdry[i].weight + #end + #Vcontour_s1 = 0.0 + #for i in 1:length(Ybdry_s1) + # x = Ybdry_s1[i].coords[1] + # y = Ybdry_s1[i].coords[2] + # nx = Ybdry_s1[i].normal[1] + # ny = Ybdry_s1[i].normal[2] + # Vcontour_s1 += (x^3 * nx + y^3*ny) * Ybdry_s1[i].weight + #end if isdefined(Main, :Infiltrator) Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) end @@ -765,6 +798,9 @@ function _lowfreq_vdim_auxiliary_quantities( R[:, n] = θ[:, monomials_indices_lowfreq[β]] R[:, n] += log(scale) * Hmat * b[:, monomials_indices_lowfreq[β]] end + if isdefined(Main, :Infiltrator) + Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + end return R end diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index e443c28a..8fcc0b50 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -8,10 +8,11 @@ using Gmsh using LinearAlgebra using HMatrices using FMMLIB2D -using GLMakie +using CairoMakie using Meshes #meshsize = 0.001/8 +#meshsize = 0.125/8 meshsize = 0.000125 interpolation_order = 4 VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(4) From c114e7c0a5e6df41d7af158342e4fb6b1b8a7ecc Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 11 Jul 2025 15:09:36 -0500 Subject: [PATCH 54/56] lvdim: fix merge from main --- src/mesh.jl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/mesh.jl b/src/mesh.jl index 2cffba98..16e01813 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -670,6 +670,41 @@ function topological_neighbors(msh::AbstractMesh, k = 1) return k_neighbors end +""" + near_interaction_list(X,Y::AbstractMesh; tol) + +For each element `el` of type `E` in `Y`, return the indices of the points in +`X` which are closer than `tol` to the `center` of `el`. + +This function returns a dictionary where e.g. `dict[E][5] --> Vector{Int}` gives +the indices of points in `X` which are closer than `tol` to the center of the +fifth element of type `E`. + +If `tol` is a `Dict`, then `tol[E]` is the tolerance for elements of type `E`. +""" +function near_interaction_list( + X::AbstractVector{<:SVector{N}}, + Y::AbstractMesh{N}; + tol, +) where {N} + @assert isa(tol, Number) || isa(tol, Dict) "tol must be a number or a dictionary mapping element types to numbers" + # for each element type, build the list of targets close to a given element + dict = Dict{DataType,Vector{Vector{Int}}}() + balltree = BallTree(X) + for E in element_types(Y) + els = elements(Y, E) + tol_ = isa(tol, Number) ? tol : tol[E] + idxs = _near_interaction_list(balltree, els, tol_) + dict[E] = idxs + end + return dict +end + +@noinline function _near_interaction_list(balltree, els, tol) + centers = map(center, els) + return inrange(balltree, centers, tol) +end + function viz_elements(args...; kwargs...) end function viz_elements_bords(args...; kwargs...) end From 345c1d7697af3706591aa5c880e235e180650fc9 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 13 Jul 2025 13:54:32 -0500 Subject: [PATCH 55/56] lvdim: fix lvdim after merge --- src/mesh.jl | 2 ++ src/quadrature.jl | 2 +- src/reference_interpolation.jl | 48 +++++++++++++++++++++++++++++++--- src/vdim.jl | 4 +-- test/lvdim_test.jl | 1 - 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/mesh.jl b/src/mesh.jl index d2eea882..da0af0fd 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -707,6 +707,8 @@ end function viz_elements(args...; kwargs...) end +function viz_elements_bords(args...; kwargs...) end + function node2etags(msh) # dictionary mapping a node index to all elements containing it. Note # that the elements are stored as a tuple (type, index) diff --git a/src/quadrature.jl b/src/quadrature.jl index 20b35cc2..8dfb5e9c 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -140,7 +140,7 @@ function Quadrature( QuadratureNode{N,T}[], Dict{DataType,Matrix{Int}}(), ) - ori = ones(length(elementlist)) + ori = ones(Int64, length(elementlist)) # loop element types and generate quadrature for each _build_quadrature!(quad, elementlist, ori, qrule; center, scale) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 3ac33960..893db90e 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -362,13 +362,13 @@ function boundary_idxs(::Type{<:LagrangeLine}) return 1, 2 end -function boundary_idxs(el::LagrangeTriangle) - I = vertices_idxs(el) +function boundary_idxs(T::Type{<:LagrangeTriangle}) + I = vertices_idxs(T) return (I[1], I[2]), (I[2], I[3]), (I[3], I[1]) end -function boundary_idxs(el::LagrangeSquare) - I = vertices_idxs(el) +function boundary_idxs(T::Type{<:LagrangeSquare}) + I = vertices_idxs(T) return (I[1], I[2]), (I[2], I[3]), (I[3], I[4]), (I[4], I[1]) end @@ -564,3 +564,43 @@ function lagrange_basis(::Type{LagrangeElement{D,N,T}}) where {D,N,T} vals = svector(i -> svector(j -> i == j, N), N) return LagrangeElement{D}(vals) end + +function boundarynd(::Type{T}, els, msh) where {T} + bdi = Inti.boundary_idxs(T) + nedges = length(els) * length(bdi) + edgelist = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) + edgelist_unsrt = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) + bords = Vector{MVector{length(bdi[1]),Int64}}(undef, length(bdi)) + for i in 1:length(bdi) + bords[i] = MVector{length(bdi[1]),Int64}(undef) + end + j = 1 + for ii in els + for k in 1:length(bdi) + for jjj in 1:length(bdi[k]) + bords[k][jjj] = Inti.connectivity(msh, T)[bdi[k][jjj], ii] + end + end + for q in bords + edgelist_unsrt[j] = q[:] + edgelist[j] = sort!(q) + j += 1 + end + end + I = sortperm(edgelist) + uniqlist = Int64[] + sizehint!(uniqlist, length(els)) + i = 1 + while i <= length(edgelist) - 1 + if isequal(edgelist[I[i]], edgelist[I[i+1]]) + i += 1 + else + push!(uniqlist, I[i]) + end + i += 1 + end + if !isequal(edgelist[I[end-1]], edgelist[I[end]]) + push!(uniqlist, I[end]) + end + return edgelist_unsrt[uniqlist] +end diff --git a/src/vdim.jl b/src/vdim.jl index 36097359..e9f58830 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -622,7 +622,7 @@ function translation_and_scaling(el::LagrangeTetrahedron) end # function barrier for type stability purposes -_newbord_line(vtxs) = LagrangeLine(SVector{3}(vtxs)) +_newbord_line(vtxs) = LagrangeLine(SVector{2}(vtxs)) # function barrier for type stability purposes _newbord_tri(vtxs) = LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) @@ -652,7 +652,7 @@ function _local_vdim_construct_local_quadratures( loc_bdry = boundarynd(T, els_idxs, mesh) # TODO handle curved boundary of Γ?? if N == 2 - bords = LagrangeElement{ReferenceHyperCube{N - 1},3,SVector{N,Float64}}[] + bords = LagrangeElement{ReferenceHyperCube{N - 1},2,SVector{N,Float64}}[] else bords = LagrangeElement{ReferenceSimplex{N - 1},3,SVector{N,Float64}}[] end diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 8fcc0b50..0b9576d1 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -8,7 +8,6 @@ using Gmsh using LinearAlgebra using HMatrices using FMMLIB2D -using CairoMakie using Meshes #meshsize = 0.001/8 From f9271421f3543d408079f13fa4a097206c86668d Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Fri, 24 Oct 2025 12:19:22 -0500 Subject: [PATCH 56/56] autoformat --- src/mesh.jl | 8 +- src/nystrom.jl | 2 +- src/quadrature.jl | 46 ++++---- src/reference_interpolation.jl | 12 +- src/vdim.jl | 198 ++++++++++++++++----------------- test/lvdim_test.jl | 18 +-- test/lvdim_test_3d.jl | 18 +-- test/poly_test.jl | 2 +- 8 files changed, 152 insertions(+), 152 deletions(-) diff --git a/src/mesh.jl b/src/mesh.jl index 330f518a..b0f2ddd8 100644 --- a/src/mesh.jl +++ b/src/mesh.jl @@ -55,7 +55,7 @@ function elements end Return an iterator for all elements of type `E` on a mesh `msh`. """ -elements(msh::AbstractMesh, E::DataType) = ElementIterator{E,typeof(msh)}(msh) +elements(msh::AbstractMesh, E::DataType) = ElementIterator{E, typeof(msh)}(msh) """ struct Mesh{N,T} <: AbstractMesh{N,T} @@ -629,8 +629,8 @@ the element `i` of type `E` in the mesh, and the value is a set of tuples function topological_neighbors(msh::AbstractMesh, k = 1) # dictionary mapping a node index to all elements containing it. Note # that the elements are stored as a tuple (type, index) - T = Tuple{DataType,Int} - node2els = Dict{Int,Vector{T}}() + T = Tuple{DataType, Int} + node2els = Dict{Int, Vector{T}}() for E in element_types(msh) mat = connectivity(msh, E)::Matrix{Int} # connectivity matrix np, Nel = size(mat) @@ -643,7 +643,7 @@ function topological_neighbors(msh::AbstractMesh, k = 1) end end # now revert the map to get the neighbors - one_neighbors = Dict{T,Set{T}}() + one_neighbors = Dict{T, Set{T}}() for (_, els) in node2els for el in els nei = get!(one_neighbors, el, Set{T}()) diff --git a/src/nystrom.jl b/src/nystrom.jl index 6f78e0d5..69c74bac 100644 --- a/src/nystrom.jl +++ b/src/nystrom.jl @@ -88,7 +88,7 @@ function IntegralOperator(k, X, Y = X) # FIXME This cripples performance for local VDIM #msg = """IntegralOperator of nonbits being created: $T""" #isbitstype(T) || (@warn msg) - return IntegralOperator{T,typeof(k),typeof(X),typeof(Y)}(k, X, Y) + return IntegralOperator{T, typeof(k), typeof(X), typeof(Y)}(k, X, Y) end Base.size(iop::IntegralOperator) = (length(iop.target), length(iop.source)) diff --git a/src/quadrature.jl b/src/quadrature.jl index ade861e8..131e859b 100644 --- a/src/quadrature.jl +++ b/src/quadrature.jl @@ -56,7 +56,7 @@ function Base.show(io::IO, q::QuadratureNode) return print(io, "-- weight: $(q.weight)") end -const Maybe{T} = Union{T,Nothing} +const Maybe{T} = Union{T, Nothing} """ struct Quadrature{N,T} <: AbstractVector{QuadratureNode{N,T}} @@ -64,11 +64,11 @@ const Maybe{T} = Union{T,Nothing} A collection of [`QuadratureNode`](@ref)s used to integrate over an [`AbstractMesh`](@ref). """ -struct Quadrature{N,T} <: AbstractVector{QuadratureNode{N,T}} - mesh::Maybe{AbstractMesh{N,T}} - etype2qrule::Dict{DataType,ReferenceQuadrature} - qnodes::Vector{QuadratureNode{N,T}} - etype2qtags::Dict{DataType,Matrix{Int}} +struct Quadrature{N, T} <: AbstractVector{QuadratureNode{N, T}} + mesh::Maybe{AbstractMesh{N, T}} + etype2qrule::Dict{DataType, ReferenceQuadrature} + qnodes::Vector{QuadratureNode{N, T}} + etype2qtags::Dict{DataType, Matrix{Int}} end # AbstractArray interface @@ -126,19 +126,19 @@ end # Quadrature constructor for list of volume elements for local vdim function Quadrature( - ::Type{T}, - elementlist::AbstractVector{E}, - etype2qrule::Dict{DataType,Q}, - qrule::Q; - center::SVector{N,Float64} = zero(SVector{N,Float64}), - scale::Float64 = 1.0, -) where {N,T,E,Q} + ::Type{T}, + elementlist::AbstractVector{E}, + etype2qrule::Dict{DataType, Q}, + qrule::Q; + center::SVector{N, Float64} = zero(SVector{N, Float64}), + scale::Float64 = 1.0, + ) where {N, T, E, Q} # initialize mesh with empty fields - quad = Quadrature{N,T}( + quad = Quadrature{N, T}( nothing, etype2qrule, - QuadratureNode{N,T}[], - Dict{DataType,Matrix{Int}}(), + QuadratureNode{N, T}[], + Dict{DataType, Matrix{Int}}(), ) ori = ones(Int64, length(elementlist)) # loop element types and generate quadrature for each @@ -170,13 +170,13 @@ function Quadrature(msh::AbstractMesh; qorder) end @noinline function _build_quadrature!( - quad::Quadrature{N,T}, - els::AbstractVector{E}, - orientation::Vector{Int}, - qrule::ReferenceQuadrature; - center::SVector{N,Float64} = zero(SVector{N,Float64}), - scale::Float64 = 1.0, -) where {E,N,T} + quad::Quadrature{N, T}, + els::AbstractVector{E}, + orientation::Vector{Int}, + qrule::ReferenceQuadrature; + center::SVector{N, Float64} = zero(SVector{N, Float64}), + scale::Float64 = 1.0, + ) where {E, N, T} x̂, ŵ = qrule() # nodes and weights on reference element x̂ = map(x̂ -> T.(x̂), qcoords(qrule)) ŵ = map(ŵ -> T.(ŵ), qweights(qrule)) diff --git a/src/reference_interpolation.jl b/src/reference_interpolation.jl index 2052d827..18f9be87 100644 --- a/src/reference_interpolation.jl +++ b/src/reference_interpolation.jl @@ -568,11 +568,11 @@ end function boundarynd(::Type{T}, els, msh) where {T} bdi = Inti.boundary_idxs(T) nedges = length(els) * length(bdi) - edgelist = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) - edgelist_unsrt = Vector{SVector{length(bdi[1]),Int64}}(undef, nedges) - bords = Vector{MVector{length(bdi[1]),Int64}}(undef, length(bdi)) + edgelist = Vector{SVector{length(bdi[1]), Int64}}(undef, nedges) + edgelist_unsrt = Vector{SVector{length(bdi[1]), Int64}}(undef, nedges) + bords = Vector{MVector{length(bdi[1]), Int64}}(undef, length(bdi)) for i in 1:length(bdi) - bords[i] = MVector{length(bdi[1]),Int64}(undef) + bords[i] = MVector{length(bdi[1]), Int64}(undef) end j = 1 for ii in els @@ -592,14 +592,14 @@ function boundarynd(::Type{T}, els, msh) where {T} sizehint!(uniqlist, length(els)) i = 1 while i <= length(edgelist) - 1 - if isequal(edgelist[I[i]], edgelist[I[i+1]]) + if isequal(edgelist[I[i]], edgelist[I[i + 1]]) i += 1 else push!(uniqlist, I[i]) end i += 1 end - if !isequal(edgelist[I[end-1]], edgelist[I[end]]) + if !isequal(edgelist[I[end - 1]], edgelist[I[end]]) push!(uniqlist, I[end]) end return edgelist_unsrt[uniqlist] diff --git a/src/vdim.jl b/src/vdim.jl index a69e703e..bad5ebc0 100644 --- a/src/vdim.jl +++ b/src/vdim.jl @@ -160,20 +160,20 @@ function _lowfreq_operator(op::AbstractDifferentialOperator{N}) where {N} end function local_vdim_correction( - op, - ::Type{Eltype}, - target, - source::Quadrature, - mesh::AbstractMesh, - bdry_nodes; - green_multiplier::Vector{<:Real}, - interpolation_order = nothing, - quadrature_order = nothing, - meshsize = 1.0, - maxdist = Inf, - center = nothing, - shift::Val{SHIFT} = Val(false), -) where {SHIFT,Eltype} + op, + ::Type{Eltype}, + target, + source::Quadrature, + mesh::AbstractMesh, + bdry_nodes; + green_multiplier::Vector{<:Real}, + interpolation_order = nothing, + quadrature_order = nothing, + meshsize = 1.0, + maxdist = Inf, + center = nothing, + shift::Val{SHIFT} = Val(false), + ) where {SHIFT, Eltype} # variables for debugging the condition properties of the method vander_cond = vander_norm = rhs_norm = res_norm = shift_norm = -Inf # figure out if we are dealing with a scalar or vector PDE @@ -230,7 +230,7 @@ function local_vdim_correction( # indices of nodes in element `n` isempty(near_list[n]) && continue c, r, diam = translation_and_scaling(els[n]) - s=1.0 + s = 1.0 if false #if r * op.k < 10^(-3) lowfreq = true @@ -265,7 +265,7 @@ function local_vdim_correction( Yvol, Ybdry, diam, - need_layer_corr; + need_layer_corr ) Yvol_s1, Ybdry_s1, need_layer_corr_s1 = _local_vdim_construct_local_quadratures( N, @@ -292,7 +292,7 @@ function local_vdim_correction( Yvol_s1, Ybdry_s1, diam, - need_layer_corr_s1; + need_layer_corr_s1 ) else lowfreq = false @@ -310,9 +310,9 @@ function local_vdim_correction( bdry_qrule, vol_qrule, ) - #if isdefined(Main, :Infiltrator) - # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - # end + #if isdefined(Main, :Infiltrator) + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + # end R, b = _local_vdim_auxiliary_quantities( op_hat, c, @@ -324,7 +324,7 @@ function local_vdim_correction( Yvol, Ybdry, diam, - need_layer_corr; + need_layer_corr ) Yvol_lowfreq, Ybdry_lowfreq, need_layer_corr_lowfreq = _local_vdim_construct_local_quadratures( N, @@ -357,7 +357,7 @@ function local_vdim_correction( Yvol_lowfreq, Ybdry_lowfreq, diam, - need_layer_corr_lowfreq; + need_layer_corr_lowfreq ) #Yvol_s1, Ybdry_s1, need_layer_corr_s1 = _local_vdim_construct_local_quadratures( # N, @@ -386,9 +386,9 @@ function local_vdim_correction( # diam, # need_layer_corr_s1; #) - #if isdefined(Main, :Infiltrator) - # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - # end + #if isdefined(Main, :Infiltrator) + # Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) + # end end jglob = @view qtags[:, n] # compute translation and scaling @@ -457,7 +457,7 @@ function local_vdim_correction( #end if isdefined(Main, :Infiltrator) Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end + end else D = Vector{Float64}(undef, num_basis) D .= r^2 @@ -628,19 +628,19 @@ _newbord_line(vtxs) = LagrangeLine(SVector{2}(vtxs)) _newbord_tri(vtxs) = LagrangeElement{ReferenceSimplex{2}}(SVector{3}(vtxs)) function _local_vdim_construct_local_quadratures( - N, - mesh, - neighbors, - el, - center, - scale, - diam, - bdry_kdtree, - bdry_etype2qrule, - vol_etype2qrule, - bdry_qrule, - vol_qrule; -) + N, + mesh, + neighbors, + el, + center, + scale, + diam, + bdry_kdtree, + bdry_etype2qrule, + vol_etype2qrule, + bdry_qrule, + vol_qrule + ) # construct the local region Etype = first(element_types(mesh)) el_neighs = neighbors[(Etype, el)] @@ -652,9 +652,9 @@ function _local_vdim_construct_local_quadratures( loc_bdry = boundarynd(T, els_idxs, mesh) # TODO handle curved boundary of Γ?? if N == 2 - bords = LagrangeElement{ReferenceHyperCube{N - 1},2,SVector{N,Float64}}[] + bords = LagrangeElement{ReferenceHyperCube{N - 1}, 2, SVector{N, Float64}}[] else - bords = LagrangeElement{ReferenceSimplex{N - 1},3,SVector{N,Float64}}[] + bords = LagrangeElement{ReferenceSimplex{N - 1}, 3, SVector{N, Float64}}[] end for idxs in loc_bdry @@ -681,18 +681,18 @@ function _local_vdim_construct_local_quadratures( end function _local_vdim_auxiliary_quantities( - op::AbstractDifferentialOperator{N}, - center, - scale, - PFE_p, - PFE_P, - X, - μ, - Yvol, - Ybdry, - diam, - need_layer_corr; -) where {N} + op::AbstractDifferentialOperator{N}, + center, + scale, + PFE_p, + PFE_P, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr + ) where {N} # TODO handle derivative case G = SingleLayerKernel(op) dG = DoubleLayerKernel(op) @@ -747,7 +747,7 @@ function _local_vdim_auxiliary_quantities( for j in 1:num_basis γ₁B[i, j] = 0 for k in 1:N - γ₁B[i, j] += grad[j, k, i] * Ybdry[i].normal[k]#nrml_bdry_vec[i][k] + γ₁B[i, j] += grad[j, k, i] * Ybdry[i].normal[k] #nrml_bdry_vec[i][k] end end end @@ -766,24 +766,24 @@ function _local_vdim_auxiliary_quantities( end function _lowfreq_vdim_auxiliary_quantities( - op::Laplace{2}, - op_lowfreq::Laplace{2}, - center, - scale, - num_basis, - PFE_p_lowfreq, - PFE_P_lowfreq, - multiindices, - multiindices_lowfreq, - monomials_indices, - monomials_indices_lowfreq, - X, - μ, - Yvol, - Ybdry, - diam, - need_layer_corr; -) + op::Laplace{2}, + op_lowfreq::Laplace{2}, + center, + scale, + num_basis, + PFE_p_lowfreq, + PFE_P_lowfreq, + multiindices, + multiindices_lowfreq, + monomials_indices, + monomials_indices_lowfreq, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr + ) θ, b = _local_vdim_auxiliary_quantities( op_lowfreq, center, @@ -795,7 +795,7 @@ function _lowfreq_vdim_auxiliary_quantities( Yvol, Ybdry, diam, - need_layer_corr; + need_layer_corr ) Xshift = [(q.coords - center) / scale for q in X] num_targets = length(Xshift) @@ -815,29 +815,29 @@ function _lowfreq_vdim_auxiliary_quantities( end if isdefined(Main, :Infiltrator) Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__) - end + end return R end function _lowfreq_vdim_auxiliary_quantities( - op::Helmholtz{2}, - op_lowfreq::Laplace{2}, - center, - scale, - num_basis, - PFE_p_lowfreq, - PFE_P_lowfreq, - multiindices, - multiindices_lowfreq, - monomials_indices, - monomials_indices_lowfreq, - X, - μ, - Yvol, - Ybdry, - diam, - need_layer_corr; -) + op::Helmholtz{2}, + op_lowfreq::Laplace{2}, + center, + scale, + num_basis, + PFE_p_lowfreq, + PFE_P_lowfreq, + multiindices, + multiindices_lowfreq, + monomials_indices, + monomials_indices_lowfreq, + X, + μ, + Yvol, + Ybdry, + diam, + need_layer_corr + ) Xshift = [(q.coords - center) / scale for q in X] # Laplace θ, b = _local_vdim_auxiliary_quantities( @@ -861,7 +861,7 @@ function _lowfreq_vdim_auxiliary_quantities( Vop = IntegralOperator(G, Xshift, Yvol) R = zeros(eltype(Vop), num_targets, num_basis) kr2 = (op.k * scale)^2 - γ = 0.57721566490153286060 + γ = 0.5772156649015328606 Hmat = Matrix{ComplexF64}(undef, length(X), length(Yvol)) #Hmat .= 0 @@ -870,19 +870,19 @@ function _lowfreq_vdim_auxiliary_quantities( for j in 1:length(Yvol) z2 = kr2 * ( - (Xshift[i][1] - Yvol[j].coords[1])^2 + + (Xshift[i][1] - Yvol[j].coords[1])^2 + (Xshift[i][2] - Yvol[j].coords[2])^2 - ) + ) bessj0 = 0 for k in 0:3 bessj0 += (-1)^k * (1 / 4 * z2)^k / (factorial(k)^2) end Hmat[i, j] = ( - (1 + 2 * im / pi * (γ + 1 / 2 * log(kr2 / 4))) * bessj0 + + (1 + 2 * im / pi * (γ + 1 / 2 * log(kr2 / 4))) * bessj0 + 2 * im / pi * (1 / 4 * z2 - 3 / 2 * (1 / 4 * z2)^2 / 4 + 11 / 3456 * z2^3) - ) * + ) * Yvol[j].weight * scale^2 end @@ -975,8 +975,8 @@ function polynomial_solutions_local_vdim(op::AbstractDifferentialOperator, order # create empty arrays to store the monomials, solutions, and traces. For the # neumann trace, we try to infer the concrete return type instead of simply # having a vector of `Function`. - monomials = Vector{ElementaryPDESolutions.Polynomial{N,Float64}}() - poly_solutions = Vector{ElementaryPDESolutions.Polynomial{N,Float64}}() + monomials = Vector{ElementaryPDESolutions.Polynomial{N, Float64}}() + poly_solutions = Vector{ElementaryPDESolutions.Polynomial{N, Float64}}() multiindices = Vector{MultiIndex{N}}() # iterate over N-tuples going from 0 to order for I in Iterators.product(ntuple(i -> 0:order, N)...) diff --git a/test/lvdim_test.jl b/test/lvdim_test.jl index 0b9576d1..c3c0a339 100644 --- a/test/lvdim_test.jl +++ b/test/lvdim_test.jl @@ -18,7 +18,7 @@ VR_qorder = Inti.Triangle_VR_interpolation_order_to_quadrature_order(4) bdry_qorder = 2 * VR_qorder function gmsh_disk(; name, meshsize, order = 1, center = (0, 0), paxis = (2, 1)) - try + return try gmsh.initialize() gmsh.option.setNumber("General.Terminal", 0) gmsh.model.add("circle-mesh") @@ -60,13 +60,13 @@ end @info "Quadrature generation time: $tquad" k0 = 1 -k = 0 -θ = (cos(π / 3), sin(π / 3)) +k = 0 +θ = (cos(π / 3), sin(π / 3)) #u = (x) -> exp(im * k0 * dot(x, θ)) #du = (x,n) -> im * k0 * dot(θ, n) * exp(im * k0 * dot(x, θ)) -u = (x) -> cos(k0 * dot(x, θ)) +u = (x) -> cos(k0 * dot(x, θ)) du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) -f = (x) -> (k^2 - k0^2) * u(x) +f = (x) -> (k^2 - k0^2) * u(x) #s = 4 #u = (x) -> 1 / (k^2 - k0^2) * exp(im * k0 * dot(x, θ)) + 1 / (k^2 - 4 * s) * exp(-s * norm(x)^2) @@ -86,7 +86,7 @@ tbnd = @elapsed begin op, target = Ωₕ_quad, source = Γₕ_quad, - compression = (method = :fmm, tol = 1e-14), + compression = (method = :fmm, tol = 1.0e-14), correction = (method = :dim, maxdist = 5 * meshsize, target_location = :inside), ) end @@ -98,7 +98,7 @@ V_d2d = Inti.volume_potential(; op, target = Ωₕ_quad, source = Ωₕ_quad, - compression = (method = :fmm, tol = 1e-14), + compression = (method = :fmm, tol = 1.0e-14), correction = ( method = :ldim, mesh = Ωₕ, @@ -112,9 +112,9 @@ V_d2d = Inti.volume_potential(; #end #@info "Volume potential time: $tvol" -vref = -u_d - D_b2d * u_b + S_b2d * du_b +vref = -u_d - D_b2d * u_b + S_b2d * du_b vapprox = V_d2d * f_d -er = vref - vapprox +er = vref - vapprox ndofs = length(er) diff --git a/test/lvdim_test_3d.jl b/test/lvdim_test_3d.jl index 30e89d61..6ffdbd19 100644 --- a/test/lvdim_test_3d.jl +++ b/test/lvdim_test_3d.jl @@ -17,7 +17,7 @@ VR_qorder = Inti.Tetrahedron_VR_interpolation_order_to_quadrature_order(interpol bdry_qorder = 2 * VR_qorder function gmsh_sphere(; order = 1, name, meshsize) - try + return try gmsh.initialize() gmsh.option.setNumber("General.Terminal", 0) gmsh.model.add("sphere-mesh") @@ -57,13 +57,13 @@ end @info "Quadrature generation time: $tquad" k0 = π -k = 0 -θ = (sin(π / 3) * cos(π / 3), sin(π / 3) * sin(π / 3), cos(π / 3)) +k = 0 +θ = (sin(π / 3) * cos(π / 3), sin(π / 3) * sin(π / 3), cos(π / 3)) #u = (x) -> exp(im * k0 * dot(x, θ)) #du = (x,n) -> im * k0 * dot(θ, n) * exp(im * k0 * dot(x, θ)) -u = (x) -> cos(k0 * dot(x, θ)) +u = (x) -> cos(k0 * dot(x, θ)) du = (x, n) -> -k0 * dot(θ, n) * sin(k0 * dot(x, θ)) -f = (x) -> (k^2 - k0^2) * u(x) +f = (x) -> (k^2 - k0^2) * u(x) u_d = map(q -> u(q.coords), Ωₕ_quad) u_b = map(q -> u(q.coords), Γₕ_quad) @@ -78,7 +78,7 @@ tbnd = @elapsed begin pde, target = Ωₕ_quad, source = Γₕ_quad, - compression = (method = :fmm, tol = 1e-8), + compression = (method = :fmm, tol = 1.0e-8), correction = (method = :dim, maxdist = 5 * meshsize, target_location = :inside), ) end @@ -90,7 +90,7 @@ tvol = @elapsed begin pde, target = Ωₕ_quad, source = Ωₕ_quad, - compression = (method = :fmm, tol = 1e-8), + compression = (method = :fmm, tol = 1.0e-8), correction = ( method = :ldim, interpolation_order, @@ -103,9 +103,9 @@ tvol = @elapsed begin end @info "Volume potential time: $tvol" -vref = -u_d - D_b2d * u_b + S_b2d * du_b +vref = -u_d - D_b2d * u_b + S_b2d * du_b vapprox = V_d2d * f_d -er = vref - vapprox +er = vref - vapprox ndofs = length(er) diff --git a/test/poly_test.jl b/test/poly_test.jl index d4049ba8..671e1faa 100644 --- a/test/poly_test.jl +++ b/test/poly_test.jl @@ -42,7 +42,7 @@ function poly_test(npts) cfg = JacobianConfig(PolSystem, pts[1]) res1 = Matrix{Float64}(undef, length(PolArray), length(pts)) res2 = Matrix{Float64}(undef, length(PolArray), length(pts)) - res3 = Vector{MVector{length(PolArray),Float64}}(undef, npts) + res3 = Vector{MVector{length(PolArray), Float64}}(undef, npts) cfg = JacobianConfig(PolSystem, pts[1]) u = Vector{Float64}(undef, length(PolArray))